diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml
index bc2626d3d6..9e11ab6663 100644
--- a/.github/workflows/diffcalc.yml
+++ b/.github/workflows/diffcalc.yml
@@ -53,6 +53,7 @@ jobs:
diffcalc:
name: Run
runs-on: self-hosted
+ timeout-minutes: 1440
if: needs.metadata.outputs.continue == 'yes'
needs: metadata
strategy:
diff --git a/osu.Android.props b/osu.Android.props
index 8fad10d247..5a0e7479fa 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,11 +51,11 @@
-
-
+
+
-
+
diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs
index dcb88efeb6..e2b40e9dc6 100644
--- a/osu.Desktop/DiscordRichPresence.cs
+++ b/osu.Desktop/DiscordRichPresence.cs
@@ -140,10 +140,10 @@ namespace osu.Desktop
switch (activity)
{
case UserActivity.InGame game:
- return game.Beatmap.ToString();
+ return game.BeatmapInfo.ToString();
case UserActivity.Editing edit:
- return edit.Beatmap.ToString();
+ return edit.BeatmapInfo.ToString();
case UserActivity.InLobby lobby:
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
index 540f02580f..f291bfed13 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -290,7 +290,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{
public IEnumerable CaughtObjects => this.ChildrenOfType();
- public TestCatcher(DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty)
+ public TestCatcher(DroppedObjectContainer droppedObjectTarget, IBeatmapDifficultyInfo difficulty)
: base(droppedObjectTarget, difficulty)
{
}
@@ -298,7 +298,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public class TestKiaiFruit : Fruit
{
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
index 6abfbdbe21..7cae9b18b9 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Tests
private ScheduledDelegate addManyFruit;
- private BeatmapDifficulty beatmapDifficulty;
+ private IBeatmapDifficultyInfo beatmapDifficulty;
public TestSceneCatcherArea()
{
@@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Catch.Tests
private class TestCatcherArea : CatcherArea
{
- public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
+ public TestCatcherArea(IBeatmapDifficultyInfo beatmapDifficulty)
{
var droppedObjectContainer = new DroppedObjectContainer();
Add(droppedObjectContainer);
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
index 82d76252d2..5b1f613f8d 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
return new CatchDifficultyAttributes { Mods = mods, Skills = skills };
// this is the same as osu!, so there's potential to share the implementation... maybe
- double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
+ double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
return new CatchDifficultyAttributes
{
diff --git a/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs
index 8c9f292aa9..046ba0ebce 100644
--- a/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfield.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Edit
public class CatchEditorPlayfield : CatchPlayfield
{
// TODO fixme: the size of the catcher is not changed when circle size is changed in setup screen.
- public CatchEditorPlayfield(BeatmapDifficulty difficulty)
+ public CatchEditorPlayfield(IBeatmapDifficultyInfo difficulty)
: base(difficulty)
{
}
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index d43e6f1c8b..ee10cf9711 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -128,11 +128,11 @@ namespace osu.Game.Rulesets.Catch.Objects
///
public int RandomSeed => (int)StartTime;
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
- TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
+ TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index a8ad34fcbe..0d6925a83d 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Objects
///
public double SpanDuration => Duration / this.SpanCount();
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index 1e20643a08..df32d917ce 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Catch.UI
internal CatcherArea CatcherArea { get; private set; }
- private readonly BeatmapDifficulty difficulty;
+ private readonly IBeatmapDifficultyInfo difficulty;
- public CatchPlayfield(BeatmapDifficulty difficulty)
+ public CatchPlayfield(IBeatmapDifficultyInfo difficulty)
{
this.difficulty = difficulty;
}
diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs
index 5cd85aac56..3745099010 100644
--- a/osu.Game.Rulesets.Catch/UI/Catcher.cs
+++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs
@@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly DrawablePool caughtBananaPool;
private readonly DrawablePool caughtDropletPool;
- public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null)
+ public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, IBeatmapDifficultyInfo difficulty = null)
{
this.droppedObjectTarget = droppedObjectTarget;
@@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Catch.UI
///
/// Calculates the scale of the catcher based off the provided beatmap difficulty.
///
- private static Vector2 calculateScale(BeatmapDifficulty difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
+ private static Vector2 calculateScale(IBeatmapDifficultyInfo difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
///
/// Calculates the width of the area used for attempting catches in gameplay.
@@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Catch.UI
/// Calculates the width of the area used for attempting catches in gameplay.
///
/// The beatmap difficulty.
- public static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(calculateScale(difficulty));
+ public static float CalculateCatchWidth(IBeatmapDifficultyInfo difficulty) => CalculateCatchWidth(calculateScale(difficulty));
///
/// Determine if this catcher can catch a in the current position.
diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
index 8b6a074426..ba6e9224c9 100644
--- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI
: base(ruleset, beatmap, mods)
{
Direction.Value = ScrollingDirection.Down;
- TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
+ TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 26393c8edb..01e44ef9e4 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -71,9 +71,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
originalTargetColumns = TargetColumns;
}
- public static int GetColumnCountForNonConvert(BeatmapInfo beatmap)
+ public static int GetColumnCountForNonConvert(BeatmapInfo beatmapInfo)
{
- var roundedCircleSize = Math.Round(beatmap.BaseDifficulty.CircleSize);
+ var roundedCircleSize = Math.Round(beatmapInfo.BaseDifficulty.CircleSize);
return (int)Math.Max(1, roundedCircleSize);
}
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken)
{
- BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
+ IBeatmapDifficultyInfo difficulty = original.BeatmapInfo.BaseDifficulty;
int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate);
Random = new FastRandom(seed);
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
index e643b82271..d65e78bb49 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
@@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (drainTime == 0)
drainTime = 10000;
- BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
+ IBeatmapDifficultyInfo difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
conversionDifficulty = ((difficulty.DrainRate + Math.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
index a7a6677b68..fc29eadedc 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
@@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
Mods = mods,
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
GreatHitWindow = (int)Math.Ceiling(getHitWindow300(mods) / clockRate),
- ScoreMultiplier = getScoreMultiplier(beatmap, mods),
+ ScoreMultiplier = getScoreMultiplier(mods),
MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1),
Skills = skills
};
@@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
{
- new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
+ new Strain(mods, ((ManiaBeatmap)Beatmap).TotalColumns)
};
protected override Mod[] DifficultyAdjustmentMods
@@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
}
}
- private double getScoreMultiplier(IBeatmap beatmap, Mod[] mods)
+ private double getScoreMultiplier(Mod[] mods)
{
double scoreMultiplier = 1;
@@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
}
}
- var maniaBeatmap = (ManiaBeatmap)beatmap;
+ var maniaBeatmap = (ManiaBeatmap)Beatmap;
int diff = maniaBeatmap.TotalColumns - maniaBeatmap.OriginalTotalColumns;
if (diff > 0)
diff --git a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs
index d9a278ef29..0290230490 100644
--- a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs
+++ b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs
@@ -13,9 +13,9 @@ namespace osu.Game.Rulesets.Mania
{
private FilterCriteria.OptionalRange keys;
- public bool Matches(BeatmapInfo beatmap)
+ public bool Matches(BeatmapInfo beatmapInfo)
{
- return !keys.HasFilter || (beatmap.RulesetID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmap)));
+ return !keys.HasFilter || (beatmapInfo.RulesetID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmapInfo)));
}
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value)
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
index 43e876b7aa..c1937af7e4 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
@@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Objects
///
private double tickSpacing = 50;
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs
index 6a3f168ee1..787807a8ea 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckTooShortSpinnersTest.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
public class CheckTooShortSpinnersTest
{
private CheckTooShortSpinners check;
- private BeatmapDifficulty difficulty;
+ private IBeatmapDifficultyInfo difficulty;
[SetUp]
public void Setup()
@@ -81,12 +81,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
assertTooShort(new List { spinnerHighOd }, difficultyHighOd);
}
- private void assertOk(List hitObjects, BeatmapDifficulty beatmapDifficulty)
+ private void assertOk(List hitObjects, IBeatmapDifficultyInfo beatmapDifficulty)
{
Assert.That(check.Run(getContext(hitObjects, beatmapDifficulty)), Is.Empty);
}
- private void assertVeryShort(List hitObjects, BeatmapDifficulty beatmapDifficulty)
+ private void assertVeryShort(List hitObjects, IBeatmapDifficultyInfo beatmapDifficulty)
{
var issues = check.Run(getContext(hitObjects, beatmapDifficulty)).ToList();
@@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Assert.That(issues.First().Template is CheckTooShortSpinners.IssueTemplateVeryShort);
}
- private void assertTooShort(List hitObjects, BeatmapDifficulty beatmapDifficulty)
+ private void assertTooShort(List hitObjects, IBeatmapDifficultyInfo beatmapDifficulty)
{
var issues = check.Run(getContext(hitObjects, beatmapDifficulty)).ToList();
@@ -102,12 +102,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Assert.That(issues.First().Template is CheckTooShortSpinners.IssueTemplateTooShort);
}
- private BeatmapVerifierContext getContext(List hitObjects, BeatmapDifficulty beatmapDifficulty)
+ private BeatmapVerifierContext getContext(List hitObjects, IBeatmapDifficultyInfo beatmapDifficulty)
{
var beatmap = new Beatmap
{
HitObjects = hitObjects,
- BeatmapInfo = new BeatmapInfo { BaseDifficulty = beatmapDifficulty }
+ BeatmapInfo = new BeatmapInfo { BaseDifficulty = new BeatmapDifficulty(beatmapDifficulty) }
};
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs
index 1ac3ad9194..ed9da36b05 100644
--- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs
@@ -4,13 +4,11 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
-using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests.Mods
@@ -122,7 +120,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
private bool checkSomeHit() => Player.ScoreProcessor.JudgedHits >= 4;
private bool objectWithIncreasedVisibilityHasIndex(int index)
- => Player.Mods.Value.OfType().Single().FirstObject == Player.ChildrenOfType().Single().HitObjects[index];
+ => Player.GameplayState.Mods.OfType().Single().FirstObject == Player.GameplayState.Beatmap.HitObjects[index];
private class TestOsuModHidden : OsuModHidden
{
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png
index a9b2d95d88..8e50cd0335 100755
Binary files a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png and b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png differ
diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/skin.ini b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/skin.ini
new file mode 100644
index 0000000000..49ac2cf80d
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/Resources/special-skin/skin.ini
@@ -0,0 +1,3 @@
+[General]
+Version: latest
+HitCircleOverlayAboveNumber: 0
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneAccuracyHeatmap.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneAccuracyHeatmap.cs
index 10d9d7ffde..79150a1941 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneAccuracyHeatmap.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneAccuracyHeatmap.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
Position = new Vector2(100, 300),
},
- accuracyHeatmap = new TestAccuracyHeatmap(new ScoreInfo { Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo })
+ accuracyHeatmap = new TestAccuracyHeatmap(new ScoreInfo { BeatmapInfo = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo })
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
index f9dc9abd75..41d9bf7132 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
@@ -17,6 +17,7 @@ using osu.Framework.Testing.Input;
using osu.Framework.Utils;
using osu.Game.Audio;
using osu.Game.Configuration;
+using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Screens.Play;
@@ -29,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public class TestSceneGameplayCursor : OsuSkinnableTestScene
{
[Cached]
- private GameplayBeatmap gameplayBeatmap;
+ private GameplayState gameplayState;
private OsuCursorContainer lastContainer;
@@ -40,7 +41,8 @@ namespace osu.Game.Rulesets.Osu.Tests
public TestSceneGameplayCursor()
{
- gameplayBeatmap = new GameplayBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
+ var ruleset = new OsuRuleset();
+ gameplayState = new GameplayState(CreateBeatmap(ruleset.RulesetInfo), ruleset, Array.Empty());
AddStep("change background colour", () =>
{
@@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddSliderStep("circle size", 0f, 10f, 0f, val =>
{
config.SetValue(OsuSetting.AutoCursorSize, true);
- gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = val;
+ gameplayState.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize = val;
Scheduler.AddOnce(() => loadContent(false));
});
@@ -73,7 +75,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public void TestSizing(int circleSize, float userScale)
{
AddStep($"set user scale to {userScale}", () => config.SetValue(OsuSetting.GameplayCursorSize, userScale));
- AddStep($"adjust cs to {circleSize}", () => gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize);
+ AddStep($"adjust cs to {circleSize}", () => gameplayState.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize);
AddStep("turn on autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, true));
AddStep("load content", () => loadContent());
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs
index 77a68b714b..cfce80a2b2 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs
@@ -452,7 +452,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private class TestSpinner : Spinner
{
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
SpinsRequired = 1;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs
index 177a4f50a1..1b85e0efde 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs
@@ -412,7 +412,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private class TestSpinner : Spinner
{
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
SpinsRequired = 1;
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 4c8d0b2ce6..a8f10f44dc 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0;
- double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
+ double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
int maxCombo = beatmap.HitObjects.Count;
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs
index 210d5e0403..b0c655b106 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs
@@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Mods
#region Reduce AR (IApplicableToDifficulty)
- public void ReadFromDifficulty(BeatmapDifficulty difficulty)
+ public void ReadFromDifficulty(IBeatmapDifficultyInfo difficulty)
{
}
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 36629fa41e..7c45b2bc07 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -122,11 +122,11 @@ namespace osu.Game.Rulesets.Osu.Objects
});
}
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
- TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN);
+ TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN);
// Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above.
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index c4420b1e87..1d2666f46b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects
Path.Version.ValueChanged += _ => updateNestedPositions();
}
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs
index a6aed2c00e..f893559548 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double SpanDuration => slider.SpanDuration;
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
index 725dbe81fb..e7e64954e9 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public int SpanIndex { get; set; }
public double SpanStartTime { get; set; }
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
index 194aa640f9..f85dc0d391 100644
--- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects
///
public int MaximumBonusSpins { get; protected set; } = 1;
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
@@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects
double secondsDuration = Duration / 1000;
- double minimumRotationsPerSecond = stable_matching_fudge * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5);
+ double minimumRotationsPerSecond = stable_matching_fudge * IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5);
SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond);
MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration);
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs
index c2db5f3f82..611ddd08eb 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
private OsuPlayfield playfield { get; set; }
[Resolved(canBeNull: true)]
- private GameplayBeatmap gameplayBeatmap { get; set; }
+ private GameplayState gameplayState { get; set; }
[BackgroundDependencyLoader]
private void load(ISkinSource skin, OsuColour colours)
@@ -75,12 +75,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
protected override void Update()
{
- if (playfield == null || gameplayBeatmap == null) return;
+ if (playfield == null || gameplayState == null) return;
DrawableHitObject kiaiHitObject = null;
// Check whether currently in a kiai section first. This is only done as an optimisation to avoid enumerating AliveObjects when not necessary.
- if (gameplayBeatmap.ControlPointInfo.EffectPointAt(Time.Current).KiaiMode)
+ if (gameplayState.Beatmap.ControlPointInfo.EffectPointAt(Time.Current).KiaiMode)
kiaiHitObject = playfield.HitObjectContainer.AliveObjects.FirstOrDefault(isTracking);
kiaiSpewer.Active.Value = kiaiHitObject != null;
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs
index 3afd814174..d1c9b1bf92 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs
@@ -35,8 +35,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
private Drawable hitCircleSprite;
- protected Drawable HitCircleOverlay { get; private set; }
+ protected Container OverlayLayer { get; private set; }
+ private Drawable hitCircleOverlay;
private SkinnableSpriteText hitCircleText;
private readonly Bindable accentColour = new Bindable();
@@ -78,17 +79,22 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
- HitCircleOverlay = new KiaiFlashingSprite
+ OverlayLayer = new Container
{
- Texture = overlayTexture,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- },
+ Child = hitCircleOverlay = new KiaiFlashingSprite
+ {
+ Texture = overlayTexture,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ }
};
if (hasNumber)
{
- AddInternal(hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText
+ OverlayLayer.Add(hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText
{
Font = OsuFont.Numeric.With(size: 40),
UseFullGlyphHeight = false,
@@ -102,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
bool overlayAboveNumber = skin.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
if (overlayAboveNumber)
- ChangeInternalChildDepth(HitCircleOverlay, float.MinValue);
+ OverlayLayer.ChangeChildDepth(hitCircleOverlay, float.MinValue);
accentColour.BindTo(drawableObject.AccentColour);
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
@@ -147,8 +153,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
hitCircleSprite.FadeOut(legacy_fade_duration, Easing.Out);
hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
- HitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out);
- HitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
+ hitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out);
+ hitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
if (hasNumber)
{
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs
index 13ba42ba50..7de2b8c7fa 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
[Resolved(canBeNull: true)]
private DrawableHitObject drawableHitObject { get; set; }
- private Drawable proxiedHitCircleOverlay;
+ private Drawable proxiedOverlayLayer;
public LegacySliderHeadHitCircle()
: base("sliderstartcircle")
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
protected override void LoadComplete()
{
base.LoadComplete();
- proxiedHitCircleOverlay = HitCircleOverlay.CreateProxy();
+ proxiedOverlayLayer = OverlayLayer.CreateProxy();
if (drawableHitObject != null)
{
@@ -35,11 +35,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
private void onHitObjectApplied(DrawableHitObject drawableObject)
{
- Debug.Assert(proxiedHitCircleOverlay.Parent == null);
+ Debug.Assert(proxiedOverlayLayer.Parent == null);
// see logic in LegacyReverseArrow.
(drawableObject as DrawableSliderHead)?.DrawableSlider
- .OverlayElementContainer.Add(proxiedHitCircleOverlay.With(d => d.Depth = float.MinValue));
+ .OverlayElementContainer.Add(proxiedOverlayLayer.With(d => d.Depth = float.MinValue));
}
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
index 83bcc88e5f..cfe83d0106 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs
@@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
}
[Resolved(canBeNull: true)]
- private GameplayBeatmap beatmap { get; set; }
+ private GameplayState state { get; set; }
[Resolved]
private OsuConfigManager config { get; set; }
@@ -96,10 +96,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
float scale = userCursorScale.Value;
- if (autoCursorScale.Value && beatmap != null)
+ if (autoCursorScale.Value && state != null)
{
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
- scale *= GetScaleForCircleSize(beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
+ scale *= GetScaleForCircleSize(state.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
}
cursorScale.Value = scale;
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index 9b73e644c5..3b5b972c01 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
case IHasDuration endTimeData:
{
- double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
+ double hitMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
yield return new Swell
{
@@ -193,9 +193,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
private class TaikoMutliplierAppliedDifficulty : BeatmapDifficulty
{
- public TaikoMutliplierAppliedDifficulty(BeatmapDifficulty difficulty)
+ public TaikoMutliplierAppliedDifficulty(IBeatmapDifficultyInfo difficulty)
{
- difficulty.CopyTo(this);
+ CopyFrom(difficulty);
+
SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
index b0634295d0..0318e32991 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY;
- protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs
index f7a1d130eb..94cd411d7b 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs
@@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring
{
base.ApplyBeatmap(beatmap);
- hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
- hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
+ hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType().Count()) * IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
+ hpMissMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
}
protected override double GetHealthIncreaseFor(JudgementResult result)
diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs
index 6fc59ea0e8..fa49242675 100644
--- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs
+++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs
@@ -25,10 +25,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
}
[BackgroundDependencyLoader(true)]
- private void load(GameplayBeatmap gameplayBeatmap)
+ private void load(GameplayState gameplayState)
{
- if (gameplayBeatmap != null)
- ((IBindable)LastResult).BindTo(gameplayBeatmap.LastJudgementResult);
+ if (gameplayState != null)
+ ((IBindable)LastResult).BindTo(gameplayState.LastJudgementResult);
}
private bool passing;
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs
index 6a16f311bf..e1063e1071 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.UI
}
[BackgroundDependencyLoader(true)]
- private void load(TextureStore textures, GameplayBeatmap gameplayBeatmap)
+ private void load(TextureStore textures, GameplayState gameplayState)
{
InternalChildren = new[]
{
@@ -49,8 +49,8 @@ namespace osu.Game.Rulesets.Taiko.UI
animations[TaikoMascotAnimationState.Fail] = new TaikoMascotAnimation(TaikoMascotAnimationState.Fail),
};
- if (gameplayBeatmap != null)
- ((IBindable)LastResult).BindTo(gameplayBeatmap.LastJudgementResult);
+ if (gameplayState != null)
+ ((IBindable)LastResult).BindTo(gameplayState.LastJudgementResult);
}
protected override void LoadComplete()
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
index bcde899789..560e2ef894 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
@@ -149,5 +149,32 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[5]).LoopType);
}
}
+
+ [Test]
+ public void TestDecodeLoopCount()
+ {
+ // all loop sequences in loop-count.osb have a total duration of 2000ms (fade in 0->1000ms, fade out 1000->2000ms).
+ const double loop_duration = 2000;
+
+ var decoder = new LegacyStoryboardDecoder();
+
+ using (var resStream = TestResources.OpenResource("loop-count.osb"))
+ using (var stream = new LineBufferedReader(resStream))
+ {
+ var storyboard = decoder.Decode(stream);
+
+ StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
+
+ // stable ensures that any loop command executes at least once, even if the loop count specified in the .osb is zero or negative.
+ StoryboardSprite zeroTimes = background.Elements.OfType().Single(s => s.Path == "zero-times.png");
+ Assert.That(zeroTimes.EndTime, Is.EqualTo(1000 + loop_duration));
+
+ StoryboardSprite oneTime = background.Elements.OfType().Single(s => s.Path == "one-time.png");
+ Assert.That(oneTime.EndTime, Is.EqualTo(4000 + loop_duration));
+
+ StoryboardSprite manyTimes = background.Elements.OfType().Single(s => s.Path == "many-times.png");
+ Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + 40 * loop_duration));
+ }
+ }
}
}
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index cba7f34ede..4cc71717ff 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -86,7 +86,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get();
- BeatmapSetInfo importedSet;
+ ILive importedSet;
using (var stream = File.OpenRead(tempPath))
{
@@ -97,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(File.Exists(tempPath), "Stream source file somehow went missing");
File.Delete(tempPath);
- var imported = manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID);
+ var imported = manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID);
deleteBeatmapSet(imported, osu);
}
@@ -172,8 +172,8 @@ namespace osu.Game.Tests.Beatmaps.IO
ensureLoaded(osu);
// but contents doesn't, so existing should still be used.
- Assert.IsTrue(imported.ID == importedSecondTime.ID);
- Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
+ Assert.IsTrue(imported.ID == importedSecondTime.Value.ID);
+ Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Value.Beatmaps.First().ID);
}
finally
{
@@ -226,8 +226,8 @@ namespace osu.Game.Tests.Beatmaps.IO
ensureLoaded(osu);
// check the newly "imported" beatmap is not the original.
- Assert.IsTrue(imported.ID != importedSecondTime.ID);
- Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID);
+ Assert.IsTrue(imported.ID != importedSecondTime.Value.ID);
+ Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Value.Beatmaps.First().ID);
}
finally
{
@@ -278,8 +278,8 @@ namespace osu.Game.Tests.Beatmaps.IO
ensureLoaded(osu);
// check the newly "imported" beatmap is not the original.
- Assert.IsTrue(imported.ID != importedSecondTime.ID);
- Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID);
+ Assert.IsTrue(imported.ID != importedSecondTime.Value.ID);
+ Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Value.Beatmaps.First().ID);
}
finally
{
@@ -329,8 +329,8 @@ namespace osu.Game.Tests.Beatmaps.IO
ensureLoaded(osu);
// check the newly "imported" beatmap is not the original.
- Assert.IsTrue(imported.ID != importedSecondTime.ID);
- Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID);
+ Assert.IsTrue(imported.ID != importedSecondTime.Value.ID);
+ Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Value.Beatmaps.First().ID);
}
finally
{
@@ -570,8 +570,8 @@ namespace osu.Game.Tests.Beatmaps.IO
var imported = await manager.Import(toImport);
Assert.NotNull(imported);
- Assert.AreEqual(null, imported.Beatmaps[0].OnlineBeatmapID);
- Assert.AreEqual(null, imported.Beatmaps[1].OnlineBeatmapID);
+ Assert.AreEqual(null, imported.Value.Beatmaps[0].OnlineBeatmapID);
+ Assert.AreEqual(null, imported.Value.Beatmaps[1].OnlineBeatmapID);
}
finally
{
@@ -706,7 +706,7 @@ namespace osu.Game.Tests.Beatmaps.IO
ensureLoaded(osu);
- Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("subfolder")), "Files contain common subfolder");
+ Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("subfolder")), "Files contain common subfolder");
}
finally
{
@@ -759,8 +759,8 @@ namespace osu.Game.Tests.Beatmaps.IO
ensureLoaded(osu);
- Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("__MACOSX")), "Files contain resource fork folder, which should be ignored");
- Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("actual_data")), "Files contain common subfolder");
+ Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("__MACOSX")), "Files contain resource fork folder, which should be ignored");
+ Assert.IsFalse(imported.Value.Files.Any(f => f.Filename.Contains("actual_data")), "Files contain common subfolder");
}
finally
{
@@ -909,13 +909,13 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get();
- var importedSet = await manager.Import(new ImportTask(temp));
+ var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false);
ensureLoaded(osu);
waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000);
- return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID);
+ return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID);
}
public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
@@ -924,13 +924,13 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get();
- var importedSet = await manager.Import(new ImportTask(temp));
+ var importedSet = await manager.Import(new ImportTask(temp)).ConfigureAwait(false);
ensureLoaded(osu);
waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000);
- return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID);
+ return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.Value.ID);
}
private void deleteBeatmapSet(BeatmapSetInfo imported, OsuGameBase osu)
@@ -945,13 +945,13 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending);
}
- private static Task createScoreForBeatmap(OsuGameBase osu, BeatmapInfo beatmap)
+ private static Task createScoreForBeatmap(OsuGameBase osu, BeatmapInfo beatmapInfo)
{
return ImportScoreTest.LoadScoreIntoOsu(osu, new ScoreInfo
{
OnlineScoreID = 2,
- Beatmap = beatmap,
- BeatmapInfoID = beatmap.ID
+ BeatmapInfo = beatmapInfo,
+ BeatmapInfoID = beatmapInfo.ID
}, new ImportScoreTest.TestArchiveReader());
}
diff --git a/osu.Game.Tests/Database/GeneralUsageTests.cs b/osu.Game.Tests/Database/GeneralUsageTests.cs
new file mode 100644
index 0000000000..245981cd9b
--- /dev/null
+++ b/osu.Game.Tests/Database/GeneralUsageTests.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using NUnit.Framework;
+
+#nullable enable
+
+namespace osu.Game.Tests.Database
+{
+ [TestFixture]
+ public class GeneralUsageTests : RealmTest
+ {
+ ///
+ /// Just test the construction of a new database works.
+ ///
+ [Test]
+ public void TestConstructRealm()
+ {
+ RunTestWithRealm((realmFactory, _) => { realmFactory.CreateContext().Refresh(); });
+ }
+
+ [Test]
+ public void TestBlockOperations()
+ {
+ RunTestWithRealm((realmFactory, _) =>
+ {
+ using (realmFactory.BlockAllOperations())
+ {
+ }
+ });
+ }
+
+ [Test]
+ public void TestBlockOperationsWithContention()
+ {
+ RunTestWithRealm((realmFactory, _) =>
+ {
+ ManualResetEventSlim stopThreadedUsage = new ManualResetEventSlim();
+ ManualResetEventSlim hasThreadedUsage = new ManualResetEventSlim();
+
+ Task.Factory.StartNew(() =>
+ {
+ using (realmFactory.CreateContext())
+ {
+ hasThreadedUsage.Set();
+
+ stopThreadedUsage.Wait();
+ }
+ }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler);
+
+ hasThreadedUsage.Wait();
+
+ Assert.Throws(() =>
+ {
+ using (realmFactory.BlockAllOperations())
+ {
+ }
+ });
+
+ stopThreadedUsage.Set();
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Database/RealmTest.cs b/osu.Game.Tests/Database/RealmTest.cs
new file mode 100644
index 0000000000..576f901c1a
--- /dev/null
+++ b/osu.Game.Tests/Database/RealmTest.cs
@@ -0,0 +1,83 @@
+// 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.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Nito.AsyncEx;
+using NUnit.Framework;
+using osu.Framework.Logging;
+using osu.Framework.Platform;
+using osu.Framework.Testing;
+using osu.Game.Database;
+
+#nullable enable
+
+namespace osu.Game.Tests.Database
+{
+ [TestFixture]
+ public abstract class RealmTest
+ {
+ private static readonly TemporaryNativeStorage storage;
+
+ static RealmTest()
+ {
+ storage = new TemporaryNativeStorage("realm-test");
+ storage.DeleteDirectory(string.Empty);
+ }
+
+ protected void RunTestWithRealm(Action testAction, [CallerMemberName] string caller = "")
+ {
+ AsyncContext.Run(() =>
+ {
+ var testStorage = storage.GetStorageForDirectory(caller);
+
+ using (var realmFactory = new RealmContextFactory(testStorage, caller))
+ {
+ Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}");
+ testAction(realmFactory, testStorage);
+
+ realmFactory.Dispose();
+
+ Logger.Log($"Final database size: {getFileSize(testStorage, realmFactory)}");
+ realmFactory.Compact();
+ Logger.Log($"Final database size after compact: {getFileSize(testStorage, realmFactory)}");
+ }
+ });
+ }
+
+ protected void RunTestWithRealmAsync(Func testAction, [CallerMemberName] string caller = "")
+ {
+ AsyncContext.Run(async () =>
+ {
+ var testStorage = storage.GetStorageForDirectory(caller);
+
+ using (var realmFactory = new RealmContextFactory(testStorage, caller))
+ {
+ Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}");
+ await testAction(realmFactory, testStorage);
+
+ realmFactory.Dispose();
+
+ Logger.Log($"Final database size: {getFileSize(testStorage, realmFactory)}");
+ realmFactory.Compact();
+ Logger.Log($"Final database size after compact: {getFileSize(testStorage, realmFactory)}");
+ }
+ });
+ }
+
+ private static long getFileSize(Storage testStorage, RealmContextFactory realmFactory)
+ {
+ try
+ {
+ using (var stream = testStorage.GetStream(realmFactory.Filename))
+ return stream?.Length ?? 0;
+ }
+ catch
+ {
+ // windows runs may error due to file still being open.
+ return 0;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
index 8be74f1a7c..f10b11733e 100644
--- a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
+++ b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Tests.Database
storage = new NativeStorage(directory.FullName);
- realmContextFactory = new RealmContextFactory(storage);
+ realmContextFactory = new RealmContextFactory(storage, "test");
keyBindingStore = new RealmKeyBindingStore(realmContextFactory);
}
@@ -53,9 +53,9 @@ namespace osu.Game.Tests.Database
private int queryCount(GlobalAction? match = null)
{
- using (var usage = realmContextFactory.GetForRead())
+ using (var realm = realmContextFactory.CreateContext())
{
- var results = usage.Realm.All();
+ var results = realm.All();
if (match.HasValue)
results = results.Where(k => k.ActionInt == (int)match.Value);
return results.Count();
@@ -69,26 +69,24 @@ namespace osu.Game.Tests.Database
keyBindingStore.Register(testContainer, Enumerable.Empty());
- using (var primaryUsage = realmContextFactory.GetForRead())
+ using (var primaryRealm = realmContextFactory.CreateContext())
{
- var backBinding = primaryUsage.Realm.All().Single(k => k.ActionInt == (int)GlobalAction.Back);
+ var backBinding = primaryRealm.All().Single(k => k.ActionInt == (int)GlobalAction.Back);
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape }));
var tsr = ThreadSafeReference.Create(backBinding);
- using (var usage = realmContextFactory.GetForWrite())
+ using (var threadedContext = realmContextFactory.CreateContext())
{
- var binding = usage.Realm.ResolveReference(tsr);
- binding.KeyCombination = new KeyCombination(InputKey.BackSpace);
-
- usage.Commit();
+ var binding = threadedContext.ResolveReference(tsr);
+ threadedContext.Write(() => binding.KeyCombination = new KeyCombination(InputKey.BackSpace));
}
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace }));
// check still correct after re-query.
- backBinding = primaryUsage.Realm.All().Single(k => k.ActionInt == (int)GlobalAction.Back);
+ backBinding = primaryRealm.All().Single(k => k.ActionInt == (int)GlobalAction.Back);
Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace }));
}
}
diff --git a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs
index dab4825919..9926acf772 100644
--- a/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs
+++ b/osu.Game.Tests/Localisation/BeatmapMetadataRomanisationTest.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Localisation
Title = "Romanised title",
TitleUnicode = "Unicode Title"
};
- var romanisableString = metadata.ToRomanisableString();
+ var romanisableString = metadata.GetDisplayTitleRomanisable();
Assert.AreEqual(metadata.ToString(), romanisableString.Romanised);
Assert.AreEqual($"{metadata.ArtistUnicode} - {metadata.TitleUnicode}", romanisableString.Original);
@@ -33,7 +33,7 @@ namespace osu.Game.Tests.Localisation
Artist = "Romanised Artist",
Title = "Romanised title"
};
- var romanisableString = metadata.ToRomanisableString();
+ var romanisableString = metadata.GetDisplayTitleRomanisable();
Assert.AreEqual(romanisableString.Romanised, romanisableString.Original);
}
diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs
index 8ff2743b6a..ed86daf8b6 100644
--- a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs
+++ b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs
@@ -239,7 +239,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
match = shouldMatch;
}
- public bool Matches(BeatmapInfo beatmap) => match;
+ public bool Matches(BeatmapInfo beatmapInfo) => match;
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) => false;
}
}
diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs
index a55bdd2df8..df42c70c87 100644
--- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs
+++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs
@@ -256,7 +256,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
{
public string CustomValue { get; set; }
- public bool Matches(BeatmapInfo beatmap) => true;
+ public bool Matches(BeatmapInfo beatmapInfo) => true;
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value)
{
diff --git a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs
index aa29d76843..91c6b6c008 100644
--- a/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs
+++ b/osu.Game.Tests/Online/TestDummyAPIRequestHandling.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Tests.Online
AddAssert("response event fired", () => response != null);
- AddAssert("request has response", () => request.Result == response);
+ AddAssert("request has response", () => request.Response == response);
}
[Test]
diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs
index 7e7e5ebc45..79767bc671 100644
--- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs
+++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs
@@ -156,20 +156,49 @@ namespace osu.Game.Tests.Online
{
public TaskCompletionSource AllowImport = new TaskCompletionSource();
- public Task CurrentImportTask { get; private set; }
+ public Task> CurrentImportTask { get; private set; }
- protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize)
- => new TestDownloadRequest(set);
-
- public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false)
- : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap, performOnlineLookups)
+ public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null)
+ : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap)
{
}
- public override async Task Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default)
+ protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host)
{
- await AllowImport.Task.ConfigureAwait(false);
- return await (CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken)).ConfigureAwait(false);
+ return new TestBeatmapModelManager(this, storage, contextFactory, rulesets, api, host);
+ }
+
+ protected override BeatmapModelDownloader CreateBeatmapModelDownloader(BeatmapModelManager modelManager, IAPIProvider api, GameHost host)
+ {
+ return new TestBeatmapModelDownloader(modelManager, api, host);
+ }
+
+ internal class TestBeatmapModelDownloader : BeatmapModelDownloader
+ {
+ public TestBeatmapModelDownloader(BeatmapModelManager modelManager, IAPIProvider apiProvider, GameHost gameHost)
+ : base(modelManager, apiProvider, gameHost)
+ {
+ }
+
+ protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize)
+ => new TestDownloadRequest(set);
+ }
+
+ internal class TestBeatmapModelManager : BeatmapModelManager
+ {
+ private readonly TestBeatmapManager testBeatmapManager;
+
+ public TestBeatmapModelManager(TestBeatmapManager testBeatmapManager, Storage storage, IDatabaseContextFactory databaseContextFactory, RulesetStore rulesetStore, IAPIProvider apiProvider, GameHost gameHost)
+ : base(storage, databaseContextFactory, rulesetStore, gameHost)
+ {
+ this.testBeatmapManager = testBeatmapManager;
+ }
+
+ public override async Task> Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default)
+ {
+ await testBeatmapManager.AllowImport.Task.ConfigureAwait(false);
+ return await (testBeatmapManager.CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken)).ConfigureAwait(false);
+ }
}
}
diff --git a/osu.Game.Tests/Resources/loop-count.osb b/osu.Game.Tests/Resources/loop-count.osb
new file mode 100644
index 0000000000..ec75e85ef1
--- /dev/null
+++ b/osu.Game.Tests/Resources/loop-count.osb
@@ -0,0 +1,15 @@
+osu file format v14
+
+[Events]
+Sprite,Background,TopCentre,"zero-times.png",320,240
+ L,1000,0
+ F,0,0,1000,0,1
+ F,0,1000,2000,1,0
+Sprite,Background,TopCentre,"one-time.png",320,240
+ L,4000,1
+ F,0,0,1000,0,1
+ F,0,1000,2000,1,0
+Sprite,Background,TopCentre,"many-times.png",320,240
+ L,9000,40
+ F,0,0,1000,0,1
+ F,0,1000,2000,1,0
diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
index cd7d744f53..2cd02329b7 100644
--- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
+++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
@@ -141,7 +141,7 @@ namespace osu.Game.Tests.Scores.IO
var beatmapManager = osu.Dependencies.Get();
var scoreManager = osu.Dependencies.Get();
- beatmapManager.Delete(beatmapManager.QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == imported.Beatmap.ID)));
+ beatmapManager.Delete(beatmapManager.QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == imported.BeatmapInfo.ID)));
Assert.That(scoreManager.Query(s => s.ID == imported.ID).DeletePending, Is.EqualTo(true));
var secondImport = await LoadScoreIntoOsu(osu, imported);
@@ -181,7 +181,7 @@ namespace osu.Game.Tests.Scores.IO
{
var beatmapManager = osu.Dependencies.Get();
- score.Beatmap ??= beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First();
+ score.BeatmapInfo ??= beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First();
score.Ruleset ??= new OsuRuleset().RulesetInfo;
var scoreManager = osu.Dependencies.Get();
diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
index 7a9fc20426..b2600bb887 100644
--- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
+++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
@@ -196,7 +196,7 @@ namespace osu.Game.Tests.Skins.IO
private async Task loadSkinIntoOsu(OsuGameBase osu, ArchiveReader archive = null)
{
var skinManager = osu.Dependencies.Get();
- return await skinManager.Import(archive);
+ return (await skinManager.Import(archive)).Value;
}
}
}
diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs
index eff430ac25..f03cda1489 100644
--- a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs
+++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Tests.Skins
private void load()
{
var imported = beatmaps.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-beatmap.osz"))).Result;
- beatmap = beatmaps.GetWorkingBeatmap(imported.Beatmaps[0]);
+ beatmap = beatmaps.GetWorkingBeatmap(imported.Value.Beatmaps[0]);
beatmap.LoadTrack();
}
diff --git a/osu.Game.Tests/Skins/TestSceneSkinResources.cs b/osu.Game.Tests/Skins/TestSceneSkinResources.cs
index 107a96292f..10f1ab31df 100644
--- a/osu.Game.Tests/Skins/TestSceneSkinResources.cs
+++ b/osu.Game.Tests/Skins/TestSceneSkinResources.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Tests.Skins
private void load()
{
var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).Result;
- skin = skins.GetSkin(imported);
+ skin = skins.GetSkin(imported.Value);
}
[Test]
diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
index 1670d86545..693c66ccb0 100644
--- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
+++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
@@ -232,7 +232,7 @@ namespace osu.Game.Tests.Visual.Background
AddStep("Transition to Results", () => player.Push(results = new FadeAccessibleResults(new ScoreInfo
{
User = new User { Username = "osu!" },
- Beatmap = new TestBeatmap(Ruleset.Value).BeatmapInfo,
+ BeatmapInfo = new TestBeatmap(Ruleset.Value).BeatmapInfo,
Ruleset = Ruleset.Value,
})));
@@ -286,7 +286,7 @@ namespace osu.Game.Tests.Visual.Background
private void setupUserSettings()
{
AddUntilStep("Song select is current", () => songSelect.IsCurrentScreen());
- AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null);
+ AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmapInfo != null);
AddStep("Set default user settings", () =>
{
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs
new file mode 100644
index 0000000000..2258a209e2
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs
@@ -0,0 +1,62 @@
+// 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.Input;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Screens.Edit;
+using osu.Game.Screens.Menu;
+using osu.Game.Screens.Select;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Visual.Editing
+{
+ public class TestSceneEditorSaving : OsuGameTestScene
+ {
+ private Editor editor => Game.ChildrenOfType().FirstOrDefault();
+
+ private EditorBeatmap editorBeatmap => (EditorBeatmap)editor.Dependencies.Get(typeof(EditorBeatmap));
+
+ ///
+ /// Tests the general expected flow of creating a new beatmap, saving it, then loading it back from song select.
+ ///
+ [Test]
+ public void TestNewBeatmapSaveThenLoad()
+ {
+ AddStep("set default beatmap", () => Game.Beatmap.SetDefault());
+
+ PushAndConfirm(() => new EditorLoader());
+
+ AddUntilStep("wait for editor load", () => editor != null);
+
+ AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint()));
+
+ AddStep("Enter compose mode", () => InputManager.Key(Key.F1));
+ AddUntilStep("Wait for compose mode load", () => editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true);
+
+ AddStep("Change to placement mode", () => InputManager.Key(Key.Number2));
+ AddStep("Move to playfield", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre));
+ AddStep("Place single hitcircle", () => InputManager.Click(MouseButton.Left));
+
+ AddStep("Save and exit", () =>
+ {
+ InputManager.Keys(PlatformAction.Save);
+ InputManager.Key(Key.Escape);
+ });
+
+ AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
+
+ PushAndConfirm(() => new PlaySongSelect());
+
+ AddUntilStep("Wait for beatmap selected", () => !Game.Beatmap.IsDefault);
+ AddStep("Open options", () => InputManager.Key(Key.F3));
+ AddStep("Enter editor", () => InputManager.Key(Key.Number5));
+
+ AddUntilStep("Wait for editor load", () => editor != null);
+ AddAssert("Beatmap contains single hitcircle", () => editorBeatmap.HitObjects.Count == 1);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs
index e560c81fb2..7398527f57 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs
@@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestEmptyLegacyBeatmapSkinFallsBack()
{
CreateSkinTest(SkinInfo.Default, () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null));
+ AddUntilStep("wait for hud load", () => Player.ChildrenOfType().All(c => c.ComponentsLoaded));
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value));
}
@@ -84,18 +85,18 @@ namespace osu.Game.Tests.Visual.Gameplay
Remove(expectedComponentsAdjustmentContainer);
return almostEqual(actualInfo, expectedInfo);
-
- static bool almostEqual(SkinnableInfo info, SkinnableInfo other) =>
- other != null
- && info.Type == other.Type
- && info.Anchor == other.Anchor
- && info.Origin == other.Origin
- && Precision.AlmostEquals(info.Position, other.Position)
- && Precision.AlmostEquals(info.Scale, other.Scale)
- && Precision.AlmostEquals(info.Rotation, other.Rotation)
- && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer(almostEqual));
}
+ private static bool almostEqual(SkinnableInfo info, SkinnableInfo other) =>
+ other != null
+ && info.Type == other.Type
+ && info.Anchor == other.Anchor
+ && info.Origin == other.Origin
+ && Precision.AlmostEquals(info.Position, other.Position, 1)
+ && Precision.AlmostEquals(info.Scale, other.Scale)
+ && Precision.AlmostEquals(info.Rotation, other.Rotation)
+ && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer(almostEqual));
+
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
=> new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin);
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs
new file mode 100644
index 0000000000..4c48d52acd
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePerformancePointsCounter.cs
@@ -0,0 +1,108 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Diagnostics;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Testing;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Judgements;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Screens.Play;
+using osu.Game.Screens.Play.HUD;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ public class TestScenePerformancePointsCounter : OsuTestScene
+ {
+ [Cached]
+ private GameplayState gameplayState;
+
+ [Cached]
+ private ScoreProcessor scoreProcessor;
+
+ private int iteration;
+ private PerformancePointsCounter counter;
+
+ public TestScenePerformancePointsCounter()
+ {
+ var ruleset = CreateRuleset();
+
+ Debug.Assert(ruleset != null);
+
+ var beatmap = CreateWorkingBeatmap(ruleset.RulesetInfo)
+ .GetPlayableBeatmap(ruleset.RulesetInfo);
+
+ gameplayState = new GameplayState(beatmap, ruleset);
+ scoreProcessor = new ScoreProcessor();
+ }
+
+ protected override Ruleset CreateRuleset() => new OsuRuleset();
+
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("Create counter", () =>
+ {
+ iteration = 0;
+
+ Child = counter = new PerformancePointsCounter
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scale = new Vector2(5),
+ };
+ });
+ }
+
+ [Test]
+ public void TestBasicCounting()
+ {
+ int previousValue = 0;
+
+ AddAssert("counter displaying zero", () => counter.Current.Value == 0);
+
+ AddRepeatStep("Add judgement", applyOneJudgement, 10);
+
+ AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
+ AddUntilStep("counter opaque", () => counter.Child.Alpha == 1);
+
+ AddStep("Revert judgement", () =>
+ {
+ previousValue = counter.Current.Value;
+
+ scoreProcessor.RevertResult(new JudgementResult(new HitObject(), new OsuJudgement()));
+ });
+
+ AddUntilStep("counter decreased", () => counter.Current.Value < previousValue);
+
+ AddStep("Add judgement", applyOneJudgement);
+
+ AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
+ }
+
+ private void applyOneJudgement()
+ {
+ var scoreInfo = gameplayState.Score.ScoreInfo;
+
+ scoreInfo.MaxCombo = iteration * 1000;
+ scoreInfo.Accuracy = 1;
+ scoreInfo.Statistics[HitResult.Great] = iteration * 1000;
+
+ scoreProcessor.ApplyResult(new OsuJudgementResult(new HitObject
+ {
+ StartTime = iteration * 10000,
+ }, new OsuJudgement())
+ {
+ Type = HitResult.Perfect,
+ });
+
+ iteration++;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index 8160a62991..aee15a145c 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -205,7 +205,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen());
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for player to be current", () => player.IsCurrentScreen());
- AddStep("retrieve mods", () => playerMod1 = (TestMod)player.Mods.Value.Single());
+ AddStep("retrieve mods", () => playerMod1 = (TestMod)player.GameplayState.Mods.Single());
AddAssert("game mods not applied", () => gameMod.Applied == false);
AddAssert("player mods applied", () => playerMod1.Applied);
@@ -217,7 +217,7 @@ namespace osu.Game.Tests.Visual.Gameplay
});
AddUntilStep("wait for player to be current", () => player.IsCurrentScreen());
- AddStep("retrieve mods", () => playerMod2 = (TestMod)player.Mods.Value.Single());
+ AddStep("retrieve mods", () => playerMod2 = (TestMod)player.GameplayState.Mods.Single());
AddAssert("game mods not applied", () => gameMod.Applied == false);
AddAssert("player has different mods", () => playerMod1 != playerMod2);
AddAssert("player mods applied", () => playerMod2.Applied);
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
index 1809332bce..5e2374cbcb 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
@@ -1,6 +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.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Online;
@@ -8,34 +9,29 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Game.Scoring;
using osu.Game.Users;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Testing;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Screens.Ranking;
+using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[TestFixture]
- public class TestSceneReplayDownloadButton : OsuTestScene
+ public class TestSceneReplayDownloadButton : OsuManualInputManagerTestScene
{
[Resolved]
private RulesetStore rulesets { get; set; }
private TestReplayDownloadButton downloadButton;
- public TestSceneReplayDownloadButton()
+ [Test]
+ public void TestDisplayStates()
{
- createButton(true);
- AddStep(@"downloading state", () => downloadButton.SetDownloadState(DownloadState.Downloading));
- AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
- AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
- createButton(false);
- createButtonNoScore();
- }
-
- private void createButton(bool withReplay)
- {
- AddStep(withReplay ? @"create button with replay" : "create button without replay", () =>
+ AddStep(@"create button with replay", () =>
{
- Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(withReplay))
+ Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(true))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -43,9 +39,81 @@ namespace osu.Game.Tests.Visual.Gameplay
});
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
+
+ AddStep(@"downloading state", () => downloadButton.SetDownloadState(DownloadState.Downloading));
+ AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
+ AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
}
- private void createButtonNoScore()
+ [Test]
+ public void TestButtonWithReplayStartsDownload()
+ {
+ bool downloadStarted = false;
+ bool downloadFinished = false;
+
+ AddStep(@"create button with replay", () =>
+ {
+ downloadStarted = false;
+ downloadFinished = false;
+
+ Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(true))
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+
+ downloadButton.State.BindValueChanged(state =>
+ {
+ switch (state.NewValue)
+ {
+ case DownloadState.Downloading:
+ downloadStarted = true;
+ break;
+ }
+
+ switch (state.OldValue)
+ {
+ case DownloadState.Downloading:
+ downloadFinished = true;
+ break;
+ }
+ });
+ });
+
+ AddUntilStep("wait for load", () => downloadButton.IsLoaded);
+
+ AddAssert("state is available", () => downloadButton.State.Value == DownloadState.NotDownloaded);
+
+ AddStep("click button", () =>
+ {
+ InputManager.MoveMouseTo(downloadButton);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ AddAssert("state entered downloading", () => downloadStarted);
+ AddUntilStep("state left downloading", () => downloadFinished);
+ }
+
+ [Test]
+ public void TestButtonWithoutReplay()
+ {
+ AddStep("create button without replay", () =>
+ {
+ Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(false))
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+ });
+
+ AddUntilStep("wait for load", () => downloadButton.IsLoaded);
+
+ AddAssert("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded);
+ AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType().First().Enabled.Value);
+ }
+
+ [Test]
+ public void CreateButtonWithNoScore()
{
AddStep("create button with null score", () =>
{
@@ -57,6 +125,9 @@ namespace osu.Game.Tests.Visual.Gameplay
});
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
+
+ AddAssert("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded);
+ AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType().First().Enabled.Value);
}
private ScoreInfo getScoreInfo(bool replayAvailable)
@@ -78,6 +149,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public void SetDownloadState(DownloadState state) => State.Value = state;
+ public new Bindable State => base.State;
+
public TestReplayDownloadButton(ScoreInfo score)
: base(score)
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs
index 0a3fedaf8e..c8040f42f0 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs
@@ -1,6 +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 NUnit.Framework;
@@ -17,6 +18,8 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Replays;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
@@ -38,7 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private TestReplayRecorder recorder;
[Cached]
- private GameplayBeatmap gameplayBeatmap = new GameplayBeatmap(new Beatmap());
+ private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty());
[SetUp]
public void SetUp() => Schedule(() =>
@@ -57,7 +60,7 @@ namespace osu.Game.Tests.Visual.Gameplay
Recorder = recorder = new TestReplayRecorder(new Score
{
Replay = replay,
- ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo }
+ ScoreInfo = { BeatmapInfo = gameplayState.Beatmap.BeatmapInfo }
})
{
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs
index dfd5e2dc58..3545fc96e8 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs
@@ -1,6 +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 osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -13,6 +14,8 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Replays;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
@@ -30,7 +33,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private readonly TestRulesetInputManager recordingManager;
[Cached]
- private GameplayBeatmap gameplayBeatmap = new GameplayBeatmap(new Beatmap());
+ private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty());
public TestSceneReplayRecording()
{
@@ -48,7 +51,7 @@ namespace osu.Game.Tests.Visual.Gameplay
Recorder = new TestReplayRecorder(new Score
{
Replay = replay,
- ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo }
+ ScoreInfo = { BeatmapInfo = gameplayState.Beatmap.BeatmapInfo }
})
{
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
index 6f5f774758..b4de060578 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
@@ -25,6 +25,8 @@ using osu.Game.Online.Spectator;
using osu.Game.Replays;
using osu.Game.Replays.Legacy;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.UI;
@@ -62,7 +64,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private SpectatorClient spectatorClient { get; set; }
[Cached]
- private GameplayBeatmap gameplayBeatmap = new GameplayBeatmap(new Beatmap());
+ private GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), Array.Empty());
[SetUp]
public void SetUp() => Schedule(() =>
@@ -354,7 +356,7 @@ namespace osu.Game.Tests.Visual.Gameplay
internal class TestReplayRecorder : ReplayRecorder
{
public TestReplayRecorder()
- : base(new Score { ScoreInfo = { Beatmap = new BeatmapInfo() } })
+ : base(new Score { ScoreInfo = { BeatmapInfo = new BeatmapInfo() } })
{
}
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs
index 5fdadfc2fb..4754a73f83 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs
@@ -6,6 +6,7 @@ using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
using osu.Game.Overlays.Login;
namespace osu.Game.Tests.Visual.Menus
@@ -30,12 +31,25 @@ namespace osu.Game.Tests.Visual.Menus
}
[Test]
- public void TestBasicLogin()
+ public void TestLoginSuccess()
{
AddStep("logout", () => API.Logout());
AddStep("enter password", () => loginPanel.ChildrenOfType().First().Text = "password");
AddStep("submit", () => loginPanel.ChildrenOfType().First(b => b.Text.ToString() == "Sign in").TriggerClick());
}
+
+ [Test]
+ public void TestLoginFailure()
+ {
+ AddStep("logout", () =>
+ {
+ API.Logout();
+ ((DummyAPIAccess)API).FailNextLogin();
+ });
+
+ AddStep("enter password", () => loginPanel.ChildrenOfType().First().Text = "password");
+ AddStep("submit", () => loginPanel.ChildrenOfType().First(b => b.Text.ToString() == "Sign in").TriggerClick());
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
index 9037338e23..79dfe79299 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("import beatmap with track", () =>
{
var setWithTrack = Game.BeatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).Result;
- Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(setWithTrack.Beatmaps.First());
+ Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(setWithTrack.Value.Beatmaps.First());
});
AddStep("bind to track change", () =>
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
index b1f5781f6f..22ff2b98ce 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
@@ -43,11 +43,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
Spacing = new Vector2(10),
Children = new Drawable[]
{
- createDrawableRoom(new Room
+ createLoungeRoom(new Room
{
- Name = { Value = "Flyte's Trash Playlist" },
+ Name = { Value = "Multiplayer room" },
Status = { Value = new RoomStatusOpen() },
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
+ Type = { Value = MatchType.HeadToHead },
Playlist =
{
new PlaylistItem
@@ -65,9 +66,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
}
}),
- createDrawableRoom(new Room
+ createLoungeRoom(new Room
{
- Name = { Value = "Room 2" },
+ Name = { Value = "Playlist room with multiple beatmaps" },
Status = { Value = new RoomStatusPlaying() },
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
Playlist =
@@ -100,15 +101,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
}
}),
- createDrawableRoom(new Room
+ createLoungeRoom(new Room
{
- Name = { Value = "Room 3" },
+ Name = { Value = "Finished room" },
Status = { Value = new RoomStatusEnded() },
EndDate = { Value = DateTimeOffset.Now },
}),
- createDrawableRoom(new Room
+ createLoungeRoom(new Room
{
- Name = { Value = "Room 4 (spotlight)" },
+ Name = { Value = "Spotlight room" },
Status = { Value = new RoomStatusOpen() },
Category = { Value = RoomCategory.Spotlight },
}),
@@ -123,14 +124,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
DrawableRoom drawableRoom = null;
Room room = null;
- AddStep("create room", () => Child = drawableRoom = createDrawableRoom(room = new Room
+ AddStep("create room", () => Child = drawableRoom = createLoungeRoom(room = new Room
{
Name = { Value = "Room with password" },
Status = { Value = new RoomStatusOpen() },
Type = { Value = MatchType.HeadToHead },
}));
- AddUntilStep("wait for panel load", () => drawableRoom.ChildrenOfType().Any());
+ AddUntilStep("wait for panel load", () => drawableRoom.ChildrenOfType().Any());
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha));
@@ -141,7 +142,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha));
}
- private DrawableRoom createDrawableRoom(Room room)
+ private DrawableRoom createLoungeRoom(Room room)
{
room.Host.Value ??= new User { Username = "peppy", Id = 2 };
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs
similarity index 66%
rename from osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs
rename to osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs
index 50ec2bf3ac..982dfc5cd9 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRecentParticipantsList.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs
@@ -13,16 +13,27 @@ using osu.Game.Users.Drawables;
namespace osu.Game.Tests.Visual.Multiplayer
{
- public class TestSceneRecentParticipantsList : OnlinePlayTestScene
+ public class TestSceneDrawableRoomParticipantsList : OnlinePlayTestScene
{
- private RecentParticipantsList list;
+ private DrawableRoomParticipantsList list;
[SetUp]
public new void Setup() => Schedule(() =>
{
- SelectedRoom.Value = new Room { Name = { Value = "test room" } };
+ SelectedRoom.Value = new Room
+ {
+ Name = { Value = "test room" },
+ Host =
+ {
+ Value = new User
+ {
+ Id = 2,
+ Username = "peppy",
+ }
+ }
+ };
- Child = list = new RecentParticipantsList
+ Child = list = new DrawableRoomParticipantsList
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -40,19 +51,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
AddStep("set 8 circles", () => list.NumberOfCircles = 8);
- AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
+ AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
AddStep("add one more user", () => addUser(9));
- AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2);
+ AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2);
AddStep("remove first user", () => removeUserAt(0));
- AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
+ AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
AddStep("add one more user", () => addUser(9));
- AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2);
+ AddAssert("2 hidden users", () => list.ChildrenOfType().Single().Count == 2);
AddStep("remove last user", () => removeUserAt(8));
- AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
+ AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
}
[Test]
@@ -69,9 +80,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
for (int i = 0; i < 8; i++)
{
AddStep("remove user", () => removeUserAt(0));
- int remainingUsers = 7 - i;
+ int remainingUsers = 8 - i;
- int displayedUsers = remainingUsers > 3 ? 2 : remainingUsers;
+ int displayedUsers = remainingUsers > 4 ? 3 : remainingUsers;
AddAssert($"{displayedUsers} avatars displayed", () => list.ChildrenOfType().Count() == displayedUsers);
}
}
@@ -86,12 +97,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
AddStep("set 3 circles", () => list.NumberOfCircles = 3);
- AddAssert("2 users displayed", () => list.ChildrenOfType().Count() == 2);
- AddAssert("48 hidden users", () => list.ChildrenOfType().Single().Count == 48);
+ AddAssert("3 users displayed", () => list.ChildrenOfType().Count() == 3);
+ AddAssert("48 hidden users", () => list.ChildrenOfType().Single().Count == 48);
AddStep("set 10 circles", () => list.NumberOfCircles = 10);
- AddAssert("9 users displayed", () => list.ChildrenOfType().Count() == 9);
- AddAssert("41 hidden users", () => list.ChildrenOfType().Single().Count == 41);
+ AddAssert("10 users displayed", () => list.ChildrenOfType().Count() == 10);
+ AddAssert("41 hidden users", () => list.ChildrenOfType().Single().Count == 41);
}
[Test]
@@ -104,24 +115,24 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
AddStep("remove from start", () => removeUserAt(0));
- AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3);
- AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46);
+ AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4);
+ AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46);
AddStep("remove from end", () => removeUserAt(SelectedRoom.Value.RecentParticipants.Count - 1));
- AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3);
- AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45);
+ AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4);
+ AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45);
AddRepeatStep("remove 45 users", () => removeUserAt(0), 45);
- AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3);
- AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
- AddAssert("hidden users bubble hidden", () => list.ChildrenOfType().Single().Alpha < 0.5f);
+ AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4);
+ AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
+ AddAssert("hidden users bubble hidden", () => list.ChildrenOfType().Single().Alpha < 0.5f);
AddStep("remove another user", () => removeUserAt(0));
- AddAssert("2 circles displayed", () => list.ChildrenOfType().Count() == 2);
- AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
+ AddAssert("3 circles displayed", () => list.ChildrenOfType().Count() == 3);
+ AddAssert("0 hidden users", () => list.ChildrenOfType().Single().Count == 0);
AddRepeatStep("remove the remaining two users", () => removeUserAt(0), 2);
- AddAssert("0 circles displayed", () => !list.ChildrenOfType().Any());
+ AddAssert("1 circle displayed", () => list.ChildrenOfType().Count() == 1);
}
private void addUser(int id)
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs
index ff06d4d9c7..5032cdaec7 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Accuracy = 0.8,
MaxCombo = 500,
Combo = 250,
- Beatmap = beatmapInfo,
+ BeatmapInfo = beatmapInfo,
User = new User { Username = "Test user" },
Date = DateTimeOffset.Now,
OnlineScoreID = 12345,
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs
index 0a8bda7ec0..99d5fd46e9 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Accuracy = 0.8,
MaxCombo = 500,
Combo = 250,
- Beatmap = beatmapInfo,
+ BeatmapInfo = beatmapInfo,
User = new User { Username = "Test user" },
Date = DateTimeOffset.Now,
OnlineScoreID = 12345,
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs
index a8fda19c60..80217a7726 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs
@@ -6,6 +6,7 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
+using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
@@ -68,6 +69,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
LoadScreen(dependenciesScreen = new DependenciesScreen(client));
});
+ AddUntilStep("wait for dependencies screen", () => Stack.CurrentScreen is DependenciesScreen);
+ AddUntilStep("wait for dependencies to start load", () => dependenciesScreen.LoadState > LoadState.NotLoaded);
AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded);
AddStep("load multiplayer", () => LoadScreen(multiplayerScreen));
diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs
index e2baa82ba0..7327d4053a 100644
--- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Textures;
@@ -123,6 +124,13 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("ruleset unchanged", () => ReferenceEquals(Ruleset.Value, ruleset));
}
+ [Test]
+ public void TestSwitchThreadExecutionMode()
+ {
+ AddStep("Change thread mode to multi threaded", () => { game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.MultiThreaded); });
+ AddStep("Change thread mode to single thread", () => { game.Dependencies.Get().SetValue(FrameworkSetting.ExecutionMode, ExecutionMode.SingleThread); });
+ }
+
[Test]
public void TestUnavailableRulesetHandled()
{
diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs
index f0ddefa51d..5f5ebfccfb 100644
--- a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs
@@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual.Navigation
Ruleset = ruleset ?? new OsuRuleset().RulesetInfo
},
}
- }).Result;
+ }).Result.Value;
});
AddAssert($"import {i} succeeded", () => imported != null);
diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
index 52b577b402..aca7ada535 100644
--- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
@@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Navigation
Ruleset = new OsuRuleset().RulesetInfo
},
}
- }).Result;
+ }).Result.Value;
});
}
@@ -130,9 +130,9 @@ namespace osu.Game.Tests.Visual.Navigation
{
Hash = Guid.NewGuid().ToString(),
OnlineScoreID = i,
- Beatmap = beatmap.Beatmaps.First(),
+ BeatmapInfo = beatmap.Beatmaps.First(),
Ruleset = ruleset ?? new OsuRuleset().RulesetInfo
- }).Result;
+ }).Result.Value;
});
AddAssert($"import {i} succeeded", () => imported != null);
diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
index aeb800f58a..ce437e7299 100644
--- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
@@ -350,13 +350,13 @@ namespace osu.Game.Tests.Visual.Navigation
// since most overlays use a scroll container that absorbs on mouse down
NowPlayingOverlay nowPlayingOverlay = null;
- AddStep("enter menu", () => InputManager.Key(Key.Enter));
+ AddUntilStep("Wait for now playing load", () => (nowPlayingOverlay = Game.ChildrenOfType().FirstOrDefault()) != null);
- AddStep("get and press now playing hotkey", () =>
- {
- nowPlayingOverlay = Game.ChildrenOfType().Single();
- InputManager.Key(Key.F6);
- });
+ AddStep("enter menu", () => InputManager.Key(Key.Enter));
+ AddUntilStep("toolbar displayed", () => Game.Toolbar.State.Value == Visibility.Visible);
+
+ AddStep("open now playing", () => InputManager.Key(Key.F6));
+ AddUntilStep("now playing is visible", () => nowPlayingOverlay.State.Value == Visibility.Visible);
// drag tests
@@ -417,7 +417,7 @@ namespace osu.Game.Tests.Visual.Navigation
pushEscape(); // returns to osu! logo
AddStep("Hold escape", () => InputManager.PressKey(Key.Escape));
- AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroTriangles);
+ AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroScreen);
AddStep("Release escape", () => InputManager.ReleaseKey(Key.Escape));
AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null);
AddStep("test dispose doesn't crash", () => Game.Dispose());
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index f420ad976b..453e26ef96 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -233,7 +233,7 @@ namespace osu.Game.Tests.Visual.Online
});
});
- AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.Beatmap.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value)));
+ AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.BeatmapInfo.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value)));
AddAssert("left-most beatmap selected", () => overlay.Header.HeaderContent.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected);
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs
index fd5c188b94..fe8e33f783 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs
@@ -58,10 +58,10 @@ namespace osu.Game.Tests.Visual.Online
var firstBeatmap = createBeatmap();
var secondBeatmap = createBeatmap();
- AddStep("set first set", () => successRate.Beatmap = firstBeatmap);
+ AddStep("set first set", () => successRate.BeatmapInfo = firstBeatmap);
AddAssert("ratings set", () => successRate.Graph.Metrics == firstBeatmap.Metrics);
- AddStep("set second set", () => successRate.Beatmap = secondBeatmap);
+ AddStep("set second set", () => successRate.BeatmapInfo = secondBeatmap);
AddAssert("ratings set", () => successRate.Graph.Metrics == secondBeatmap.Metrics);
static BeatmapInfo createBeatmap() => new BeatmapInfo
@@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestOnlyFailMetrics()
{
- AddStep("set beatmap", () => successRate.Beatmap = new BeatmapInfo
+ AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo
{
Metrics = new BeatmapMetrics
{
@@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestEmptyMetrics()
{
- AddStep("set beatmap", () => successRate.Beatmap = new BeatmapInfo
+ AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo
{
Metrics = new BeatmapMetrics()
});
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
index 609e637914..9562b41363 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -18,6 +19,7 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
using osu.Game.Overlays;
+using osu.Game.Overlays.Chat;
using osu.Game.Overlays.Chat.Selection;
using osu.Game.Overlays.Chat.Tabs;
using osu.Game.Users;
@@ -41,6 +43,9 @@ namespace osu.Game.Tests.Visual.Online
private Channel channel2 => channels[1];
private Channel channel3 => channels[2];
+ [CanBeNull]
+ private Func> onGetMessages;
+
[Resolved]
private GameHost host { get; set; }
@@ -79,6 +84,8 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("register request handling", () =>
{
+ onGetMessages = null;
+
((DummyAPIAccess)API).HandleRequest = req =>
{
switch (req)
@@ -102,6 +109,12 @@ namespace osu.Game.Tests.Visual.Online
}
return true;
+
+ case GetMessagesRequest getMessages:
+ var messages = onGetMessages?.Invoke(getMessages.Channel);
+ if (messages != null)
+ getMessages.TriggerSuccess(messages);
+ return true;
}
return false;
@@ -122,14 +135,37 @@ namespace osu.Game.Tests.Visual.Online
}
[Test]
- public void TestSelectingChannelClosesSelector()
+ public void TestChannelSelection()
{
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
+ AddStep("Setup get message response", () => onGetMessages = channel =>
+ {
+ if (channel == channel1)
+ {
+ return new List
+ {
+ new Message(1)
+ {
+ ChannelId = channel1.Id,
+ Content = "hello from channel 1!",
+ Sender = new User
+ {
+ Id = 2,
+ Username = "test_user"
+ }
+ }
+ };
+ }
+
+ return null;
+ });
AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
AddAssert("Current channel is channel 1", () => currentChannel == channel1);
+ AddUntilStep("Loading spinner hidden", () => chatOverlay.ChildrenOfType().All(spinner => !spinner.IsPresent));
+ AddAssert("Channel message shown", () => chatOverlay.ChildrenOfType().Count() == 1);
AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs
index 5dca218531..513631a221 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Online
{
PP = 1047.21,
Rank = ScoreRank.SH,
- Beatmap = new BeatmapInfo
+ BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
@@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Online
{
PP = 134.32,
Rank = ScoreRank.A,
- Beatmap = new BeatmapInfo
+ BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
@@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.Online
{
PP = 96.83,
Rank = ScoreRank.S,
- Beatmap = new BeatmapInfo
+ BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
@@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Online
var noPPScore = new ScoreInfo
{
Rank = ScoreRank.B,
- Beatmap = new BeatmapInfo
+ BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
index 9051c71fc6..d8ec89a94e 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
@@ -117,7 +117,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 1;
- importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result;
+ importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result.Value;
});
AddStep("load room", () =>
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs
index a5e2f02f31..df8500fab2 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs
@@ -71,7 +71,7 @@ namespace osu.Game.Tests.Visual.Ranking
Id = 2,
Username = "peppy",
},
- Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
+ BeatmapInfo = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 2845370,
Accuracy = accuracy,
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
index 5180854aba..899f351a2a 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Ranking
AddStep("show example score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo)
{
- Beatmap = createTestBeatmap(author)
+ BeatmapInfo = createTestBeatmap(author)
}));
}
@@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Ranking
AddStep("show excess mods score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo, true)
{
- Beatmap = createTestBeatmap(author)
+ BeatmapInfo = createTestBeatmap(author)
}));
AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Current.Value == "mapper_name"));
@@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Ranking
{
AddStep("show example score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo)
{
- Beatmap = createTestBeatmap(null)
+ BeatmapInfo = createTestBeatmap(null)
}));
AddAssert("mapped by text not present", () =>
@@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Ranking
showPanel(new TestScoreInfo(ruleset.RulesetInfo)
{
Mods = mods,
- Beatmap = beatmap,
+ BeatmapInfo = beatmap,
Date = default,
});
});
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
index 631455b727..8d5d0ba8c7 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
@@ -337,8 +337,8 @@ namespace osu.Game.Tests.Visual.Ranking
public UnrankedSoloResultsScreen(ScoreInfo score)
: base(score, true)
{
- Score.Beatmap.OnlineBeatmapID = 0;
- Score.Beatmap.Status = BeatmapSetOnlineStatus.Pending;
+ Score.BeatmapInfo.OnlineBeatmapID = 0;
+ Score.BeatmapInfo.Status = BeatmapSetOnlineStatus.Pending;
}
protected override void LoadComplete()
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
index 6f3b3028be..b7b7407428 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
@@ -221,6 +221,8 @@ namespace osu.Game.Tests.Visual.Ranking
list.SelectedScore.Value = middleScore;
});
+ AddUntilStep("wait for all scores to be visible", () => list.ChildrenOfType().All(t => t.IsPresent));
+
assertScoreState(highestScore, false);
assertScoreState(middleScore, true);
assertScoreState(lowestScore, false);
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
index 168d9fafcf..1effe52608 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
@@ -7,6 +7,7 @@ using NUnit.Framework;
using osu.Framework.Testing;
using osu.Framework.Threading;
using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Overlays.Settings.Sections.Input;
using osuTK.Input;
@@ -230,6 +231,22 @@ namespace osu.Game.Tests.Visual.Settings
AddAssert("first binding selected", () => multiBindingRow.ChildrenOfType().First().IsBinding);
}
+ [Test]
+ public void TestFilteringHidesResetSectionButtons()
+ {
+ SearchTextBox searchTextBox = null;
+
+ AddStep("add any search term", () =>
+ {
+ searchTextBox = panel.ChildrenOfType().Single();
+ searchTextBox.Current.Value = "chat";
+ });
+ AddUntilStep("all reset section bindings buttons hidden", () => panel.ChildrenOfType().All(button => button.Alpha == 0));
+
+ AddStep("clear search term", () => searchTextBox.Current.Value = string.Empty);
+ AddUntilStep("all reset section bindings buttons shown", () => panel.ChildrenOfType().All(button => button.Alpha == 1));
+ }
+
private void checkBinding(string name, string keyName)
{
AddAssert($"Check {name} is bound to {keyName}", () =>
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
index dcc2111ad3..4538e36c5e 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs
@@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestNoMod()
{
- AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+ AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
AddStep("no mods selected", () => SelectedMods.Value = Array.Empty());
@@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestManiaFirstBarText()
{
- AddStep("set beatmap", () => advancedStats.Beatmap = new BeatmapInfo
+ AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(3),
BaseDifficulty = new BeatmapDifficulty
@@ -84,11 +84,11 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestEasyMod()
{
- AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+ AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
AddStep("select EZ mod", () =>
{
- var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
+ var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance();
SelectedMods.Value = new[] { ruleset.CreateMod() };
});
@@ -101,11 +101,11 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestHardRockMod()
{
- AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+ AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
AddStep("select HR mod", () =>
{
- var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
+ var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance();
SelectedMods.Value = new[] { ruleset.CreateMod() };
});
@@ -118,13 +118,13 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestUnchangedDifficultyAdjustMod()
{
- AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+ AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
AddStep("select unchanged Difficulty Adjust mod", () =>
{
- var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
+ var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance();
var difficultyAdjustMod = ruleset.CreateMod();
- difficultyAdjustMod.ReadFromDifficulty(advancedStats.Beatmap.BaseDifficulty);
+ difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.BaseDifficulty);
SelectedMods.Value = new[] { difficultyAdjustMod };
});
@@ -137,13 +137,13 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestChangedDifficultyAdjustMod()
{
- AddStep("set beatmap", () => advancedStats.Beatmap = exampleBeatmapInfo);
+ AddStep("set beatmap", () => advancedStats.BeatmapInfo = exampleBeatmapInfo);
AddStep("select changed Difficulty Adjust mod", () =>
{
- var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance();
+ var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance();
var difficultyAdjustMod = ruleset.CreateMod();
- var originalDifficulty = advancedStats.Beatmap.BaseDifficulty;
+ var originalDifficulty = advancedStats.BeatmapInfo.BaseDifficulty;
difficultyAdjustMod.ReadFromDifficulty(originalDifficulty);
difficultyAdjustMod.DrainRate.Value = originalDifficulty.DrainRate - 0.5f;
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
index 78ddfa9ed2..66f15670f5 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private readonly Stack selectedSets = new Stack();
private readonly HashSet eagerSelectedIDs = new HashSet();
- private BeatmapInfo currentSelection => carousel.SelectedBeatmap;
+ private BeatmapInfo currentSelection => carousel.SelectedBeatmapInfo;
private const int set_count = 5;
@@ -75,11 +75,11 @@ namespace osu.Game.Tests.Visual.SongSelect
{
for (int i = 0; i < 3; i++)
{
- AddStep("store selection", () => selection = carousel.SelectedBeatmap);
+ AddStep("store selection", () => selection = carousel.SelectedBeatmapInfo);
if (isIterating)
- AddUntilStep("selection changed", () => carousel.SelectedBeatmap != selection);
+ AddUntilStep("selection changed", () => carousel.SelectedBeatmapInfo != selection);
else
- AddUntilStep("selection not changed", () => carousel.SelectedBeatmap == selection);
+ AddUntilStep("selection not changed", () => carousel.SelectedBeatmapInfo == selection);
}
}
}
@@ -387,7 +387,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("Set non-empty mode filter", () =>
carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(1) }, false));
- AddAssert("Something is selected", () => carousel.SelectedBeatmap != null);
+ AddAssert("Something is selected", () => carousel.SelectedBeatmapInfo != null);
}
///
@@ -562,7 +562,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("filter to ruleset 0", () =>
carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false));
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false));
- AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmap.RulesetID == 0);
+ AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmapInfo.RulesetID == 0);
AddStep("remove mixed set", () =>
{
@@ -653,7 +653,7 @@ namespace osu.Game.Tests.Visual.SongSelect
carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false);
});
- AddAssert("selection lost", () => carousel.SelectedBeatmap == null);
+ AddAssert("selection lost", () => carousel.SelectedBeatmapInfo == null);
AddStep("Restore different ruleset filter", () =>
{
@@ -661,7 +661,7 @@ namespace osu.Game.Tests.Visual.SongSelect
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
});
- AddAssert("selection changed", () => carousel.SelectedBeatmap != manySets.First().Beatmaps.First());
+ AddAssert("selection changed", () => carousel.SelectedBeatmapInfo != manySets.First().Beatmaps.First());
}
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 2);
@@ -763,9 +763,9 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () =>
{
if (diff != null)
- return carousel.SelectedBeatmap == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First();
+ return carousel.SelectedBeatmapInfo == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First();
- return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmap);
+ return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmapInfo);
});
private void setSelected(int set, int diff) =>
@@ -800,7 +800,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
carousel.RandomAlgorithm.Value = RandomSelectAlgorithm.RandomPermutation;
- if (!selectedSets.Any() && carousel.SelectedBeatmap != null)
+ if (!selectedSets.Any() && carousel.SelectedBeatmapInfo != null)
selectedSets.Push(carousel.SelectedBeatmapSet);
carousel.SelectNextRandom();
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
index b4544fbc85..d5b4fb9a80 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestAllMetrics()
{
- AddStep("all metrics", () => details.Beatmap = new BeatmapInfo
+ AddStep("all metrics", () => details.BeatmapInfo = new BeatmapInfo
{
BeatmapSet = new BeatmapSetInfo
{
@@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestAllMetricsExceptSource()
{
- AddStep("all except source", () => details.Beatmap = new BeatmapInfo
+ AddStep("all except source", () => details.BeatmapInfo = new BeatmapInfo
{
BeatmapSet = new BeatmapSetInfo
{
@@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestOnlyRatings()
{
- AddStep("ratings", () => details.Beatmap = new BeatmapInfo
+ AddStep("ratings", () => details.BeatmapInfo = new BeatmapInfo
{
BeatmapSet = new BeatmapSetInfo
{
@@ -117,7 +117,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestOnlyFailsAndRetries()
{
- AddStep("fails retries", () => details.Beatmap = new BeatmapInfo
+ AddStep("fails retries", () => details.BeatmapInfo = new BeatmapInfo
{
Version = "Only Retries and Fails",
Metadata = new BeatmapMetadata
@@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestNoMetrics()
{
- AddStep("no metrics", () => details.Beatmap = new BeatmapInfo
+ AddStep("no metrics", () => details.BeatmapInfo = new BeatmapInfo
{
Version = "No Metrics",
Metadata = new BeatmapMetadata
@@ -166,13 +166,13 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestNullBeatmap()
{
- AddStep("null beatmap", () => details.Beatmap = null);
+ AddStep("null beatmap", () => details.BeatmapInfo = null);
}
[Test]
public void TestOnlineMetrics()
{
- AddStep("online ratings/retries/fails", () => details.Beatmap = new BeatmapInfo
+ AddStep("online ratings/retries/fails", () => details.BeatmapInfo = new BeatmapInfo
{
OnlineBeatmapID = 162,
});
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
index 29815ce9ff..13b769c80a 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
@@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.SongSelect
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First();
- leaderboard.Beatmap = beatmapInfo;
+ leaderboard.BeatmapInfo = beatmapInfo;
});
clearScores();
@@ -186,7 +186,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void checkCount(int expected) =>
AddUntilStep("Correct count displayed", () => leaderboard.ChildrenOfType().Count() == expected);
- private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmap)
+ private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmapInfo)
{
return new[]
{
@@ -197,7 +197,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 6602580,
@@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 4608074,
@@ -235,7 +235,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 1014222,
@@ -254,7 +254,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 1541390,
@@ -273,7 +273,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 2243452,
@@ -292,7 +292,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 2705430,
@@ -311,7 +311,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 7151382,
@@ -330,7 +330,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 2051389,
@@ -349,7 +349,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 6169483,
@@ -368,7 +368,7 @@ namespace osu.Game.Tests.Visual.SongSelect
MaxCombo = 244,
TotalScore = 1707827,
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
- Beatmap = beatmap,
+ BeatmapInfo = beatmapInfo,
User = new User
{
Id = 6702666,
@@ -385,7 +385,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void showBeatmapWithStatus(BeatmapSetOnlineStatus status)
{
- leaderboard.Beatmap = new BeatmapInfo
+ leaderboard.BeatmapInfo = new BeatmapInfo
{
OnlineBeatmapID = 1113057,
Status = status,
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
index 53cb628bb3..c22b6a54e9 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
@@ -192,7 +192,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}).ToList()
};
- return Game.BeatmapManager.Import(beatmapSet).Result;
+ return Game.BeatmapManager.Import(beatmapSet).Result.Value;
}
private bool ensureAllBeatmapSetsImported(IEnumerable beatmapSets) => beatmapSets.All(set => set != null);
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index 102e5ee425..067f1cabb4 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select next and enter", () =>
{
InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType()
- .First(b => ((CarouselBeatmap)b.Item).Beatmap != songSelect.Carousel.SelectedBeatmap));
+ .First(b => ((CarouselBeatmap)b.Item).BeatmapInfo != songSelect.Carousel.SelectedBeatmapInfo));
InputManager.Click(MouseButton.Left);
@@ -172,7 +172,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select next and enter", () =>
{
InputManager.MoveMouseTo(songSelect.Carousel.ChildrenOfType()
- .First(b => ((CarouselBeatmap)b.Item).Beatmap != songSelect.Carousel.SelectedBeatmap));
+ .First(b => ((CarouselBeatmap)b.Item).BeatmapInfo != songSelect.Carousel.SelectedBeatmapInfo));
InputManager.PressButton(MouseButton.Left);
@@ -312,7 +312,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
createSongSelect();
addRulesetImportStep(2);
- AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null);
+ AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmapInfo == null);
}
[Test]
@@ -322,13 +322,13 @@ namespace osu.Game.Tests.Visual.SongSelect
changeRuleset(2);
addRulesetImportStep(2);
addRulesetImportStep(1);
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2);
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.RulesetID == 2);
changeRuleset(1);
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 1);
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.RulesetID == 1);
changeRuleset(0);
- AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null);
+ AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmapInfo == null);
}
[Test]
@@ -338,7 +338,7 @@ namespace osu.Game.Tests.Visual.SongSelect
changeRuleset(2);
addRulesetImportStep(2);
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2);
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.RulesetID == 2);
addRulesetImportStep(0);
addRulesetImportStep(0);
@@ -355,7 +355,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Beatmap.Value = manager.GetWorkingBeatmap(target);
});
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.Equals(target));
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(target));
// this is an important check, to make sure updateComponentFromBeatmap() was actually run
AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.Equals(target));
@@ -368,7 +368,7 @@ namespace osu.Game.Tests.Visual.SongSelect
changeRuleset(2);
addRulesetImportStep(2);
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2);
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.RulesetID == 2);
addRulesetImportStep(0);
addRulesetImportStep(0);
@@ -385,7 +385,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == 0);
});
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.Equals(target));
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(target));
AddUntilStep("has correct ruleset", () => Ruleset.Value.ID == 0);
@@ -444,7 +444,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
createSongSelect();
addManyTestMaps();
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null);
bool startRequested = false;
@@ -473,13 +473,13 @@ namespace osu.Game.Tests.Visual.SongSelect
// used for filter check below
AddStep("allow convert display", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true));
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null);
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nonono");
AddUntilStep("dummy selected", () => Beatmap.Value is DummyWorkingBeatmap);
- AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmap == null);
+ AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmapInfo == null);
BeatmapInfo target = null;
@@ -494,7 +494,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Beatmap.Value = manager.GetWorkingBeatmap(target);
});
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null);
AddAssert("selected only shows expected ruleset (plus converts)", () =>
{
@@ -502,16 +502,16 @@ namespace osu.Game.Tests.Visual.SongSelect
// special case for converts checked here.
return selectedPanel.ChildrenOfType().All(i =>
- i.IsFiltered || i.Item.Beatmap.Ruleset.ID == targetRuleset || i.Item.Beatmap.Ruleset.ID == 0);
+ i.IsFiltered || i.Item.BeatmapInfo.Ruleset.ID == targetRuleset || i.Item.BeatmapInfo.Ruleset.ID == 0);
});
- AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID);
+ AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineBeatmapID == target.OnlineBeatmapID);
AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
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.SelectedBeatmap.OnlineBeatmapID == target.OnlineBeatmapID);
+ AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
}
[Test]
@@ -522,13 +522,13 @@ namespace osu.Game.Tests.Visual.SongSelect
changeRuleset(0);
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null);
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nonono");
AddUntilStep("dummy selected", () => Beatmap.Value is DummyWorkingBeatmap);
- AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmap == null);
+ AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmapInfo == null);
BeatmapInfo target = null;
@@ -540,15 +540,15 @@ namespace osu.Game.Tests.Visual.SongSelect
Beatmap.Value = manager.GetWorkingBeatmap(target);
});
- AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
+ AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null);
- AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID);
+ AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineBeatmapID == target.OnlineBeatmapID);
AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nononoo");
AddUntilStep("game lost selection", () => Beatmap.Value is DummyWorkingBeatmap);
- AddAssert("carousel lost selection", () => songSelect.Carousel.SelectedBeatmap == null);
+ AddAssert("carousel lost selection", () => songSelect.Carousel.SelectedBeatmapInfo == null);
}
[Test]
@@ -581,9 +581,9 @@ namespace osu.Game.Tests.Visual.SongSelect
createSongSelect();
addRulesetImportStep(0);
AddStep("Move to last difficulty", () => songSelect.Carousel.SelectBeatmap(songSelect.Carousel.BeatmapSets.First().Beatmaps.Last()));
- AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID);
+ AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmapInfo.ID);
AddStep("Hide first beatmap", () => manager.Hide(songSelect.Carousel.SelectedBeatmapSet.Beatmaps.First()));
- AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID);
+ AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmapInfo.ID == previousID);
}
[Test]
@@ -641,7 +641,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Click(MouseButton.Left);
});
- AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmap == filteredBeatmap);
+ AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmapInfo == filteredBeatmap);
}
[Test]
@@ -714,10 +714,11 @@ namespace osu.Game.Tests.Visual.SongSelect
});
FilterableDifficultyIcon difficultyIcon = null;
- AddStep("Find an icon for different ruleset", () =>
+ AddUntilStep("Find an icon for different ruleset", () =>
{
difficultyIcon = set.ChildrenOfType()
- .First(icon => icon.Item.Beatmap.Ruleset.ID == 3);
+ .FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3);
+ return difficultyIcon != null;
});
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
@@ -735,7 +736,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
- AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmap.BeatmapSet.ID == previousSetID);
+ AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmapInfo.BeatmapSet.ID == previousSetID);
AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.ID == 3);
}
@@ -751,7 +752,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import huge difficulty count map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
- imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result;
+ imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result.Value;
});
AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First()));
@@ -767,7 +768,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("Find group icon for different ruleset", () =>
{
groupIcon = set.ChildrenOfType()
- .First(icon => icon.Items.First().Beatmap.Ruleset.ID == 3);
+ .First(icon => icon.Items.First().BeatmapInfo.Ruleset.ID == 3);
});
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
@@ -781,7 +782,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
- AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.Equals(groupIcon.Items.First().Beatmap));
+ AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.Equals(groupIcon.Items.First().BeatmapInfo));
}
[Test]
@@ -805,7 +806,7 @@ namespace osu.Game.Tests.Visual.SongSelect
songSelect.PresentScore(new ScoreInfo
{
User = new User { Username = "woo" },
- Beatmap = getPresentBeatmap(),
+ BeatmapInfo = getPresentBeatmap(),
Ruleset = getPresentBeatmap().Ruleset
});
});
@@ -837,7 +838,7 @@ namespace osu.Game.Tests.Visual.SongSelect
songSelect.PresentScore(new ScoreInfo
{
User = new User { Username = "woo" },
- Beatmap = getPresentBeatmap(),
+ BeatmapInfo = getPresentBeatmap(),
Ruleset = getPresentBeatmap().Ruleset
});
});
@@ -856,7 +857,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info);
- private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmap);
+ private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmapInfo);
private int getDifficultyIconIndex(DrawableCarouselBeatmapSet set, FilterableDifficultyIcon icon)
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
index 2e30ed9827..189b143a35 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs
@@ -37,7 +37,8 @@ namespace osu.Game.Tests.Visual.UserInterface
private ScoreManager scoreManager;
private readonly List importedScores = new List();
- private BeatmapInfo beatmap;
+
+ private BeatmapInfo beatmapInfo;
[Cached]
private readonly DialogOverlay dialogOverlay;
@@ -55,7 +56,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = BeatmapLeaderboardScope.Local,
- Beatmap = new BeatmapInfo
+ BeatmapInfo = new BeatmapInfo
{
ID = 1,
Metadata = new BeatmapMetadata
@@ -84,15 +85,15 @@ namespace osu.Game.Tests.Visual.UserInterface
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default));
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory, Scheduler));
- beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0];
+ beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Value.Beatmaps[0];
for (int i = 0; i < 50; i++)
{
var score = new ScoreInfo
{
OnlineScoreID = i,
- Beatmap = beatmap,
- BeatmapInfoID = beatmap.ID,
+ BeatmapInfo = beatmapInfo,
+ BeatmapInfoID = beatmapInfo.ID,
Accuracy = RNG.NextDouble(),
TotalScore = RNG.Next(1, 1000000),
MaxCombo = RNG.Next(1, 1000),
@@ -100,7 +101,7 @@ namespace osu.Game.Tests.Visual.UserInterface
User = new User { Username = "TestUser" },
};
- importedScores.Add(scoreManager.Import(score).Result);
+ importedScores.Add(scoreManager.Import(score).Result.Value);
}
return dependencies;
@@ -115,7 +116,7 @@ namespace osu.Game.Tests.Visual.UserInterface
leaderboard.Scores = null;
leaderboard.FinishTransforms(true); // After setting scores, we may be waiting for transforms to expire drawables
- leaderboard.Beatmap = beatmap;
+ leaderboard.BeatmapInfo = beatmapInfo;
leaderboard.RefreshScores(); // Required in the case that the beatmap hasn't changed
});
@@ -161,6 +162,8 @@ namespace osu.Game.Tests.Visual.UserInterface
InputManager.Click(MouseButton.Left);
});
+ AddUntilStep("wait for fetch", () => leaderboard.Scores != null);
+
AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scoreBeingDeleted.OnlineScoreID));
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuFont.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuFont.cs
new file mode 100644
index 0000000000..eedafce271
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuFont.cs
@@ -0,0 +1,77 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneOsuFont : OsuTestScene
+ {
+ private OsuSpriteText spriteText;
+
+ private readonly BindableBool useAlternates = new BindableBool();
+ private readonly Bindable weight = new Bindable(FontWeight.Regular);
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Child = spriteText = new OsuSpriteText
+ {
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ AllowMultiline = true,
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ useAlternates.BindValueChanged(_ => updateFont());
+ weight.BindValueChanged(_ => updateFont(), true);
+ }
+
+ private void updateFont()
+ {
+ FontUsage usage = useAlternates.Value ? OsuFont.TorusAlternate : OsuFont.Torus;
+ spriteText.Font = usage.With(size: 40, weight: weight.Value);
+ }
+
+ [Test]
+ public void TestTorusAlternates()
+ {
+ AddStep("set all ASCII letters", () => spriteText.Text = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ
+abcdefghijklmnopqrstuvwxyz");
+ AddStep("set all alternates", () => spriteText.Text = @"A Á Ă Â Ä À Ā Ą Å Ã
+Æ B D Ð Ď Đ E É Ě Ê
+Ë Ė È Ē Ę F G Ğ Ģ Ġ
+H I Í Î Ï İ Ì Ī Į K
+Ķ O Œ P Þ Q R Ŕ Ř Ŗ
+T Ŧ Ť Ţ Ț V W Ẃ Ŵ Ẅ
+Ẁ X Y Ý Ŷ Ÿ Ỳ a á ă
+â ä à ā ą å ã æ b d
+ď đ e é ě ê ë ė è ē
+ę f g ğ ģ ġ k ķ m n
+ń ň ņ ŋ ñ o œ p þ q
+t ŧ ť ţ ț u ú û ü ù
+ű ū ų ů w ẃ ŵ ẅ ẁ x
+y ý ŷ ÿ ỳ");
+
+ AddToggleStep("toggle alternates", alternates => useAlternates.Value = alternates);
+
+ addSetWeightStep(FontWeight.Light);
+ addSetWeightStep(FontWeight.Regular);
+ addSetWeightStep(FontWeight.SemiBold);
+ addSetWeightStep(FontWeight.Bold);
+
+ void addSetWeightStep(FontWeight newWeight) => AddStep($"set weight {newWeight}", () => weight.Value = newWeight);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
index 198cc70e01..74cd675a05 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs
@@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.UserInterface
var req = new GetBeatmapSetRequest(1);
api.Queue(req);
- AddUntilStep("wait for api response", () => req.Result != null);
+ AddUntilStep("wait for api response", () => req.Response != null);
TestUpdateableBeatmapBackgroundSprite background = null;
@@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Child = background = new TestUpdateableBeatmapBackgroundSprite
{
RelativeSizeAxes = Axes.Both,
- Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Result?.ToBeatmapSet(rulesets) } }
+ Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Response?.ToBeatmapSet(rulesets) } }
};
});
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 696f930467..cd56cb51ae 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -4,6 +4,7 @@
+
diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs
index bc32a12ab7..f9c553cb3f 100644
--- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs
+++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Tournament.Tests.Components
private void success(APIBeatmap apiBeatmap)
{
- var beatmap = apiBeatmap.ToBeatmap(rulesets);
+ var beatmap = apiBeatmap.ToBeatmapInfo(rulesets);
Add(new TournamentBeatmapPanel(beatmap)
{
Anchor = Anchor.Centre,
diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs
index 47e7ed9b61..27eb55a9fb 100644
--- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs
+++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Tournament.Tests.Components
private FillFlowContainer fillFlow;
- private BeatmapInfo beatmap;
+ private BeatmapInfo beatmapInfo;
[BackgroundDependencyLoader]
private void load()
@@ -44,12 +44,12 @@ namespace osu.Game.Tournament.Tests.Components
private void success(APIBeatmap apiBeatmap)
{
- beatmap = apiBeatmap.ToBeatmap(rulesets);
+ beatmapInfo = apiBeatmap.ToBeatmapInfo(rulesets);
var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().AllMods;
foreach (var mod in mods)
{
- fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym)
+ fillFlow.Add(new TournamentBeatmapPanel(beatmapInfo, mod.Acronym)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs
index 6080f7b636..13888699ef 100644
--- a/osu.Game.Tournament/Components/SongBar.cs
+++ b/osu.Game.Tournament/Components/SongBar.cs
@@ -21,22 +21,22 @@ namespace osu.Game.Tournament.Components
{
public class SongBar : CompositeDrawable
{
- private BeatmapInfo beatmap;
+ private BeatmapInfo beatmapInfo;
public const float HEIGHT = 145 / 2f;
[Resolved]
private IBindable ruleset { get; set; }
- public BeatmapInfo Beatmap
+ public BeatmapInfo BeatmapInfo
{
- get => beatmap;
+ get => beatmapInfo;
set
{
- if (beatmap == value)
+ if (beatmapInfo == value)
return;
- beatmap = value;
+ beatmapInfo = value;
update();
}
}
@@ -95,18 +95,18 @@ namespace osu.Game.Tournament.Components
private void update()
{
- if (beatmap == null)
+ if (beatmapInfo == null)
{
flow.Clear();
return;
}
- var bpm = beatmap.BeatmapSet.OnlineInfo.BPM;
- var length = beatmap.Length;
+ var bpm = beatmapInfo.BeatmapSet.OnlineInfo.BPM;
+ var length = beatmapInfo.Length;
string hardRockExtra = "";
string srExtra = "";
- var ar = beatmap.BaseDifficulty.ApproachRate;
+ var ar = beatmapInfo.BaseDifficulty.ApproachRate;
if ((mods & LegacyMods.HardRock) > 0)
{
@@ -117,7 +117,7 @@ namespace osu.Game.Tournament.Components
if ((mods & LegacyMods.DoubleTime) > 0)
{
// temporary local calculation (taken from OsuDifficultyCalculator)
- double preempt = (int)BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / 1.5;
+ double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(ar, 1800, 1200, 450) / 1.5;
ar = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5);
bpm *= 1.5f;
@@ -132,9 +132,9 @@ namespace osu.Game.Tournament.Components
default:
stats = new (string heading, string content)[]
{
- ("CS", $"{beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"),
+ ("CS", $"{beatmapInfo.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"),
("AR", $"{ar:0.#}{hardRockExtra}"),
- ("OD", $"{beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"),
+ ("OD", $"{beatmapInfo.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"),
};
break;
@@ -142,15 +142,15 @@ namespace osu.Game.Tournament.Components
case 3:
stats = new (string heading, string content)[]
{
- ("OD", $"{beatmap.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"),
- ("HP", $"{beatmap.BaseDifficulty.DrainRate:0.#}{hardRockExtra}")
+ ("OD", $"{beatmapInfo.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"),
+ ("HP", $"{beatmapInfo.BaseDifficulty.DrainRate:0.#}{hardRockExtra}")
};
break;
case 2:
stats = new (string heading, string content)[]
{
- ("CS", $"{beatmap.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"),
+ ("CS", $"{beatmapInfo.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"),
("AR", $"{ar:0.#}"),
};
break;
@@ -186,7 +186,7 @@ namespace osu.Game.Tournament.Components
Children = new Drawable[]
{
new DiffPiece(stats),
- new DiffPiece(("Star Rating", $"{beatmap.StarDifficulty:0.#}{srExtra}"))
+ new DiffPiece(("Star Rating", $"{beatmapInfo.StarDifficulty:0.#}{srExtra}"))
}
},
new FillFlowContainer
@@ -229,7 +229,7 @@ namespace osu.Game.Tournament.Components
}
}
},
- new TournamentBeatmapPanel(beatmap)
+ new TournamentBeatmapPanel(beatmapInfo)
{
RelativeSizeAxes = Axes.X,
Width = 0.5f,
diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs
index e6d73c6e83..0e5a66e7fe 100644
--- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs
+++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Components
{
public class TournamentBeatmapPanel : CompositeDrawable
{
- public readonly BeatmapInfo Beatmap;
+ public readonly BeatmapInfo BeatmapInfo;
private readonly string mod;
private const float horizontal_padding = 10;
@@ -32,11 +32,11 @@ namespace osu.Game.Tournament.Components
private readonly Bindable currentMatch = new Bindable();
private Box flash;
- public TournamentBeatmapPanel(BeatmapInfo beatmap, string mod = null)
+ public TournamentBeatmapPanel(BeatmapInfo beatmapInfo, string mod = null)
{
- if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
+ if (beatmapInfo == null) throw new ArgumentNullException(nameof(beatmapInfo));
- Beatmap = beatmap;
+ BeatmapInfo = beatmapInfo;
this.mod = mod;
Width = 400;
Height = HEIGHT;
@@ -61,7 +61,7 @@ namespace osu.Game.Tournament.Components
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.5f),
- BeatmapSet = Beatmap.BeatmapSet,
+ BeatmapSet = BeatmapInfo.BeatmapSet,
},
new FillFlowContainer
{
@@ -75,8 +75,8 @@ namespace osu.Game.Tournament.Components
new TournamentSpriteText
{
Text = new RomanisableString(
- $"{Beatmap.Metadata.ArtistUnicode ?? Beatmap.Metadata.Artist} - {Beatmap.Metadata.TitleUnicode ?? Beatmap.Metadata.Title}",
- $"{Beatmap.Metadata.Artist} - {Beatmap.Metadata.Title}"),
+ $"{BeatmapInfo.Metadata.ArtistUnicode ?? BeatmapInfo.Metadata.Artist} - {BeatmapInfo.Metadata.TitleUnicode ?? BeatmapInfo.Metadata.Title}",
+ $"{BeatmapInfo.Metadata.Artist} - {BeatmapInfo.Metadata.Title}"),
Font = OsuFont.Torus.With(weight: FontWeight.Bold),
},
new FillFlowContainer
@@ -93,7 +93,7 @@ namespace osu.Game.Tournament.Components
},
new TournamentSpriteText
{
- Text = Beatmap.Metadata.AuthorString,
+ Text = BeatmapInfo.Metadata.AuthorString,
Padding = new MarginPadding { Right = 20 },
Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
},
@@ -105,7 +105,7 @@ namespace osu.Game.Tournament.Components
},
new TournamentSpriteText
{
- Text = Beatmap.Version,
+ Text = BeatmapInfo.Version,
Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
},
}
@@ -149,7 +149,7 @@ namespace osu.Game.Tournament.Components
private void updateState()
{
- var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap.OnlineBeatmapID);
+ var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == BeatmapInfo.OnlineBeatmapID);
bool doFlash = found != choice;
choice = found;
diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs
index f538d4a7d9..7010a30eb7 100644
--- a/osu.Game.Tournament/IPC/FileBasedIPC.cs
+++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs
@@ -94,7 +94,7 @@ namespace osu.Game.Tournament.IPC
else
{
beatmapLookupRequest = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId });
- beatmapLookupRequest.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets);
+ beatmapLookupRequest.Success += b => Beatmap.Value = b.ToBeatmapInfo(Rulesets);
API.Queue(beatmapLookupRequest);
}
}
diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs
index 50498304ca..b94b164116 100644
--- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs
+++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Tournament.Screens
private void beatmapChanged(ValueChangedEvent beatmap)
{
SongBar.FadeInFromZero(300, Easing.OutQuint);
- SongBar.Beatmap = beatmap.NewValue;
+ SongBar.BeatmapInfo = beatmap.NewValue;
}
}
}
diff --git a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs
index 27ad6650d1..6e4fc8fe1a 100644
--- a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs
+++ b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs
@@ -238,7 +238,7 @@ namespace osu.Game.Tournament.Screens.Editors
req.Success += res =>
{
- Model.BeatmapInfo = res.ToBeatmap(rulesets);
+ Model.BeatmapInfo = res.ToBeatmapInfo(rulesets);
updatePanel();
};
diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs
index 6418bf97da..b64a3993e6 100644
--- a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs
+++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs
@@ -246,7 +246,7 @@ namespace osu.Game.Tournament.Screens.Editors
req.Success += res =>
{
- Model.BeatmapInfo = res.ToBeatmap(rulesets);
+ Model.BeatmapInfo = res.ToBeatmapInfo(rulesets);
updatePanel();
};
diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
index d4292c5492..1e3c550323 100644
--- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
+++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs
@@ -147,11 +147,11 @@ namespace osu.Game.Tournament.Screens.MapPool
if (map != null)
{
- if (e.Button == MouseButton.Left && map.Beatmap.OnlineBeatmapID != null)
- addForBeatmap(map.Beatmap.OnlineBeatmapID.Value);
+ if (e.Button == MouseButton.Left && map.BeatmapInfo.OnlineBeatmapID != null)
+ addForBeatmap(map.BeatmapInfo.OnlineBeatmapID.Value);
else
{
- var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineBeatmapID);
+ var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.BeatmapInfo.OnlineBeatmapID);
if (existing != null)
{
diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs
index 531da00faf..bdf7269c83 100644
--- a/osu.Game.Tournament/TournamentGameBase.cs
+++ b/osu.Game.Tournament/TournamentGameBase.cs
@@ -182,7 +182,7 @@ namespace osu.Game.Tournament
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
API.Perform(req);
- b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore);
+ b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore);
addedInfo = true;
}
@@ -203,7 +203,7 @@ namespace osu.Game.Tournament
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
req.Perform(API);
- b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore);
+ b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore);
addedInfo = true;
}
diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs
index 1844b193f2..2bb0787b4c 100644
--- a/osu.Game/Beatmaps/BeatmapDifficulty.cs
+++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs
@@ -5,7 +5,7 @@ using osu.Game.Database;
namespace osu.Game.Beatmaps
{
- public class BeatmapDifficulty : IHasPrimaryKey
+ public class BeatmapDifficulty : IHasPrimaryKey, IBeatmapDifficultyInfo
{
///
/// The default value used for all difficulty settings except and .
@@ -20,6 +20,15 @@ namespace osu.Game.Beatmaps
private float? approachRate;
+ public BeatmapDifficulty()
+ {
+ }
+
+ public BeatmapDifficulty(IBeatmapDifficultyInfo source)
+ {
+ CopyFrom(source);
+ }
+
public float ApproachRate
{
get => approachRate ?? OverallDifficulty;
@@ -39,6 +48,17 @@ namespace osu.Game.Beatmaps
return diff;
}
+ public void CopyFrom(IBeatmapDifficultyInfo difficulty)
+ {
+ ApproachRate = difficulty.ApproachRate;
+ DrainRate = difficulty.DrainRate;
+ CircleSize = difficulty.CircleSize;
+ OverallDifficulty = difficulty.OverallDifficulty;
+
+ SliderMultiplier = difficulty.SliderMultiplier;
+ SliderTickRate = difficulty.SliderTickRate;
+ }
+
public void CopyTo(BeatmapDifficulty difficulty)
{
difficulty.ApproachRate = ApproachRate;
@@ -49,47 +69,5 @@ namespace osu.Game.Beatmaps
difficulty.SliderMultiplier = SliderMultiplier;
difficulty.SliderTickRate = SliderTickRate;
}
-
- ///
- /// Maps a difficulty value [0, 10] to a two-piece linear range of values.
- ///
- /// The difficulty value to be mapped.
- /// Minimum of the resulting range which will be achieved by a difficulty value of 0.
- /// Midpoint of the resulting range which will be achieved by a difficulty value of 5.
- /// Maximum of the resulting range which will be achieved by a difficulty value of 10.
- /// Value to which the difficulty value maps in the specified range.
- public static double DifficultyRange(double difficulty, double min, double mid, double max)
- {
- if (difficulty > 5)
- return mid + (max - mid) * (difficulty - 5) / 5;
- if (difficulty < 5)
- return mid - (mid - min) * (5 - difficulty) / 5;
-
- return mid;
- }
-
- ///
- /// Maps a difficulty value [0, 10] to a two-piece linear range of values.
- ///
- /// The difficulty value to be mapped.
- /// The values that define the two linear ranges.
- ///
- /// -
- /// od0
- /// Minimum of the resulting range which will be achieved by a difficulty value of 0.
- ///
- /// -
- /// od5
- /// Midpoint of the resulting range which will be achieved by a difficulty value of 5.
- ///
- /// -
- /// od10
- /// Maximum of the resulting range which will be achieved by a difficulty value of 10.
- ///
- ///
- ///
- /// Value to which the difficulty value maps in the specified range.
- public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range)
- => DifficultyRange(difficulty, range.od0, range.od5, range.od10);
}
}
diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
index 0aa6a6dd0b..3777365088 100644
--- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
+++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
@@ -17,6 +17,7 @@ using osu.Framework.Utils;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
@@ -147,6 +148,14 @@ namespace osu.Game.Beatmaps
}, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
}
+ public Task> GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default)
+ {
+ return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods),
+ token,
+ TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously,
+ updateScheduler);
+ }
+
///
/// Retrieves the that describes a star rating.
///
@@ -242,7 +251,7 @@ namespace osu.Game.Beatmaps
{
// GetDifficultyAsync will fall back to existing data from BeatmapInfo if not locally available
// (contrary to GetAsync)
- GetDifficultyAsync(bindable.Beatmap, rulesetInfo, mods, cancellationToken)
+ GetDifficultyAsync(bindable.BeatmapInfo, rulesetInfo, mods, cancellationToken)
.ContinueWith(t =>
{
// We're on a threadpool thread, but we should exit back to the update thread so consumers can safely handle value-changed events.
@@ -262,7 +271,7 @@ namespace osu.Game.Beatmaps
private StarDifficulty computeDifficulty(in DifficultyCacheLookup key)
{
// In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset.
- var beatmapInfo = key.Beatmap;
+ var beatmapInfo = key.BeatmapInfo;
var rulesetInfo = key.Ruleset;
try
@@ -270,7 +279,7 @@ namespace osu.Game.Beatmaps
var ruleset = rulesetInfo.CreateInstance();
Debug.Assert(ruleset != null);
- var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(key.Beatmap));
+ var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(key.BeatmapInfo));
var attributes = calculator.Calculate(key.OrderedMods);
return new StarDifficulty(attributes);
@@ -300,21 +309,21 @@ namespace osu.Game.Beatmaps
public readonly struct DifficultyCacheLookup : IEquatable
{
- public readonly BeatmapInfo Beatmap;
+ public readonly BeatmapInfo BeatmapInfo;
public readonly RulesetInfo Ruleset;
public readonly Mod[] OrderedMods;
- public DifficultyCacheLookup([NotNull] BeatmapInfo beatmap, [CanBeNull] RulesetInfo ruleset, IEnumerable mods)
+ public DifficultyCacheLookup([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo ruleset, IEnumerable mods)
{
- Beatmap = beatmap;
+ BeatmapInfo = beatmapInfo;
// In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset.
- Ruleset = ruleset ?? Beatmap.Ruleset;
+ Ruleset = ruleset ?? BeatmapInfo.Ruleset;
OrderedMods = mods?.OrderBy(m => m.Acronym).Select(mod => mod.DeepClone()).ToArray() ?? Array.Empty();
}
public bool Equals(DifficultyCacheLookup other)
- => Beatmap.ID == other.Beatmap.ID
+ => BeatmapInfo.ID == other.BeatmapInfo.ID
&& Ruleset.ID == other.Ruleset.ID
&& OrderedMods.SequenceEqual(other.OrderedMods);
@@ -322,7 +331,7 @@ namespace osu.Game.Beatmaps
{
var hashCode = new HashCode();
- hashCode.Add(Beatmap.ID);
+ hashCode.Add(BeatmapInfo.ID);
hashCode.Add(Ruleset.ID);
foreach (var mod in OrderedMods)
@@ -334,12 +343,12 @@ namespace osu.Game.Beatmaps
private class BindableStarDifficulty : Bindable
{
- public readonly BeatmapInfo Beatmap;
+ public readonly BeatmapInfo BeatmapInfo;
public readonly CancellationToken CancellationToken;
- public BindableStarDifficulty(BeatmapInfo beatmap, CancellationToken cancellationToken)
+ public BindableStarDifficulty(BeatmapInfo beatmapInfo, CancellationToken cancellationToken)
{
- Beatmap = beatmap;
+ BeatmapInfo = beatmapInfo;
CancellationToken = cancellationToken;
}
}
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 8cb5da8083..ac5b5d7a8a 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -7,7 +7,6 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Newtonsoft.Json;
-using osu.Framework.Localisation;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Rulesets;
@@ -17,7 +16,7 @@ namespace osu.Game.Beatmaps
{
[ExcludeFromDynamicCompile]
[Serializable]
- public class BeatmapInfo : IEquatable, IHasPrimaryKey
+ public class BeatmapInfo : IEquatable, IHasPrimaryKey, IBeatmapInfo
{
public int ID { get; set; }
@@ -152,18 +151,7 @@ namespace osu.Game.Beatmaps
[JsonIgnore]
public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(StarDifficulty);
- public string[] SearchableTerms => new[]
- {
- Version
- }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
-
- public override string ToString() => $"{Metadata ?? BeatmapSet?.Metadata} {versionString}".Trim();
-
- public RomanisableString ToRomanisableString()
- {
- var metadata = (Metadata ?? BeatmapSet?.Metadata)?.ToRomanisableString() ?? new RomanisableString(null, null);
- return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim());
- }
+ public override string ToString() => this.GetDisplayTitle();
public bool Equals(BeatmapInfo other)
{
@@ -187,5 +175,22 @@ namespace osu.Game.Beatmaps
/// Returns a shallow-clone of this .
///
public BeatmapInfo Clone() => (BeatmapInfo)MemberwiseClone();
+
+ #region Implementation of IHasOnlineID
+
+ public int? OnlineID => OnlineBeatmapID;
+
+ #endregion
+
+ #region Implementation of IBeatmapInfo
+
+ string IBeatmapInfo.DifficultyName => Version;
+ IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata;
+ IBeatmapDifficultyInfo IBeatmapInfo.Difficulty => BaseDifficulty;
+ IBeatmapSetInfo IBeatmapInfo.BeatmapSet => BeatmapSet;
+ IRulesetInfo IBeatmapInfo.Ruleset => Ruleset;
+ double IBeatmapInfo.StarRating => StarDifficulty;
+
+ #endregion
}
}
diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs
new file mode 100644
index 0000000000..eba19ac1a1
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs
@@ -0,0 +1,38 @@
+// 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 osu.Framework.Localisation;
+
+namespace osu.Game.Beatmaps
+{
+ public static class BeatmapInfoExtensions
+ {
+ ///
+ /// A user-presentable display title representing this beatmap.
+ ///
+ public static string GetDisplayTitle(this IBeatmapInfo beatmapInfo) => $"{getClosestMetadata(beatmapInfo)} {getVersionString(beatmapInfo)}".Trim();
+
+ ///
+ /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields.
+ ///
+ public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo)
+ {
+ var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable();
+ var versionString = getVersionString(beatmapInfo);
+
+ return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim());
+ }
+
+ public static string[] GetSearchableTerms(this IBeatmapInfo beatmapInfo) => new[]
+ {
+ beatmapInfo.DifficultyName
+ }.Concat(getClosestMetadata(beatmapInfo).GetSearchableTerms()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
+
+ private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]";
+
+ // temporary helper methods until we figure which metadata should be where.
+ private static IBeatmapMetadataInfo getClosestMetadata(IBeatmapInfo beatmapInfo) =>
+ beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet?.Metadata ?? new BeatmapMetadata();
+ }
+}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index bd85017d58..240db22c00 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -6,111 +6,68 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore;
using osu.Framework.Audio;
-using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
-using osu.Framework.Extensions;
-using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
-using osu.Framework.Lists;
-using osu.Framework.Logging;
using osu.Framework.Platform;
-using osu.Framework.Statistics;
using osu.Framework.Testing;
-using osu.Game.Beatmaps.Formats;
using osu.Game.Database;
using osu.Game.IO;
using osu.Game.IO.Archives;
using osu.Game.Online.API;
-using osu.Game.Online.API.Requests;
+using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
-using osu.Game.Rulesets.Objects;
using osu.Game.Skinning;
using osu.Game.Users;
-using Decoder = osu.Game.Beatmaps.Formats.Decoder;
namespace osu.Game.Beatmaps
{
///
- /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps.
+ /// Handles general operations related to global beatmap management.
///
[ExcludeFromDynamicCompile]
- public partial class BeatmapManager : DownloadableArchiveModelManager, IDisposable, IBeatmapResourceProvider
+ public class BeatmapManager : IModelDownloader, IModelManager, IModelFileManager, ICanAcceptFiles, IWorkingBeatmapCache, IDisposable
{
- ///
- /// Fired when a single difficulty has been hidden.
- ///
- public IBindable> BeatmapHidden => beatmapHidden;
+ private readonly BeatmapModelManager beatmapModelManager;
+ private readonly BeatmapModelDownloader beatmapModelDownloader;
- private readonly Bindable> beatmapHidden = new Bindable>();
-
- ///
- /// Fired when a single difficulty has been restored.
- ///
- public IBindable> BeatmapRestored => beatmapRestored;
-
- private readonly Bindable> beatmapRestored = new Bindable>();
-
- ///
- /// A default representation of a WorkingBeatmap to use when no beatmap is available.
- ///
- public readonly WorkingBeatmap DefaultBeatmap;
-
- public override IEnumerable HandledExtensions => new[] { ".osz" };
-
- protected override string[] HashableFileTypes => new[] { ".osu" };
-
- protected override string ImportFromStablePath => ".";
-
- protected override Storage PrepareStableStorage(StableStorage stableStorage) => stableStorage.GetSongStorage();
-
- private readonly RulesetStore rulesets;
- private readonly BeatmapStore beatmaps;
- private readonly AudioManager audioManager;
- private readonly IResourceStore resources;
- private readonly LargeTextureStore largeTextureStore;
- private readonly ITrackStore trackStore;
-
- [CanBeNull]
- private readonly GameHost host;
-
- [CanBeNull]
- private readonly BeatmapOnlineLookupQueue onlineLookupQueue;
+ private readonly WorkingBeatmapCache workingBeatmapCache;
+ private readonly BeatmapOnlineLookupQueue onlineBeatmapLookupQueue;
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null,
WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false)
- : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host)
{
- this.rulesets = rulesets;
- this.audioManager = audioManager;
- this.resources = resources;
- this.host = host;
+ beatmapModelManager = CreateBeatmapModelManager(storage, contextFactory, rulesets, api, host);
+ beatmapModelDownloader = CreateBeatmapModelDownloader(beatmapModelManager, api, host);
+ workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, resources, new FileStore(contextFactory, storage).Store, defaultBeatmap, host);
- DefaultBeatmap = defaultBeatmap;
-
- beatmaps = (BeatmapStore)ModelStore;
- beatmaps.BeatmapHidden += b => beatmapHidden.Value = new WeakReference(b);
- beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference(b);
- beatmaps.ItemRemoved += removeWorkingCache;
- beatmaps.ItemUpdated += removeWorkingCache;
+ workingBeatmapCache.BeatmapManager = beatmapModelManager;
+ beatmapModelManager.WorkingBeatmapCache = workingBeatmapCache;
if (performOnlineLookups)
- onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
-
- largeTextureStore = new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store));
- trackStore = audioManager.GetTrackStore(Files.Store);
+ {
+ onlineBeatmapLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
+ beatmapModelManager.OnlineLookupQueue = onlineBeatmapLookupQueue;
+ }
}
- protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) =>
- new DownloadBeatmapSetRequest(set, minimiseDownloadSize);
+ protected virtual BeatmapModelDownloader CreateBeatmapModelDownloader(BeatmapModelManager modelManager, IAPIProvider api, GameHost host)
+ {
+ return new BeatmapModelDownloader(modelManager, api, host);
+ }
- protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz";
+ protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap defaultBeatmap, GameHost host) =>
+ new WorkingBeatmapCache(audioManager, resources, storage, defaultBeatmap, host);
+ protected virtual BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) =>
+ new BeatmapModelManager(storage, contextFactory, rulesets, host);
+
+ ///
+ /// Create a new .
+ ///
public WorkingBeatmap CreateNew(RulesetInfo ruleset, User user)
{
var metadata = new BeatmapMetadata
@@ -134,112 +91,22 @@ namespace osu.Game.Beatmaps
}
};
- var working = Import(set).Result;
- return GetWorkingBeatmap(working.Beatmaps.First());
+ var imported = beatmapModelManager.Import(set).Result.Value;
+
+ return GetWorkingBeatmap(imported.Beatmaps.First());
}
- protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
- {
- if (archive != null)
- beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files);
-
- foreach (BeatmapInfo b in beatmapSet.Beatmaps)
- {
- // remove metadata from difficulties where it matches the set
- if (beatmapSet.Metadata.Equals(b.Metadata))
- b.Metadata = null;
-
- b.BeatmapSet = beatmapSet;
- }
-
- validateOnlineIds(beatmapSet);
-
- bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 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 (beatmapSet.OnlineBeatmapSetID != null)
- {
- beatmapSet.OnlineBeatmapSetID = null;
- LogForModel(beatmapSet, "Disassociating beatmap set ID due to loss of all beatmap IDs");
- }
- }
- }
-
- protected override void PreImport(BeatmapSetInfo beatmapSet)
- {
- if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null))
- 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)
- {
- var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID);
-
- if (existingOnlineId != null)
- {
- Delete(existingOnlineId);
-
- // in order to avoid a unique key constraint, immediately remove the online ID from the previous set.
- existingOnlineId.OnlineBeatmapSetID = null;
- foreach (var b in existingOnlineId.Beatmaps)
- b.OnlineBeatmapID = null;
-
- LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been deleted.");
- }
- }
- }
-
- private void validateOnlineIds(BeatmapSetInfo beatmapSet)
- {
- var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList();
-
- // ensure all IDs are unique
- if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1))
- {
- LogForModel(beatmapSet, "Found non-unique IDs, resetting...");
- resetIds();
- return;
- }
-
- // find any existing beatmaps in the database that have matching online ids
- var existingBeatmaps = QueryBeatmaps(b => beatmapIds.Contains(b.OnlineBeatmapID)).ToList();
-
- if (existingBeatmaps.Count > 0)
- {
- // reset the import ids (to force a re-fetch) *unless* they match the candidate CheckForExisting set.
- // we can ignore the case where the new ids are contained by the CheckForExisting set as it will either be used (import skipped) or deleted.
- var existing = CheckForExisting(beatmapSet);
-
- if (existing == null || existingBeatmaps.Any(b => !existing.Beatmaps.Contains(b)))
- {
- LogForModel(beatmapSet, "Found existing import with IDs already, resetting...");
- resetIds();
- }
- }
-
- void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null);
- }
-
- protected override bool CheckLocalAvailability(BeatmapSetInfo model, IQueryable items)
- => base.CheckLocalAvailability(model, items)
- || (model.OnlineBeatmapSetID != null && items.Any(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID));
+ #region Delegation to BeatmapModelManager (methods which previously existed locally).
///
- /// Delete a beatmap difficulty.
+ /// Fired when a single difficulty has been hidden.
///
- /// The beatmap difficulty to hide.
- public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap);
+ public IBindable> BeatmapHidden => beatmapModelManager.BeatmapHidden;
///
- /// Restore a beatmap difficulty.
+ /// Fired when a single difficulty has been restored.
///
- /// The beatmap difficulty to restore.
- public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
+ public IBindable> BeatmapRestored => beatmapModelManager.BeatmapRestored;
///
/// Saves an file against a given .
@@ -247,109 +114,13 @@ namespace osu.Game.Beatmaps
/// The to save the content against. The file referenced by will be replaced.
/// The content to write.
/// The beatmap content to write, null if to be omitted.
- public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null)
- {
- var setInfo = info.BeatmapSet;
-
- using (var stream = new MemoryStream())
- {
- using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
- new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw);
-
- stream.Seek(0, SeekOrigin.Begin);
-
- using (ContextFactory.GetForWrite())
- {
- var beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID);
- var metadata = beatmapInfo.Metadata ?? setInfo.Metadata;
-
- // grab the original file (or create a new one if not found).
- var fileInfo = setInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, beatmapInfo.Path, StringComparison.OrdinalIgnoreCase)) ?? new BeatmapSetFileInfo();
-
- // metadata may have changed; update the path with the standard format.
- beatmapInfo.Path = $"{metadata.Artist} - {metadata.Title} ({metadata.Author}) [{beatmapInfo.Version}].osu";
- beatmapInfo.MD5Hash = stream.ComputeMD5Hash();
-
- // update existing or populate new file's filename.
- fileInfo.Filename = beatmapInfo.Path;
-
- stream.Seek(0, SeekOrigin.Begin);
- ReplaceFile(setInfo, fileInfo, stream);
- }
- }
-
- removeWorkingCache(info);
- }
-
- private readonly WeakList workingCache = new WeakList();
-
- ///
- /// Retrieve a instance for the provided
- ///
- /// The beatmap to lookup.
- /// A instance correlating to the provided .
- public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo)
- {
- // if there are no files, presume the full beatmap info has not yet been fetched from the database.
- if (beatmapInfo?.BeatmapSet?.Files.Count == 0)
- {
- int lookupId = beatmapInfo.ID;
- beatmapInfo = QueryBeatmap(b => b.ID == lookupId);
- }
-
- if (beatmapInfo?.BeatmapSet == null)
- return DefaultBeatmap;
-
- lock (workingCache)
- {
- var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID);
- if (working != null)
- return working;
-
- beatmapInfo.Metadata ??= beatmapInfo.BeatmapSet.Metadata;
-
- workingCache.Add(working = new BeatmapManagerWorkingBeatmap(beatmapInfo, this));
-
- // best effort; may be higher than expected.
- GlobalStatistics.Get(nameof(Beatmaps), $"Cached {nameof(WorkingBeatmap)}s").Value = workingCache.Count();
-
- return working;
- }
- }
-
- ///
- /// Perform a lookup query on available s.
- ///
- /// The query.
- /// The first result for the provided query, or null if no results were found.
- public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().FirstOrDefault(query);
-
- protected override bool CanSkipImport(BeatmapSetInfo existing, BeatmapSetInfo import)
- {
- if (!base.CanSkipImport(existing, import))
- return false;
-
- return existing.Beatmaps.Any(b => b.OnlineBeatmapID != null);
- }
-
- protected override bool CanReuseExisting(BeatmapSetInfo existing, BeatmapSetInfo import)
- {
- 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);
-
- // force re-import if we are not in a sane state.
- return existing.OnlineBeatmapSetID == import.OnlineBeatmapSetID && existingIds.SequenceEqual(importIds);
- }
+ public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) => beatmapModelManager.Save(info, beatmapContent, beatmapSkin);
///
/// Returns a list of all usable s.
///
/// A list of available .
- public List GetAllUsableBeatmapSets(IncludedDetails includes = IncludedDetails.All, bool includeProtected = false) =>
- GetAllUsableBeatmapSetsEnumerable(includes, includeProtected).ToList();
+ public List GetAllUsableBeatmapSets(IncludedDetails includes = IncludedDetails.All, bool includeProtected = false) => beatmapModelManager.GetAllUsableBeatmapSets(includes, includeProtected);
///
/// Returns a list of all usable s. Note that files are not populated.
@@ -357,34 +128,7 @@ namespace osu.Game.Beatmaps
/// The level of detail to include in the returned objects.
/// Whether to include protected (system) beatmaps. These should not be included for gameplay playable use cases.
/// A list of available .
- public IEnumerable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes, bool includeProtected = false)
- {
- IQueryable queryable;
-
- switch (includes)
- {
- case IncludedDetails.Minimal:
- queryable = beatmaps.BeatmapSetsOverview;
- break;
-
- case IncludedDetails.AllButRuleset:
- queryable = beatmaps.BeatmapSetsWithoutRuleset;
- break;
-
- case IncludedDetails.AllButFiles:
- queryable = beatmaps.BeatmapSetsWithoutFiles;
- break;
-
- default:
- queryable = beatmaps.ConsumableItems;
- break;
- }
-
- // AsEnumerable used here to avoid applying the WHERE in sql. When done so, ef core 2.x uses an incorrect ORDER BY
- // clause which causes queries to take 5-10x longer.
- // TODO: remove if upgrading to EF core 3.x.
- return queryable.AsEnumerable().Where(s => !s.DeletePending && (includeProtected || !s.Protected));
- }
+ public IEnumerable GetAllUsableBeatmapSetsEnumerable(IncludedDetails includes, bool includeProtected = false) => beatmapModelManager.GetAllUsableBeatmapSetsEnumerable(includes, includeProtected);
///
/// Perform a lookup query on available s.
@@ -392,207 +136,207 @@ namespace osu.Game.Beatmaps
/// The query.
/// The level of detail to include in the returned objects.
/// Results from the provided query.
- public IEnumerable QueryBeatmapSets(Expression> query, IncludedDetails includes = IncludedDetails.All)
- {
- IQueryable queryable;
-
- switch (includes)
- {
- case IncludedDetails.Minimal:
- queryable = beatmaps.BeatmapSetsOverview;
- break;
-
- case IncludedDetails.AllButRuleset:
- queryable = beatmaps.BeatmapSetsWithoutRuleset;
- break;
-
- case IncludedDetails.AllButFiles:
- queryable = beatmaps.BeatmapSetsWithoutFiles;
- break;
-
- default:
- queryable = beatmaps.ConsumableItems;
- break;
- }
-
- return queryable.AsNoTracking().Where(query);
- }
+ public IEnumerable QueryBeatmapSets(Expression> query, IncludedDetails includes = IncludedDetails.All) => beatmapModelManager.QueryBeatmapSets(query, includes);
///
- /// Perform a lookup query on available s.
+ /// Perform a lookup query on available s.
///
/// The query.
/// The first result for the provided query, or null if no results were found.
- public BeatmapInfo QueryBeatmap(Expression> query) => beatmaps.Beatmaps.AsNoTracking().FirstOrDefault(query);
+ public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmapModelManager.QueryBeatmapSet(query);
///
/// Perform a lookup query on available s.
///
/// The query.
/// Results from the provided query.
- public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
+ public IQueryable QueryBeatmaps(Expression> query) => beatmapModelManager.QueryBeatmaps(query);
- protected override string HumanisedModelName => "beatmap";
+ ///
+ /// Perform a lookup query on available s.
+ ///
+ /// The query.
+ /// The first result for the provided query, or null if no results were found.
+ public BeatmapInfo QueryBeatmap(Expression> query) => beatmapModelManager.QueryBeatmap(query);
- protected override BeatmapSetInfo CreateModel(ArchiveReader reader)
+ ///
+ /// A default representation of a WorkingBeatmap to use when no beatmap is available.
+ ///
+ public WorkingBeatmap DefaultBeatmap => workingBeatmapCache.DefaultBeatmap;
+
+ ///
+ /// Fired when a notification should be presented to the user.
+ ///
+ public Action PostNotification
{
- // let's make sure there are actually .osu files to import.
- string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu", StringComparison.OrdinalIgnoreCase));
-
- if (string.IsNullOrEmpty(mapName))
+ set
{
- Logger.Log($"No beatmap files found in the beatmap archive ({reader.Name}).", LoggingTarget.Database);
- return null;
+ beatmapModelManager.PostNotification = value;
+ beatmapModelDownloader.PostNotification = value;
}
-
- Beatmap beatmap;
- using (var stream = new LineBufferedReader(reader.GetStream(mapName)))
- beatmap = Decoder.GetDecoder(stream).Decode(stream);
-
- return new BeatmapSetInfo
- {
- OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID,
- Beatmaps = new List(),
- Metadata = beatmap.Metadata,
- DateAdded = DateTimeOffset.UtcNow
- };
}
///
- /// Create all required s for the provided archive.
+ /// Fired when the user requests to view the resulting import.
///
- private List createBeatmapDifficulties(List files)
- {
- var beatmapInfos = new List();
+ public Action>> PresentImport { set => beatmapModelManager.PostImport = value; }
- foreach (var file in files.Where(f => f.Filename.EndsWith(".osu", StringComparison.OrdinalIgnoreCase)))
- {
- using (var raw = Files.Store.GetStream(file.FileInfo.StoragePath))
- using (var ms = new MemoryStream()) // we need a memory stream so we can seek
- using (var sr = new LineBufferedReader(ms))
- {
- raw.CopyTo(ms);
- ms.Position = 0;
+ ///
+ /// Delete a beatmap difficulty.
+ ///
+ /// The beatmap difficulty to hide.
+ public void Hide(BeatmapInfo beatmapInfo) => beatmapModelManager.Hide(beatmapInfo);
- var decoder = Decoder.GetDecoder(sr);
- IBeatmap beatmap = decoder.Decode(sr);
-
- string hash = ms.ComputeSHA2Hash();
-
- if (beatmapInfos.Any(b => b.Hash == hash))
- continue;
-
- beatmap.BeatmapInfo.Path = file.Filename;
- beatmap.BeatmapInfo.Hash = hash;
- beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
-
- var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
- beatmap.BeatmapInfo.Ruleset = ruleset;
-
- // TODO: this should be done in a better place once we actually need to dynamically update it.
- beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0;
- beatmap.BeatmapInfo.Length = calculateLength(beatmap);
- beatmap.BeatmapInfo.BPM = 60000 / beatmap.GetMostCommonBeatLength();
-
- beatmapInfos.Add(beatmap.BeatmapInfo);
- }
- }
-
- return beatmapInfos;
- }
-
- private double calculateLength(IBeatmap b)
- {
- if (!b.HitObjects.Any())
- return 0;
-
- var lastObject = b.HitObjects.Last();
-
- //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list).
- double endTime = lastObject.GetEndTime();
- double startTime = b.HitObjects.First().StartTime;
-
- return endTime - startTime;
- }
-
- private void removeWorkingCache(BeatmapSetInfo info)
- {
- if (info.Beatmaps == null) return;
-
- foreach (var b in info.Beatmaps)
- removeWorkingCache(b);
- }
-
- private void removeWorkingCache(BeatmapInfo info)
- {
- lock (workingCache)
- {
- var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID);
- if (working != null)
- workingCache.Remove(working);
- }
- }
-
- public void Dispose()
- {
- onlineLookupQueue?.Dispose();
- }
-
- #region IResourceStorageProvider
-
- TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore;
- ITrackStore IBeatmapResourceProvider.Tracks => trackStore;
- AudioManager IStorageResourceProvider.AudioManager => audioManager;
- IResourceStore IStorageResourceProvider.Files => Files.Store;
- IResourceStore IStorageResourceProvider.Resources => resources;
- IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host?.CreateTextureLoaderStore(underlyingStore);
+ ///
+ /// Restore a beatmap difficulty.
+ ///
+ /// The beatmap difficulty to restore.
+ public void Restore(BeatmapInfo beatmapInfo) => beatmapModelManager.Restore(beatmapInfo);
#endregion
- ///
- /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation.
- ///
- private class DummyConversionBeatmap : WorkingBeatmap
+ #region Implementation of IModelManager
+
+ public bool IsAvailableLocally(BeatmapSetInfo model)
{
- private readonly IBeatmap beatmap;
-
- public DummyConversionBeatmap(IBeatmap beatmap)
- : base(beatmap.BeatmapInfo, null)
- {
- this.beatmap = beatmap;
- }
-
- protected override IBeatmap GetBeatmap() => beatmap;
- protected override Texture GetBackground() => null;
- protected override Track GetBeatmapTrack() => null;
- protected internal override ISkin GetSkin() => null;
- public override Stream GetStream(string storagePath) => null;
+ return beatmapModelManager.IsAvailableLocally(model);
}
- }
- ///
- /// The level of detail to include in database results.
- ///
- public enum IncludedDetails
- {
- ///
- /// Only include beatmap difficulties and set level metadata.
- ///
- Minimal,
+ public IBindable> ItemUpdated => beatmapModelManager.ItemUpdated;
- ///
- /// Include all difficulties, rulesets, difficulty metadata but no files.
- ///
- AllButFiles,
+ public IBindable> ItemRemoved => beatmapModelManager.ItemRemoved;
- ///
- /// Include everything except ruleset. Used for cases where we aren't sure the ruleset is present but still want to consume the beatmap.
- ///
- AllButRuleset,
+ public Task ImportFromStableAsync(StableStorage stableStorage)
+ {
+ return beatmapModelManager.ImportFromStableAsync(stableStorage);
+ }
- ///
- /// Include everything.
- ///
- All
+ public void Export(BeatmapSetInfo item)
+ {
+ beatmapModelManager.Export(item);
+ }
+
+ public void ExportModelTo(BeatmapSetInfo model, Stream outputStream)
+ {
+ beatmapModelManager.ExportModelTo(model, outputStream);
+ }
+
+ public void Update(BeatmapSetInfo item)
+ {
+ beatmapModelManager.Update(item);
+ }
+
+ public bool Delete(BeatmapSetInfo item)
+ {
+ return beatmapModelManager.Delete(item);
+ }
+
+ public void Delete(List items, bool silent = false)
+ {
+ beatmapModelManager.Delete(items, silent);
+ }
+
+ public void Undelete(List items, bool silent = false)
+ {
+ beatmapModelManager.Undelete(items, silent);
+ }
+
+ public void Undelete(BeatmapSetInfo item)
+ {
+ beatmapModelManager.Undelete(item);
+ }
+
+ #endregion
+
+ #region Implementation of IModelDownloader
+
+ public IBindable>> DownloadBegan => beatmapModelDownloader.DownloadBegan;
+
+ public IBindable>> DownloadFailed => beatmapModelDownloader.DownloadFailed;
+
+ public bool Download(BeatmapSetInfo model, bool minimiseDownloadSize = false)
+ {
+ return beatmapModelDownloader.Download(model, minimiseDownloadSize);
+ }
+
+ public ArchiveDownloadRequest GetExistingDownload(BeatmapSetInfo model)
+ {
+ return beatmapModelDownloader.GetExistingDownload(model);
+ }
+
+ #endregion
+
+ #region Implementation of ICanAcceptFiles
+
+ public Task Import(params string[] paths)
+ {
+ return beatmapModelManager.Import(paths);
+ }
+
+ public Task Import(params ImportTask[] tasks)
+ {
+ return beatmapModelManager.Import(tasks);
+ }
+
+ public Task>> Import(ProgressNotification notification, params ImportTask[] tasks)
+ {
+ return beatmapModelManager.Import(notification, tasks);
+ }
+
+ public Task> Import(ImportTask task, bool lowPriority = false, CancellationToken cancellationToken = default)
+ {
+ return beatmapModelManager.Import(task, lowPriority, cancellationToken);
+ }
+
+ public Task> Import(ArchiveReader archive, bool lowPriority = false, CancellationToken cancellationToken = default)
+ {
+ return beatmapModelManager.Import(archive, lowPriority, cancellationToken);
+ }
+
+ public Task> Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default)
+ {
+ return beatmapModelManager.Import(item, archive, lowPriority, cancellationToken);
+ }
+
+ public IEnumerable HandledExtensions => beatmapModelManager.HandledExtensions;
+
+ #endregion
+
+ #region Implementation of IWorkingBeatmapCache
+
+ public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo importedBeatmap) => workingBeatmapCache.GetWorkingBeatmap(importedBeatmap);
+
+ void IWorkingBeatmapCache.Invalidate(BeatmapSetInfo beatmapSetInfo) => workingBeatmapCache.Invalidate(beatmapSetInfo);
+ void IWorkingBeatmapCache.Invalidate(BeatmapInfo beatmapInfo) => workingBeatmapCache.Invalidate(beatmapInfo);
+
+ #endregion
+
+ #region Implementation of IModelFileManager
+
+ public void ReplaceFile(BeatmapSetInfo model, BeatmapSetFileInfo file, Stream contents, string filename = null)
+ {
+ beatmapModelManager.ReplaceFile(model, file, contents, filename);
+ }
+
+ public void DeleteFile(BeatmapSetInfo model, BeatmapSetFileInfo file)
+ {
+ beatmapModelManager.DeleteFile(model, file);
+ }
+
+ public void AddFile(BeatmapSetInfo model, Stream contents, string filename)
+ {
+ beatmapModelManager.AddFile(model, contents, filename);
+ }
+
+ #endregion
+
+ #region Implementation of IDisposable
+
+ public void Dispose()
+ {
+ onlineBeatmapLookupQueue?.Dispose();
+ }
+
+ #endregion
}
}
diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs
deleted file mode 100644
index 3dd34f6c2f..0000000000
--- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-// 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.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Data.Sqlite;
-using osu.Framework.Development;
-using osu.Framework.IO.Network;
-using osu.Framework.Logging;
-using osu.Framework.Platform;
-using osu.Framework.Testing;
-using osu.Framework.Threading;
-using osu.Game.Database;
-using osu.Game.Online.API;
-using osu.Game.Online.API.Requests;
-using SharpCompress.Compressors;
-using SharpCompress.Compressors.BZip2;
-
-namespace osu.Game.Beatmaps
-{
- public partial class BeatmapManager
- {
- [ExcludeFromDynamicCompile]
- private class BeatmapOnlineLookupQueue : IDisposable
- {
- private readonly IAPIProvider api;
- private readonly Storage storage;
-
- private const int update_queue_request_concurrency = 4;
-
- private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapOnlineLookupQueue));
-
- private FileWebRequest cacheDownloadRequest;
-
- private const string cache_database_name = "online.db";
-
- public BeatmapOnlineLookupQueue(IAPIProvider api, Storage storage)
- {
- this.api = api;
- this.storage = storage;
-
- // avoid downloading / using cache for unit tests.
- if (!DebugUtils.IsNUnitRunning && !storage.Exists(cache_database_name))
- prepareLocalCache();
- }
-
- public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken)
- {
- return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray());
- }
-
- // todo: expose this when we need to do individual difficulty lookups.
- protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken)
- => Task.Factory.StartNew(() => lookup(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
-
- private void lookup(BeatmapSetInfo set, BeatmapInfo beatmap)
- {
- if (checkLocalCache(set, beatmap))
- return;
-
- if (api?.State.Value != APIState.Online)
- return;
-
- var req = new GetBeatmapRequest(beatmap);
-
- req.Failure += fail;
-
- try
- {
- // intentionally blocking to limit web request concurrency
- api.Perform(req);
-
- var res = req.Result;
-
- if (res != null)
- {
- beatmap.Status = res.Status;
- beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
- beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
- beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
-
- if (beatmap.Metadata != null)
- beatmap.Metadata.AuthorID = res.AuthorID;
-
- if (beatmap.BeatmapSet.Metadata != null)
- beatmap.BeatmapSet.Metadata.AuthorID = res.AuthorID;
-
- LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
- }
- }
- catch (Exception e)
- {
- fail(e);
- }
-
- void fail(Exception e)
- {
- beatmap.OnlineBeatmapID = null;
- LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})");
- }
- }
-
- private void prepareLocalCache()
- {
- string cacheFilePath = storage.GetFullPath(cache_database_name);
- string compressedCacheFilePath = $"{cacheFilePath}.bz2";
-
- cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2?{DateTimeOffset.UtcNow:yyyyMMdd}");
-
- cacheDownloadRequest.Failed += ex =>
- {
- File.Delete(compressedCacheFilePath);
- File.Delete(cacheFilePath);
-
- Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache download failed: {ex}", LoggingTarget.Database);
- };
-
- cacheDownloadRequest.Finished += () =>
- {
- try
- {
- using (var stream = File.OpenRead(cacheDownloadRequest.Filename))
- using (var outStream = File.OpenWrite(cacheFilePath))
- using (var bz2 = new BZip2Stream(stream, CompressionMode.Decompress, false))
- bz2.CopyTo(outStream);
-
- // set to null on completion to allow lookups to begin using the new source
- cacheDownloadRequest = null;
- }
- catch (Exception ex)
- {
- Logger.Log($"{nameof(BeatmapOnlineLookupQueue)}'s online cache extraction failed: {ex}", LoggingTarget.Database);
- File.Delete(cacheFilePath);
- }
- finally
- {
- File.Delete(compressedCacheFilePath);
- }
- };
-
- cacheDownloadRequest.PerformAsync();
- }
-
- private bool checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmap)
- {
- // download is in progress (or was, and failed).
- if (cacheDownloadRequest != null)
- return false;
-
- // database is unavailable.
- if (!storage.Exists(cache_database_name))
- return false;
-
- if (string.IsNullOrEmpty(beatmap.MD5Hash)
- && string.IsNullOrEmpty(beatmap.Path)
- && beatmap.OnlineBeatmapID == null)
- return false;
-
- try
- {
- using (var db = new SqliteConnection(DatabaseContextFactory.CreateDatabaseConnectionString("online.db", storage)))
- {
- db.Open();
-
- 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.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash));
- cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value));
- cmd.Parameters.Add(new SqliteParameter("@Path", beatmap.Path));
-
- using (var reader = cmd.ExecuteReader())
- {
- if (reader.Read())
- {
- var status = (BeatmapSetOnlineStatus)reader.GetByte(2);
-
- beatmap.Status = status;
- beatmap.BeatmapSet.Status = status;
- beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0);
- beatmap.OnlineBeatmapID = reader.GetInt32(1);
-
- if (beatmap.Metadata != null)
- beatmap.Metadata.AuthorID = reader.GetInt32(3);
-
- if (beatmap.BeatmapSet.Metadata != null)
- beatmap.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3);
-
- LogForModel(set, $"Cached local retrieval for {beatmap}.");
- return true;
- }
- }
- }
- }
- }
- catch (Exception ex)
- {
- LogForModel(set, $"Cached local retrieval for {beatmap} failed with {ex}.");
- }
-
- return false;
- }
-
- public void Dispose()
- {
- cacheDownloadRequest?.Dispose();
- updateScheduler?.Dispose();
- }
- }
- }
-}
diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs
index 713f80d1fe..711533e118 100644
--- a/osu.Game/Beatmaps/BeatmapMetadata.cs
+++ b/osu.Game/Beatmaps/BeatmapMetadata.cs
@@ -4,9 +4,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
-using System.Linq;
using Newtonsoft.Json;
-using osu.Framework.Localisation;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Users;
@@ -15,7 +13,7 @@ namespace osu.Game.Beatmaps
{
[ExcludeFromDynamicCompile]
[Serializable]
- public class BeatmapMetadata : IEquatable, IHasPrimaryKey
+ public class BeatmapMetadata : IEquatable, IHasPrimaryKey, IBeatmapMetadataInfo
{
public int ID { get; set; }
@@ -83,50 +81,13 @@ namespace osu.Game.Beatmaps
public int PreviewTime { get; set; }
public string AudioFile { get; set; }
+
public string BackgroundFile { get; set; }
- public override string ToString()
- {
- string author = Author == null ? string.Empty : $"({Author})";
- return $"{Artist} - {Title} {author}".Trim();
- }
+ public bool Equals(BeatmapMetadata other) => ((IBeatmapMetadataInfo)this).Equals(other);
- public RomanisableString ToRomanisableString()
- {
- string author = Author == null ? string.Empty : $"({Author})";
- var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode;
- var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode;
+ public override string ToString() => this.GetDisplayTitle();
- return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim());
- }
-
- [JsonIgnore]
- public string[] SearchableTerms => new[]
- {
- Author?.Username,
- Artist,
- ArtistUnicode,
- Title,
- TitleUnicode,
- Source,
- Tags
- }.Where(s => !string.IsNullOrEmpty(s)).ToArray();
-
- public bool Equals(BeatmapMetadata other)
- {
- if (other == null)
- return false;
-
- return Title == other.Title
- && TitleUnicode == other.TitleUnicode
- && Artist == other.Artist
- && ArtistUnicode == other.ArtistUnicode
- && AuthorString == other.AuthorString
- && Source == other.Source
- && Tags == other.Tags
- && PreviewTime == other.PreviewTime
- && AudioFile == other.AudioFile
- && BackgroundFile == other.BackgroundFile;
- }
+ string IBeatmapMetadataInfo.Author => AuthorString;
}
}
diff --git a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs
new file mode 100644
index 0000000000..ee946eeeec
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs
@@ -0,0 +1,46 @@
+// 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 osu.Framework.Localisation;
+
+namespace osu.Game.Beatmaps
+{
+ public static class BeatmapMetadataInfoExtensions
+ {
+ ///
+ /// An array of all searchable terms provided in contained metadata.
+ ///
+ public static string[] GetSearchableTerms(this IBeatmapMetadataInfo metadataInfo) => new[]
+ {
+ metadataInfo.Author,
+ metadataInfo.Artist,
+ metadataInfo.ArtistUnicode,
+ metadataInfo.Title,
+ metadataInfo.TitleUnicode,
+ metadataInfo.Source,
+ metadataInfo.Tags
+ }.Where(s => !string.IsNullOrEmpty(s)).ToArray();
+
+ ///
+ /// A user-presentable display title representing this metadata.
+ ///
+ public static string GetDisplayTitle(this IBeatmapMetadataInfo metadataInfo)
+ {
+ string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})";
+ return $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim();
+ }
+
+ ///
+ /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields.
+ ///
+ public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo)
+ {
+ string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})";
+ var artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode;
+ var titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode;
+
+ return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim());
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/BeatmapModelDownloader.cs b/osu.Game/Beatmaps/BeatmapModelDownloader.cs
new file mode 100644
index 0000000000..ae482eeafd
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapModelDownloader.cs
@@ -0,0 +1,21 @@
+// 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.Platform;
+using osu.Game.Database;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+
+namespace osu.Game.Beatmaps
+{
+ public class BeatmapModelDownloader : ModelDownloader
+ {
+ protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) =>
+ new DownloadBeatmapSetRequest(set, minimiseDownloadSize);
+
+ public BeatmapModelDownloader(BeatmapModelManager beatmapModelManager, IAPIProvider api, GameHost host = null)
+ : base(beatmapModelManager, api, host)
+ {
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs
new file mode 100644
index 0000000000..787559899a
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapModelManager.cs
@@ -0,0 +1,473 @@
+// 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.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Logging;
+using osu.Framework.Platform;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps.Formats;
+using osu.Game.Database;
+using osu.Game.IO;
+using osu.Game.IO.Archives;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Skinning;
+using Decoder = osu.Game.Beatmaps.Formats.Decoder;
+
+namespace osu.Game.Beatmaps
+{
+ ///