diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3c52802cf6..ec3816d541 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,10 +20,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- - name: Install .NET 5.0.x
+ - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1
with:
- dotnet-version: "5.0.x"
+ dotnet-version: "6.0.x"
# FIXME: libavformat is not included in Ubuntu. Let's fix that.
# https://github.com/ppy/osu-framework/issues/4349
@@ -65,10 +65,10 @@ jobs:
run: |
$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.2
- - name: Install .NET 5.0.x
+ - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1
with:
- dotnet-version: "5.0.x"
+ dotnet-version: "6.0.x"
# Contrary to seemingly any other msbuild, msbuild running on macOS/Mono
# cannot accept .sln(f) files as arguments.
@@ -84,10 +84,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- - name: Install .NET 5.0.x
+ - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1
with:
- dotnet-version: "5.0.x"
+ dotnet-version: "6.0.x"
# Contrary to seemingly any other msbuild, msbuild running on macOS/Mono
# cannot accept .sln(f) files as arguments.
@@ -102,17 +102,17 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- # FIXME: Tools won't run in .NET 5.0 unless you install 3.1.x LTS side by side.
+ # FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side.
# https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
- name: Install .NET 3.1.x LTS
uses: actions/setup-dotnet@v1
with:
dotnet-version: "3.1.x"
- - name: Install .NET 5.0.x
+ - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1
with:
- dotnet-version: "5.0.x"
+ dotnet-version: "6.0.x"
- name: Restore Tools
run: dotnet tool restore
diff --git a/.run/osu! (Second Client).run.xml b/.run/osu! (Second Client).run.xml
index 599b4b986b..9a471df902 100644
--- a/.run/osu! (Second Client).run.xml
+++ b/.run/osu! (Second Client).run.xml
@@ -1,8 +1,8 @@
-
+
-
+
@@ -12,9 +12,9 @@
-
+
-
\ No newline at end of file
+
diff --git a/Directory.Build.props b/Directory.Build.props
index 894ea25c8b..c1682638c2 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -18,7 +18,7 @@
-
+
$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset
@@ -32,13 +32,8 @@
NU1701:
DeepEqual is not netstandard-compatible. This is fine since we run tests with .NET Framework anyway.
This is required due to https://github.com/NuGet/Home/issues/5740
-
- CA9998:
- Microsoft.CodeAnalysis.FxCopAnalyzers has been deprecated.
- The entire package will be able to be removed after migrating to .NET 5,
- as analysers are shipped as part of the .NET 5 SDK anyway.
-->
- $(NoWarn);NU1701;CA9998
+ $(NoWarn);NU1701
false
diff --git a/README.md b/README.md
index b1dfcab416..7ace47a74f 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ You can see some examples of custom rulesets by visiting the [custom ruleset dir
Please make sure you have the following prerequisites:
-- A desktop platform with the [.NET 5.0 SDK](https://dotnet.microsoft.com/download) installed.
+- A desktop platform with the [.NET 6.0 SDK](https://dotnet.microsoft.com/download) installed.
- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
index 3c6aaa39ca..cb922c5a58 100644
--- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
+++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
@@ -20,7 +20,7 @@
WinExe
- net5.0
+ net6.0
osu.Game.Rulesets.EmptyFreeform.Tests
-
\ No newline at end of file
+
diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index 0719dd30df..5ecd9cc675 100644
--- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -20,7 +20,7 @@
WinExe
- net5.0
+ net6.0
osu.Game.Rulesets.Pippidon.Tests
-
\ No newline at end of file
+
diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
index d0db43cc81..33ad0ac4f7 100644
--- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
@@ -20,7 +20,7 @@
WinExe
- net5.0
+ net6.0
osu.Game.Rulesets.EmptyScrolling.Tests
-
\ No newline at end of file
+
diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index 0719dd30df..5ecd9cc675 100644
--- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -20,7 +20,7 @@
WinExe
- net5.0
+ net6.0
osu.Game.Rulesets.Pippidon.Tests
-
\ No newline at end of file
+
diff --git a/osu.Android.props b/osu.Android.props
index 1a2859c851..24a0d20874 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,10 +52,10 @@
-
+
-
+
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 89b9ffb94b..b1117bf796 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -1,6 +1,6 @@
- net5.0
+ net6.0
WinExe
true
A free-to-win rhythm game. Rhythm is just a *click* away!
@@ -26,10 +26,13 @@
-
+
-
-
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
index 57b914bee6..434c0e0367 100644
--- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
+++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj
@@ -1,7 +1,7 @@
- net5.0
+ net6.0
Exe
false
diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
index 13f2e25f05..fc6d900567 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -9,9 +9,9 @@
WinExe
- net5.0
+ net6.0
-
\ No newline at end of file
+
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs
index 39a58d336d..8e069d7d16 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs
@@ -9,6 +9,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{
public class CatchDifficultyAttributes : DifficultyAttributes
{
+ ///
+ /// The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
+ ///
+ ///
+ /// Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
+ ///
[JsonProperty("approach_rate")]
public double ApproachRate { get; set; }
diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
index d51a6da4f9..ddad2adfea 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -9,9 +9,9 @@
WinExe
- net5.0
+ net6.0
-
\ No newline at end of file
+
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs
index 979a04ddf8..5b7a460079 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs
@@ -9,9 +9,18 @@ namespace osu.Game.Rulesets.Mania.Difficulty
{
public class ManiaDifficultyAttributes : DifficultyAttributes
{
+ ///
+ /// The hit window for a GREAT hit inclusive of rate-adjusting mods (DT/HT/etc).
+ ///
+ ///
+ /// Rate-adjusting mods do not affect the hit window at all in osu-stable.
+ ///
[JsonProperty("great_hit_window")]
public double GreatHitWindow { get; set; }
+ ///
+ /// The score multiplier applied via score-reducing mods.
+ ///
[JsonProperty("score_multiplier")]
public double ScoreMultiplier { get; set; }
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
index 1f82eb7ccd..b17aa7fc4d 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs
@@ -48,7 +48,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty
{
StarRating = skills[0].DifficultyValue() * star_scaling_factor,
Mods = mods,
- GreatHitWindow = Math.Ceiling(getHitWindow300(mods) / clockRate),
+ // In osu-stable mania, rate-adjustment mods don't affect the hit window.
+ // This is done the way it is to introduce fractional differences in order to match osu-stable for the time being.
+ GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate),
ScoreMultiplier = getScoreMultiplier(mods),
MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1),
};
@@ -108,7 +110,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
}
}
- private int getHitWindow300(Mod[] mods)
+ private double getHitWindow300(Mod[] mods)
{
if (isForCurrentRuleset)
{
@@ -121,19 +123,14 @@ namespace osu.Game.Rulesets.Mania.Difficulty
return applyModAdjustments(47, mods);
- static int applyModAdjustments(double value, Mod[] mods)
+ static double applyModAdjustments(double value, Mod[] mods)
{
if (mods.Any(m => m is ManiaModHardRock))
value /= 1.4;
else if (mods.Any(m => m is ManiaModEasy))
value *= 1.4;
- if (mods.Any(m => m is ManiaModDoubleTime))
- value *= 1.5;
- else if (mods.Any(m => m is ManiaModHalfTime))
- value *= 0.75;
-
- return (int)value;
+ return value;
}
}
diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs
index 8a8c41bb8a..722cb55036 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs
@@ -43,14 +43,11 @@ namespace osu.Game.Rulesets.Mania.Difficulty
countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss);
- IEnumerable scoreIncreaseMods = Ruleset.GetModsFor(ModType.DifficultyIncrease);
-
- double scoreMultiplier = 1.0;
- foreach (var m in mods.Where(m => !scoreIncreaseMods.Contains(m)))
- scoreMultiplier *= m.ScoreMultiplier;
-
- // Scale score up, so it's comparable to other keymods
- scaledScore *= 1.0 / scoreMultiplier;
+ if (Attributes.ScoreMultiplier > 0)
+ {
+ // Scale score up, so it's comparable to other keymods
+ scaledScore *= 1.0 / Attributes.ScoreMultiplier;
+ }
// Arbitrary initial value for scaling pp in order to standardize distributions across game modes.
// The specific number has no intrinsic meaning and can be adjusted as needed.
diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs
index 952fc7ddd6..fdacc75c92 100644
--- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs
@@ -98,8 +98,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
float rightLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightLineWidth, columnIndex)?.Value ?? 1;
bool hasLeftLine = leftLineWidth > 0;
- bool hasRightLine = rightLineWidth > 0 && skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
- || isLastColumn;
+ bool hasRightLine = (rightLineWidth > 0 && skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value >= 2.4m) || isLastColumn;
Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnLineColour, columnIndex)?.Value ?? Color4.White;
Color4 backgroundColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, columnIndex)?.Value ?? Color4.Black;
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index fea2e408f6..bd4c3d3345 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -10,9 +10,9 @@
WinExe
- net5.0
+ net6.0
-
\ No newline at end of file
+
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
index 126a9b0183..3deed4ea3d 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
@@ -12,30 +12,68 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
public class OsuDifficultyAttributes : DifficultyAttributes
{
+ ///
+ /// The difficulty corresponding to the aim skill.
+ ///
[JsonProperty("aim_difficulty")]
public double AimDifficulty { get; set; }
+ ///
+ /// The difficulty corresponding to the speed skill.
+ ///
[JsonProperty("speed_difficulty")]
public double SpeedDifficulty { get; set; }
+ ///
+ /// The difficulty corresponding to the flashlight skill.
+ ///
[JsonProperty("flashlight_difficulty")]
public double FlashlightDifficulty { get; set; }
+ ///
+ /// Describes how much of is contributed to by hitcircles or sliders.
+ /// A value closer to 1.0 indicates most of is contributed by hitcircles.
+ /// A value closer to 0.0 indicates most of is contributed by sliders.
+ ///
[JsonProperty("slider_factor")]
public double SliderFactor { get; set; }
+ ///
+ /// The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
+ ///
+ ///
+ /// Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
+ ///
[JsonProperty("approach_rate")]
public double ApproachRate { get; set; }
+ ///
+ /// The perceived overall difficulty inclusive of rate-adjusting mods (DT/HT/etc).
+ ///
+ ///
+ /// Rate-adjusting mods don't directly affect the overall difficulty value, but have a perceived effect as a result of adjusting audio timing.
+ ///
[JsonProperty("overall_difficulty")]
public double OverallDifficulty { get; set; }
+ ///
+ /// The beatmap's drain rate. This doesn't scale with rate-adjusting mods.
+ ///
public double DrainRate { get; set; }
+ ///
+ /// The number of hitcircles in the beatmap.
+ ///
public int HitCircleCount { get; set; }
+ ///
+ /// The number of sliders in the beatmap.
+ ///
public int SliderCount { get; set; }
+ ///
+ /// The number of spinners in the beatmap.
+ ///
public int SpinnerCount { get; set; }
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
index 5d191119b9..1bf63ef6d4 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
@@ -87,8 +87,8 @@ namespace osu.Game.Rulesets.Osu.Mods
requiresHold |= slider.Ball.IsHovered || h.IsHovered;
break;
- case DrawableSpinner _:
- requiresHold = true;
+ case DrawableSpinner spinner:
+ requiresHold |= spinner.HitObject.SpinsRequired > 0;
break;
}
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
index ad3713e047..a6b8eb8651 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -9,9 +9,9 @@
WinExe
- net5.0
+ net6.0
-
\ No newline at end of file
+
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
index 31f5a6f570..3dc5438072 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
@@ -9,18 +9,39 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
{
public class TaikoDifficultyAttributes : DifficultyAttributes
{
+ ///
+ /// The difficulty corresponding to the stamina skill.
+ ///
[JsonProperty("stamina_difficulty")]
public double StaminaDifficulty { get; set; }
+ ///
+ /// The difficulty corresponding to the rhythm skill.
+ ///
[JsonProperty("rhythm_difficulty")]
public double RhythmDifficulty { get; set; }
+ ///
+ /// The difficulty corresponding to the colour skill.
+ ///
[JsonProperty("colour_difficulty")]
public double ColourDifficulty { get; set; }
+ ///
+ /// The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
+ ///
+ ///
+ /// Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
+ ///
[JsonProperty("approach_rate")]
public double ApproachRate { get; set; }
+ ///
+ /// The perceived hit window for a GREAT hit inclusive of rate-adjusting mods (DT/HT/etc).
+ ///
+ ///
+ /// Rate-adjusting mods don't directly affect the hit window, but have a perceived effect as a result of adjusting audio timing.
+ ///
[JsonProperty("great_hit_window")]
public double GreatHitWindow { get; set; }
diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
index 0c49a18c8f..4adb7002a0 100644
--- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
+++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
@@ -21,8 +21,8 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
{
var user = new APIUser { Id = 33 };
- AddRepeatStep("add user multiple times", () => Client.AddUser(user), 3);
- AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
+ AddRepeatStep("add user multiple times", () => MultiplayerClient.AddUser(user), 3);
+ AddAssert("room has 2 users", () => MultiplayerClient.Room?.Users.Count == 2);
}
[Test]
@@ -30,11 +30,11 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
{
var user = new APIUser { Id = 44 };
- AddStep("add user", () => Client.AddUser(user));
- AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
+ AddStep("add user", () => MultiplayerClient.AddUser(user));
+ AddAssert("room has 2 users", () => MultiplayerClient.Room?.Users.Count == 2);
- AddRepeatStep("remove user multiple times", () => Client.RemoveUser(user), 3);
- AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1);
+ AddRepeatStep("remove user multiple times", () => MultiplayerClient.RemoveUser(user), 3);
+ AddAssert("room has 1 user", () => MultiplayerClient.Room?.Users.Count == 1);
}
[Test]
@@ -42,7 +42,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
{
int id = 2000;
- AddRepeatStep("add some users", () => Client.AddUser(new APIUser { Id = id++ }), 5);
+ AddRepeatStep("add some users", () => MultiplayerClient.AddUser(new APIUser { Id = id++ }), 5);
checkPlayingUserCount(0);
changeState(3, MultiplayerUserState.WaitingForLoad);
@@ -57,17 +57,17 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
changeState(6, MultiplayerUserState.WaitingForLoad);
checkPlayingUserCount(6);
- AddStep("another user left", () => Client.RemoveUser((Client.Room?.Users.Last().User).AsNonNull()));
+ AddStep("another user left", () => MultiplayerClient.RemoveUser((MultiplayerClient.Room?.Users.Last().User).AsNonNull()));
checkPlayingUserCount(5);
- AddStep("leave room", () => Client.LeaveRoom());
+ AddStep("leave room", () => MultiplayerClient.LeaveRoom());
checkPlayingUserCount(0);
}
[Test]
public void TestPlayingUsersUpdatedOnJoin()
{
- AddStep("leave room", () => Client.LeaveRoom());
+ AddStep("leave room", () => MultiplayerClient.LeaveRoom());
AddUntilStep("wait for room part", () => !RoomJoined);
AddStep("create room initially in gameplay", () =>
@@ -76,7 +76,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
newRoom.CopyFrom(SelectedRoom.Value);
newRoom.RoomID.Value = null;
- Client.RoomSetupAction = room =>
+ MultiplayerClient.RoomSetupAction = room =>
{
room.State = MultiplayerRoomState.Playing;
room.Users.Add(new MultiplayerRoomUser(PLAYER_1_ID)
@@ -94,15 +94,15 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
}
private void checkPlayingUserCount(int expectedCount)
- => AddAssert($"{"user".ToQuantity(expectedCount)} playing", () => Client.CurrentMatchPlayingUserIds.Count == expectedCount);
+ => AddAssert($"{"user".ToQuantity(expectedCount)} playing", () => MultiplayerClient.CurrentMatchPlayingUserIds.Count == expectedCount);
private void changeState(int userCount, MultiplayerUserState state)
=> AddStep($"{"user".ToQuantity(userCount)} in {state}", () =>
{
for (int i = 0; i < userCount; ++i)
{
- int userId = Client.Room?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!");
- Client.ChangeUserState(userId, state);
+ int userId = MultiplayerClient.Room?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!");
+ MultiplayerClient.ChangeUserState(userId, state);
}
});
}
diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs
index 343fc7e6e0..dd7feb6699 100644
--- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs
+++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
@@ -12,6 +13,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
+using osu.Framework.Graphics;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Framework.Testing;
@@ -21,6 +23,8 @@ 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.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Tests.Resources;
@@ -53,6 +57,25 @@ namespace osu.Game.Tests.Online
[SetUp]
public void SetUp() => Schedule(() =>
{
+ ((DummyAPIAccess)API).HandleRequest = req =>
+ {
+ switch (req)
+ {
+ case GetBeatmapsRequest beatmapsReq:
+ var beatmap = CreateAPIBeatmap();
+ beatmap.OnlineID = testBeatmapInfo.OnlineID;
+ beatmap.OnlineBeatmapSetID = testBeatmapSet.OnlineID;
+ beatmap.Checksum = testBeatmapInfo.MD5Hash;
+ beatmap.BeatmapSet!.OnlineID = testBeatmapSet.OnlineID;
+
+ beatmapsReq.TriggerSuccess(new GetBeatmapsResponse { Beatmaps = new List { beatmap } });
+ return true;
+
+ default:
+ return false;
+ }
+ };
+
beatmaps.AllowImport = new TaskCompletionSource();
testBeatmapFile = TestResources.GetQuickTestBeatmapForImport();
@@ -63,18 +86,35 @@ namespace osu.Game.Tests.Online
Realm.Write(r => r.RemoveAll());
Realm.Write(r => r.RemoveAll());
- selectedItem.Value = new PlaylistItem
+ selectedItem.Value = new PlaylistItem(testBeatmapInfo)
{
- Beatmap = { Value = testBeatmapInfo },
- Ruleset = { Value = testBeatmapInfo.Ruleset },
+ RulesetID = testBeatmapInfo.Ruleset.OnlineID,
};
- Child = availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker
- {
- SelectedItem = { BindTarget = selectedItem, }
- };
+ recreateChildren();
});
+ private void recreateChildren()
+ {
+ var beatmapLookupCache = new BeatmapLookupCache();
+
+ Child = new DependencyProvidingContainer
+ {
+ CachedDependencies = new[]
+ {
+ (typeof(BeatmapLookupCache), (object)beatmapLookupCache)
+ },
+ Children = new Drawable[]
+ {
+ beatmapLookupCache,
+ availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker
+ {
+ SelectedItem = { BindTarget = selectedItem, }
+ }
+ }
+ };
+ }
+
[Test]
public void TestBeatmapDownloadingFlow()
{
@@ -123,10 +163,7 @@ namespace osu.Game.Tests.Online
});
addAvailabilityCheckStep("state not downloaded", BeatmapAvailability.NotDownloaded);
- AddStep("recreate tracker", () => Child = availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker
- {
- SelectedItem = { BindTarget = selectedItem }
- });
+ AddStep("recreate tracker", recreateChildren);
addAvailabilityCheckStep("state not downloaded as well", BeatmapAvailability.NotDownloaded);
AddStep("reimport original beatmap", () => beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely());
@@ -167,7 +204,8 @@ namespace osu.Game.Tests.Online
public Live CurrentImport { get; private set; }
- public TestBeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null)
+ public TestBeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources,
+ GameHost host = null, WorkingBeatmap defaultBeatmap = null)
: base(storage, realm, rulesets, api, audioManager, resources, host, defaultBeatmap)
{
}
diff --git a/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs
index d33081662d..9e7ea02101 100644
--- a/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs
+++ b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs
@@ -3,6 +3,7 @@
using System;
using NUnit.Framework;
+using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
namespace osu.Game.Tests.OnlinePlay
@@ -29,9 +30,9 @@ namespace osu.Game.Tests.OnlinePlay
{
var items = new[]
{
- new PlaylistItem { ID = 1, BeatmapID = 1001, PlaylistOrder = 1 },
- new PlaylistItem { ID = 2, BeatmapID = 1002, PlaylistOrder = 2 },
- new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1001 }) { ID = 1, PlaylistOrder = 1 },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 2, PlaylistOrder = 2 },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1003 }) { ID = 3, PlaylistOrder = 3 },
};
Assert.Multiple(() =>
@@ -47,9 +48,9 @@ namespace osu.Game.Tests.OnlinePlay
{
var items = new[]
{
- new PlaylistItem { ID = 2, BeatmapID = 1002, PlaylistOrder = 2 },
- new PlaylistItem { ID = 1, BeatmapID = 1001, PlaylistOrder = 1 },
- new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 2, PlaylistOrder = 2 },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1001 }) { ID = 1, PlaylistOrder = 1 },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1003 }) { ID = 3, PlaylistOrder = 3 },
};
Assert.Multiple(() =>
@@ -65,9 +66,9 @@ namespace osu.Game.Tests.OnlinePlay
{
var items = new[]
{
- new PlaylistItem { ID = 1, BeatmapID = 1001, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 55, 0, TimeSpan.Zero) },
- new PlaylistItem { ID = 2, BeatmapID = 1002, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 53, 0, TimeSpan.Zero) },
- new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1001 }) { ID = 1, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 55, 0, TimeSpan.Zero) },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 2, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 53, 0, TimeSpan.Zero) },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1003 }) { ID = 3, PlaylistOrder = 3 },
};
Assert.Multiple(() =>
@@ -83,9 +84,9 @@ namespace osu.Game.Tests.OnlinePlay
{
var items = new[]
{
- new PlaylistItem { ID = 1, BeatmapID = 1001, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 55, 0, TimeSpan.Zero) },
- new PlaylistItem { ID = 2, BeatmapID = 1002, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 53, 0, TimeSpan.Zero) },
- new PlaylistItem { ID = 3, BeatmapID = 1002, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 57, 0, TimeSpan.Zero) },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1001 }) { ID = 1, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 55, 0, TimeSpan.Zero) },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 2, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 53, 0, TimeSpan.Zero) },
+ new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 3, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 57, 0, TimeSpan.Zero) },
};
Assert.Multiple(() =>
diff --git a/osu.Game.Tests/Resources/client.db b/osu.Game.Tests/Resources/client.db
new file mode 100644
index 0000000000..079d5af3b7
Binary files /dev/null and b/osu.Game.Tests/Resources/client.db differ
diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
index f0d9ece06f..c6e7988543 100644
--- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
+++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
@@ -36,9 +36,9 @@ namespace osu.Game.Tests.Rulesets.Scoring
[TestCase(ScoringMode.Standardised, HitResult.Meh, 750_000)]
[TestCase(ScoringMode.Standardised, HitResult.Ok, 800_000)]
[TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)]
- [TestCase(ScoringMode.Classic, HitResult.Meh, 41)]
- [TestCase(ScoringMode.Classic, HitResult.Ok, 46)]
- [TestCase(ScoringMode.Classic, HitResult.Great, 72)]
+ [TestCase(ScoringMode.Classic, HitResult.Meh, 20)]
+ [TestCase(ScoringMode.Classic, HitResult.Ok, 23)]
+ [TestCase(ScoringMode.Classic, HitResult.Great, 36)]
public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
scoreProcessor.Mode.Value = scoringMode;
@@ -86,17 +86,17 @@ namespace osu.Game.Tests.Rulesets.Scoring
[TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points)
[TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points)
[TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)]
- [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 68)]
- [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 81)]
- [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 109)]
- [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 149)]
- [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 149)]
- [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 9)]
- [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 15)]
+ [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 86)]
+ [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 104)]
+ [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 140)]
+ [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 190)]
+ [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 190)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 18)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 31)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)]
- [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 149)]
- [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 18)]
- [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 18)]
+ [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 12)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 36)]
+ [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 36)]
public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore)
{
var minResult = new TestJudgement(hitResult).MinResult;
@@ -128,8 +128,8 @@ namespace osu.Game.Tests.Rulesets.Scoring
///
[TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, 978_571)] // (3 * 10 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.SmallTickMiss, 914_286)] // (3 * 0 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000
- [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 69)] // (((3 * 10 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
- [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 60)] // (((3 * 0 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 34)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 30)]
public void TestSmallTicksAccuracy(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
IEnumerable hitObjects = Enumerable
diff --git a/osu.Game.Tests/Utils/NamingUtilsTest.cs b/osu.Game.Tests/Utils/NamingUtilsTest.cs
new file mode 100644
index 0000000000..62e688db90
--- /dev/null
+++ b/osu.Game.Tests/Utils/NamingUtilsTest.cs
@@ -0,0 +1,132 @@
+// 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.Game.Utils;
+
+namespace osu.Game.Tests.Utils
+{
+ [TestFixture]
+ public class NamingUtilsTest
+ {
+ [Test]
+ public void TestEmptySet()
+ {
+ string nextBestName = NamingUtils.GetNextBestName(Enumerable.Empty(), "New Difficulty");
+
+ Assert.AreEqual("New Difficulty", nextBestName);
+ }
+
+ [Test]
+ public void TestNotTaken()
+ {
+ string[] existingNames =
+ {
+ "Something",
+ "Entirely",
+ "Different"
+ };
+
+ string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty");
+
+ Assert.AreEqual("New Difficulty", nextBestName);
+ }
+
+ [Test]
+ public void TestNotTakenButClose()
+ {
+ string[] existingNames =
+ {
+ "New Difficulty(1)",
+ "New Difficulty (abcd)",
+ "New Difficulty but not really"
+ };
+
+ string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty");
+
+ Assert.AreEqual("New Difficulty", nextBestName);
+ }
+
+ [Test]
+ public void TestAlreadyTaken()
+ {
+ string[] existingNames =
+ {
+ "New Difficulty"
+ };
+
+ string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty");
+
+ Assert.AreEqual("New Difficulty (1)", nextBestName);
+ }
+
+ [Test]
+ public void TestAlreadyTakenWithDifferentCase()
+ {
+ string[] existingNames =
+ {
+ "new difficulty"
+ };
+
+ string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty");
+
+ Assert.AreEqual("New Difficulty (1)", nextBestName);
+ }
+
+ [Test]
+ public void TestAlreadyTakenWithBrackets()
+ {
+ string[] existingNames =
+ {
+ "new difficulty (copy)"
+ };
+
+ string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty (copy)");
+
+ Assert.AreEqual("New Difficulty (copy) (1)", nextBestName);
+ }
+
+ [Test]
+ public void TestMultipleAlreadyTaken()
+ {
+ string[] existingNames =
+ {
+ "New Difficulty",
+ "New difficulty (1)",
+ "new Difficulty (2)",
+ "New DIFFICULTY (3)"
+ };
+
+ string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty");
+
+ Assert.AreEqual("New Difficulty (4)", nextBestName);
+ }
+
+ [Test]
+ public void TestEvenMoreAlreadyTaken()
+ {
+ string[] existingNames = Enumerable.Range(1, 30).Select(i => $"New Difficulty ({i})").Append("New Difficulty").ToArray();
+
+ string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty");
+
+ Assert.AreEqual("New Difficulty (31)", nextBestName);
+ }
+
+ [Test]
+ public void TestMultipleAlreadyTakenWithGaps()
+ {
+ string[] existingNames =
+ {
+ "New Difficulty",
+ "New Difficulty (1)",
+ "New Difficulty (4)",
+ "New Difficulty (9)"
+ };
+
+ string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty");
+
+ Assert.AreEqual("New Difficulty (2)", nextBestName);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs
index a14c9aded3..ecd4035edd 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs
@@ -6,15 +6,23 @@ using System.IO;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.ObjectExtensions;
+using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Database;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Setup;
using osu.Game.Storyboards;
using osu.Game.Tests.Resources;
+using osuTK;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
@@ -92,12 +100,27 @@ namespace osu.Game.Tests.Visual.Editing
}
[Test]
- public void TestCreateNewDifficulty()
+ public void TestCreateNewDifficulty([Values] bool sameRuleset)
{
string firstDifficultyName = Guid.NewGuid().ToString();
string secondDifficultyName = Guid.NewGuid().ToString();
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = firstDifficultyName);
+ AddStep("add timing point", () => EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }));
+ AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[]
+ {
+ new HitCircle
+ {
+ Position = new Vector2(0),
+ StartTime = 0
+ },
+ new HitCircle
+ {
+ Position = OsuPlayfield.BASE_SIZE,
+ StartTime = 1000
+ }
+ }));
+
AddStep("save beatmap", () => Editor.Save());
AddAssert("new beatmap persisted", () =>
{
@@ -111,13 +134,27 @@ namespace osu.Game.Tests.Visual.Editing
});
AddAssert("can save again", () => Editor.Save());
- AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
+ AddStep("create new difficulty", () => Editor.CreateNewDifficulty(sameRuleset ? new OsuRuleset().RulesetInfo : new CatchRuleset().RulesetInfo));
+
+ if (sameRuleset)
+ {
+ AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
+ AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog.PerformOkAction());
+ }
+
AddUntilStep("wait for created", () =>
{
string difficultyName = Editor.ChildrenOfType().SingleOrDefault()?.BeatmapInfo.DifficultyName;
return difficultyName != null && difficultyName != firstDifficultyName;
});
+ AddAssert("created difficulty has timing point", () =>
+ {
+ var timingPoint = EditorBeatmap.ControlPointInfo.TimingPoints.Single();
+ return timingPoint.Time == 0 && timingPoint.BeatLength == 1000;
+ });
+ AddAssert("created difficulty has no objects", () => EditorBeatmap.HitObjects.Count == 0);
+
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = secondDifficultyName);
AddStep("save beatmap", () => Editor.Save());
AddAssert("new beatmap persisted", () =>
@@ -133,11 +170,111 @@ namespace osu.Game.Tests.Visual.Editing
}
[Test]
- public void TestCreateNewBeatmapFailsWithBlankNamedDifficulties()
+ public void TestCopyDifficulty()
+ {
+ string originalDifficultyName = Guid.NewGuid().ToString();
+ string copyDifficultyName = $"{originalDifficultyName} (copy)";
+
+ AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = originalDifficultyName);
+ AddStep("add timing point", () => EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }));
+ AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[]
+ {
+ new HitCircle
+ {
+ Position = new Vector2(0),
+ StartTime = 0
+ },
+ new HitCircle
+ {
+ Position = OsuPlayfield.BASE_SIZE,
+ StartTime = 1000
+ }
+ }));
+ AddStep("set approach rate", () => EditorBeatmap.Difficulty.ApproachRate = 4);
+ AddStep("set combo colours", () =>
+ {
+ var beatmapSkin = EditorBeatmap.BeatmapSkin.AsNonNull();
+ beatmapSkin.ComboColours.Clear();
+ beatmapSkin.ComboColours.AddRange(new[]
+ {
+ new Colour4(255, 0, 0, 255),
+ new Colour4(0, 0, 255, 255)
+ });
+ });
+ AddStep("set status & online ID", () =>
+ {
+ EditorBeatmap.BeatmapInfo.OnlineID = 123456;
+ EditorBeatmap.BeatmapInfo.Status = BeatmapOnlineStatus.WIP;
+ });
+
+ AddStep("save beatmap", () => Editor.Save());
+ AddAssert("new beatmap persisted", () =>
+ {
+ var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == originalDifficultyName);
+ var set = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
+
+ return beatmap != null
+ && beatmap.DifficultyName == originalDifficultyName
+ && set != null
+ && set.PerformRead(s => s.Beatmaps.Single().ID == beatmap.ID);
+ });
+ AddAssert("can save again", () => Editor.Save());
+
+ AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
+
+ AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
+ AddStep("confirm creation as a copy", () => DialogOverlay.CurrentDialog.Buttons.ElementAt(1).TriggerClick());
+
+ AddUntilStep("wait for created", () =>
+ {
+ string difficultyName = Editor.ChildrenOfType().SingleOrDefault()?.BeatmapInfo.DifficultyName;
+ return difficultyName != null && difficultyName != originalDifficultyName;
+ });
+
+ AddAssert("created difficulty has copy suffix in name", () => EditorBeatmap.BeatmapInfo.DifficultyName == copyDifficultyName);
+ AddAssert("created difficulty has timing point", () =>
+ {
+ var timingPoint = EditorBeatmap.ControlPointInfo.TimingPoints.Single();
+ return timingPoint.Time == 0 && timingPoint.BeatLength == 1000;
+ });
+ AddAssert("created difficulty has objects", () => EditorBeatmap.HitObjects.Count == 2);
+ AddAssert("approach rate correctly copied", () => EditorBeatmap.Difficulty.ApproachRate == 4);
+ AddAssert("combo colours correctly copied", () => EditorBeatmap.BeatmapSkin.AsNonNull().ComboColours.Count == 2);
+
+ AddAssert("status not copied", () => EditorBeatmap.BeatmapInfo.Status == BeatmapOnlineStatus.None);
+ AddAssert("online ID not copied", () => EditorBeatmap.BeatmapInfo.OnlineID == -1);
+
+ AddStep("save beatmap", () => Editor.Save());
+
+ BeatmapInfo refetchedBeatmap = null;
+ Live refetchedBeatmapSet = null;
+
+ AddStep("refetch from database", () =>
+ {
+ refetchedBeatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == copyDifficultyName);
+ refetchedBeatmapSet = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
+ });
+
+ AddAssert("new beatmap persisted", () =>
+ {
+ return refetchedBeatmap != null
+ && refetchedBeatmap.DifficultyName == copyDifficultyName
+ && refetchedBeatmapSet != null
+ && refetchedBeatmapSet.PerformRead(s =>
+ s.Beatmaps.Count == 2
+ && s.Beatmaps.Any(b => b.DifficultyName == originalDifficultyName)
+ && s.Beatmaps.Any(b => b.DifficultyName == copyDifficultyName));
+ });
+ AddAssert("old beatmap file not deleted", () => refetchedBeatmapSet.AsNonNull().PerformRead(s => s.Files.Count == 2));
+ }
+
+ [Test]
+ public void TestCreateMultipleNewDifficultiesSucceeds()
{
Guid setId = Guid.Empty;
AddStep("retrieve set ID", () => setId = EditorBeatmap.BeatmapInfo.BeatmapSet!.ID);
+ AddStep("set difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = "New Difficulty");
AddStep("save beatmap", () => Editor.Save());
AddAssert("new beatmap persisted", () =>
{
@@ -146,15 +283,24 @@ namespace osu.Game.Tests.Visual.Editing
});
AddStep("try to create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
- AddAssert("beatmap set unchanged", () =>
+ AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
+ AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog.PerformOkAction());
+
+ AddUntilStep("wait for created", () =>
+ {
+ string difficultyName = Editor.ChildrenOfType().SingleOrDefault()?.BeatmapInfo.DifficultyName;
+ return difficultyName != null && difficultyName != "New Difficulty";
+ });
+ AddAssert("new difficulty has correct name", () => EditorBeatmap.BeatmapInfo.DifficultyName == "New Difficulty (1)");
+ AddAssert("new difficulty persisted", () =>
{
var set = beatmapManager.QueryBeatmapSet(s => s.ID == setId);
- return set != null && set.PerformRead(s => s.Beatmaps.Count == 1 && s.Files.Count == 1);
+ return set != null && set.PerformRead(s => s.Beatmaps.Count == 2 && s.Files.Count == 2);
});
}
[Test]
- public void TestCreateNewBeatmapFailsWithSameNamedDifficulties()
+ public void TestSavingBeatmapFailsWithSameNamedDifficulties([Values] bool sameRuleset)
{
Guid setId = Guid.Empty;
const string duplicate_difficulty_name = "duplicate";
@@ -168,7 +314,14 @@ namespace osu.Game.Tests.Visual.Editing
return set != null && set.PerformRead(s => s.Beatmaps.Count == 1 && s.Files.Count == 1);
});
- AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo));
+ AddStep("create new difficulty", () => Editor.CreateNewDifficulty(sameRuleset ? new OsuRuleset().RulesetInfo : new CatchRuleset().RulesetInfo));
+
+ if (sameRuleset)
+ {
+ AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
+ AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog.PerformOkAction());
+ }
+
AddUntilStep("wait for created", () =>
{
string difficultyName = Editor.ChildrenOfType().SingleOrDefault()?.BeatmapInfo.DifficultyName;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
index 157c248d69..d47ebf9f0d 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
@@ -39,7 +39,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Resolved]
private OsuGameBase game { get; set; }
- private TestSpectatorClient spectatorClient;
+ private TestSpectatorClient spectatorClient => dependenciesScreen.SpectatorClient;
+ private DependenciesScreen dependenciesScreen;
private SoloSpectator spectatorScreen;
private BeatmapSetInfo importedBeatmap;
@@ -48,16 +49,16 @@ namespace osu.Game.Tests.Visual.Gameplay
[SetUpSteps]
public void SetupSteps()
{
- DependenciesScreen dependenciesScreen = null;
-
AddStep("load dependencies", () =>
{
- spectatorClient = new TestSpectatorClient();
+ LoadScreen(dependenciesScreen = new DependenciesScreen());
- // The screen gets suspended so it stops receiving updates.
- Child = spectatorClient;
-
- LoadScreen(dependenciesScreen = new DependenciesScreen(spectatorClient));
+ // The dependencies screen gets suspended so it stops receiving updates. So its children are manually added to the test scene instead.
+ Children = new Drawable[]
+ {
+ dependenciesScreen.UserLookupCache,
+ dependenciesScreen.SpectatorClient,
+ };
});
AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded);
@@ -335,12 +336,10 @@ namespace osu.Game.Tests.Visual.Gameplay
private class DependenciesScreen : OsuScreen
{
[Cached(typeof(SpectatorClient))]
- public readonly TestSpectatorClient Client;
+ public readonly TestSpectatorClient SpectatorClient = new TestSpectatorClient();
- public DependenciesScreen(TestSpectatorClient client)
- {
- Client = client;
- }
+ [Cached(typeof(UserLookupCache))]
+ public readonly TestUserLookupCache UserLookupCache = new TestUserLookupCache();
}
}
}
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs
index e34ec6c46a..bbab6380ba 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs
@@ -19,6 +19,10 @@ namespace osu.Game.Tests.Visual.Menus
base.SetUpSteps();
AddAssert("no screen offset applied", () => Game.ScreenOffsetContainer.X == 0f);
+
+ // avoids mouse interacting with settings overlay.
+ AddStep("move mouse to centre", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre));
+
AddUntilStep("wait for overlays", () => Game.Settings.IsLoaded && Game.Notifications.IsLoaded);
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs
index c79395b343..99ecf02a92 100644
--- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs
@@ -10,7 +10,6 @@ using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
-using osu.Game.Database;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
@@ -39,10 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private TestMultiplayerComponents multiplayerComponents;
- protected TestMultiplayerClient Client => multiplayerComponents.Client;
-
- [Cached(typeof(UserLookupCache))]
- private UserLookupCache lookupCache = new TestUserLookupCache();
+ protected TestMultiplayerClient MultiplayerClient => multiplayerComponents.MultiplayerClient;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -75,10 +71,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
QueueMode = { Value = Mode },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(InitialBeatmap)
{
- Beatmap = { Value = InitialBeatmap },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
}));
@@ -88,21 +83,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
ClickButtonWhenEnabled();
- AddUntilStep("wait for join", () => Client.RoomJoined);
+ AddUntilStep("wait for join", () => MultiplayerClient.RoomJoined);
}
[Test]
public void TestCreatedWithCorrectMode()
{
- AddAssert("room created with correct mode", () => Client.APIRoom?.QueueMode.Value == Mode);
+ AddAssert("room created with correct mode", () => MultiplayerClient.APIRoom?.QueueMode.Value == Mode);
}
protected void RunGameplay()
{
- AddUntilStep("wait for idle", () => Client.LocalUser?.State == MultiplayerUserState.Idle);
+ AddUntilStep("wait for idle", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Idle);
ClickButtonWhenEnabled();
- AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready);
+ AddUntilStep("wait for ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready);
ClickButtonWhenEnabled();
AddUntilStep("wait for player", () => multiplayerComponents.CurrentScreen is Player player && player.IsLoaded);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs
index ad60ac824d..0785315b26 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs
@@ -31,19 +31,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestFirstItemSelectedByDefault()
{
- AddAssert("first item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID);
+ AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
}
[Test]
public void TestItemAddedToTheEndOfQueue()
{
addItem(() => OtherBeatmap);
- AddAssert("playlist has 2 items", () => Client.APIRoom?.Playlist.Count == 2);
+ AddAssert("playlist has 2 items", () => MultiplayerClient.APIRoom?.Playlist.Count == 2);
addItem(() => InitialBeatmap);
- AddAssert("playlist has 3 items", () => Client.APIRoom?.Playlist.Count == 3);
+ AddAssert("playlist has 3 items", () => MultiplayerClient.APIRoom?.Playlist.Count == 3);
- AddAssert("first item still selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID);
+ AddAssert("first item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
}
[Test]
@@ -51,9 +51,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
RunGameplay();
- AddAssert("playlist has only one item", () => Client.APIRoom?.Playlist.Count == 1);
- AddAssert("playlist item is expired", () => Client.APIRoom?.Playlist[0].Expired == true);
- AddAssert("last item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID);
+ AddAssert("playlist has only one item", () => MultiplayerClient.APIRoom?.Playlist.Count == 1);
+ AddAssert("playlist item is expired", () => MultiplayerClient.APIRoom?.Playlist[0].Expired == true);
+ AddAssert("last item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
}
[Test]
@@ -64,13 +64,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
RunGameplay();
- AddAssert("first item expired", () => Client.APIRoom?.Playlist[0].Expired == true);
- AddAssert("next item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[1].ID);
+ AddAssert("first item expired", () => MultiplayerClient.APIRoom?.Playlist[0].Expired == true);
+ AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[1].ID);
RunGameplay();
- AddAssert("second item expired", () => Client.APIRoom?.Playlist[1].Expired == true);
- AddAssert("next item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[2].ID);
+ AddAssert("second item expired", () => MultiplayerClient.APIRoom?.Playlist[1].Expired == true);
+ AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[2].ID);
}
[Test]
@@ -82,10 +82,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
// Move to the "other" beatmap.
RunGameplay();
- AddStep("change queue mode", () => Client.ChangeSettings(queueMode: QueueMode.HostOnly));
- AddAssert("playlist has 3 items", () => Client.APIRoom?.Playlist.Count == 3);
- AddAssert("item 2 is not expired", () => Client.APIRoom?.Playlist[1].Expired == false);
- AddAssert("current item is the other beatmap", () => Client.Room?.Settings.PlaylistItemId == 2);
+ AddStep("change queue mode", () => MultiplayerClient.ChangeSettings(queueMode: QueueMode.HostOnly));
+ AddAssert("playlist has 3 items", () => MultiplayerClient.APIRoom?.Playlist.Count == 3);
+ AddAssert("item 2 is not expired", () => MultiplayerClient.APIRoom?.Playlist[1].Expired == false);
+ AddAssert("current item is the other beatmap", () => MultiplayerClient.Room?.Settings.PlaylistItemId == 2);
}
[Test]
@@ -101,10 +101,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
addItem(() => OtherBeatmap, new CatchRuleset().RulesetInfo);
AddUntilStep("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID);
- AddUntilStep("wait for idle", () => Client.LocalUser?.State == MultiplayerUserState.Idle);
+ AddUntilStep("wait for idle", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Idle);
ClickButtonWhenEnabled();
- AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready);
+ AddUntilStep("wait for ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready);
ClickButtonWhenEnabled();
AddUntilStep("wait for player", () => CurrentScreen is Player player && player.IsLoaded);
@@ -118,10 +118,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
addItem(() => OtherBeatmap, mods: new Mod[] { new OsuModDoubleTime() });
AddUntilStep("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID);
- AddUntilStep("wait for idle", () => Client.LocalUser?.State == MultiplayerUserState.Idle);
+ AddUntilStep("wait for idle", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Idle);
ClickButtonWhenEnabled();
- AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready);
+ AddUntilStep("wait for ready", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready);
ClickButtonWhenEnabled();
AddUntilStep("wait for player", () => CurrentScreen is Player player && player.IsLoaded);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs
index 2f0398c6ef..0674fc7a39 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs
@@ -37,10 +37,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("end joining room", () => joiningRoomOperation.Dispose());
assertButtonEnableState(true);
- AddStep("disconnect client", () => Client.Disconnect());
+ AddStep("disconnect client", () => MultiplayerClient.Disconnect());
assertButtonEnableState(false);
- AddStep("re-connect client", () => Client.Connect());
+ AddStep("re-connect client", () => MultiplayerClient.Connect());
assertButtonEnableState(true);
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
index 423822cbe4..d8ec0ad1f0 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
@@ -53,19 +53,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
Type = { Value = MatchType.HeadToHead },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
{
- Beatmap =
+ BeatmapInfo =
{
- Value = new TestBeatmap(new OsuRuleset().RulesetInfo)
- {
- BeatmapInfo =
- {
- StarRating = 2.5
- }
- }.BeatmapInfo,
+ StarRating = 2.5
}
- }
+ }.BeatmapInfo)
}
}),
createLoungeRoom(new Room
@@ -76,26 +70,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
Type = { Value = MatchType.HeadToHead },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
{
- Beatmap =
+ BeatmapInfo =
{
- Value = new TestBeatmap(new OsuRuleset().RulesetInfo)
+ StarRating = 2.5,
+ Metadata =
{
- BeatmapInfo =
- {
- StarRating = 2.5,
- Metadata =
- {
- Artist = "very very very very very very very very very long artist",
- ArtistUnicode = "very very very very very very very very very long artist",
- Title = "very very very very very very very very very very very long title",
- TitleUnicode = "very very very very very very very very very very very long title",
- }
- }
- }.BeatmapInfo,
+ Artist = "very very very very very very very very very long artist",
+ ArtistUnicode = "very very very very very very very very very long artist",
+ Title = "very very very very very very very very very very very long title",
+ TitleUnicode = "very very very very very very very very very very very long title",
+ }
}
- }
+ }.BeatmapInfo)
}
}),
createLoungeRoom(new Room
@@ -105,32 +93,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
{
- Beatmap =
+ BeatmapInfo =
{
- Value = new TestBeatmap(new OsuRuleset().RulesetInfo)
- {
- BeatmapInfo =
- {
- StarRating = 2.5
- }
- }.BeatmapInfo,
+ StarRating = 2.5
}
- },
- new PlaylistItem
+ }.BeatmapInfo),
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
{
- Beatmap =
+ BeatmapInfo =
{
- Value = new TestBeatmap(new OsuRuleset().RulesetInfo)
- {
- BeatmapInfo =
- {
- StarRating = 4.5
- }
- }.BeatmapInfo,
+ StarRating = 4.5
}
- }
+ }.BeatmapInfo)
}
}),
createLoungeRoom(new Room
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs
index 5c8c90e166..4f01c14659 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs
@@ -17,6 +17,7 @@ using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics.Containers;
using osu.Game.Models;
+using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
@@ -29,16 +30,13 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Multiplayer
{
- public class TestSceneDrawableRoomPlaylist : OsuManualInputManagerTestScene
+ public class TestSceneDrawableRoomPlaylist : MultiplayerTestScene
{
private TestPlaylist playlist;
private BeatmapManager manager;
private RulesetStore rulesets;
- [Cached(typeof(UserLookupCache))]
- private readonly TestUserLookupCache userLookupCache = new TestUserLookupCache();
-
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
@@ -170,7 +168,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
assertDownloadButtonVisible(false);
void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}",
- () => playlist.ChildrenOfType().Single().Alpha == (visible ? 1 : 0));
+ () => playlist.ChildrenOfType().SingleOrDefault()?.Alpha == (visible ? 1 : 0));
}
[Test]
@@ -211,29 +209,27 @@ namespace osu.Game.Tests.Visual.Multiplayer
Size = new Vector2(500, 300),
Items =
{
- new PlaylistItem
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
ID = 0,
- Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
Expired = true,
- RequiredMods =
+ RequiredMods = new[]
{
- new OsuModHardRock(),
- new OsuModDoubleTime(),
- new OsuModAutoplay()
+ new APIMod(new OsuModHardRock()),
+ new APIMod(new OsuModDoubleTime()),
+ new APIMod(new OsuModAutoplay())
}
},
- new PlaylistItem
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
ID = 1,
- Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
- RequiredMods =
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ RequiredMods = new[]
{
- new OsuModHardRock(),
- new OsuModDoubleTime(),
- new OsuModAutoplay()
+ new APIMod(new OsuModHardRock()),
+ new APIMod(new OsuModDoubleTime()),
+ new APIMod(new OsuModAutoplay())
}
}
}
@@ -264,7 +260,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
private void moveToItem(int index, Vector2? offset = null)
- => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset));
+ => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset));
private void moveToDragger(int index, Vector2? offset = null) => AddStep($"move mouse to dragger {index}", () =>
{
@@ -295,31 +291,27 @@ namespace osu.Game.Tests.Visual.Multiplayer
for (int i = 0; i < 20; i++)
{
- playlist.Items.Add(new PlaylistItem
+ playlist.Items.Add(new PlaylistItem(i % 2 == 1
+ ? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo
+ : new BeatmapInfo
+ {
+ Metadata = new BeatmapMetadata
+ {
+ Artist = "Artist",
+ Author = new RealmUser { Username = "Creator name here" },
+ Title = "Long title used to check background colour",
+ },
+ BeatmapSet = new BeatmapSetInfo()
+ })
{
ID = i,
OwnerID = 2,
- Beatmap =
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ RequiredMods = new[]
{
- Value = i % 2 == 1
- ? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo
- : new BeatmapInfo
- {
- Metadata = new BeatmapMetadata
- {
- Artist = "Artist",
- Author = new RealmUser { Username = "Creator name here" },
- Title = "Long title used to check background colour",
- },
- BeatmapSet = new BeatmapSetInfo()
- }
- },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
- RequiredMods =
- {
- new OsuModHardRock(),
- new OsuModDoubleTime(),
- new OsuModAutoplay()
+ new APIMod(new OsuModHardRock()),
+ new APIMod(new OsuModDoubleTime()),
+ new APIMod(new OsuModAutoplay())
}
});
}
@@ -343,17 +335,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
foreach (var b in beatmaps())
{
- playlist.Items.Add(new PlaylistItem
+ playlist.Items.Add(new PlaylistItem(b)
{
ID = index++,
OwnerID = 2,
- Beatmap = { Value = b },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
- RequiredMods =
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ RequiredMods = new[]
{
- new OsuModHardRock(),
- new OsuModDoubleTime(),
- new OsuModAutoplay()
+ new APIMod(new OsuModHardRock()),
+ new APIMod(new OsuModDoubleTime()),
+ new APIMod(new OsuModAutoplay())
}
});
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs
index c7eeff81fe..c3ec7a5369 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Extensions;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
@@ -21,7 +22,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestFirstItemSelectedByDefault()
{
- AddAssert("first item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID);
+ AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
}
[Test]
@@ -29,7 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
selectNewItem(() => InitialBeatmap);
- AddAssert("playlist item still selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID);
+ AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
}
[Test]
@@ -37,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
selectNewItem(() => OtherBeatmap);
- AddAssert("playlist item still selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID);
+ AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
}
[Test]
@@ -45,10 +46,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
RunGameplay();
- AddAssert("playlist contains two items", () => Client.APIRoom?.Playlist.Count == 2);
- AddAssert("first playlist item expired", () => Client.APIRoom?.Playlist[0].Expired == true);
- AddAssert("second playlist item not expired", () => Client.APIRoom?.Playlist[1].Expired == false);
- AddAssert("second playlist item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[1].ID);
+ AddAssert("playlist contains two items", () => MultiplayerClient.APIRoom?.Playlist.Count == 2);
+ AddAssert("first playlist item expired", () => MultiplayerClient.APIRoom?.Playlist[0].Expired == true);
+ AddAssert("second playlist item not expired", () => MultiplayerClient.APIRoom?.Playlist[1].Expired == false);
+ AddAssert("second playlist item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[1].ID);
}
[Test]
@@ -57,23 +58,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
RunGameplay();
IBeatmapInfo firstBeatmap = null;
- AddStep("get first playlist item beatmap", () => firstBeatmap = Client.APIRoom?.Playlist[0].Beatmap.Value);
+ AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.APIRoom?.Playlist[0].Beatmap);
selectNewItem(() => OtherBeatmap);
- AddAssert("first playlist item hasn't changed", () => Client.APIRoom?.Playlist[0].Beatmap.Value == firstBeatmap);
- AddAssert("second playlist item changed", () => Client.APIRoom?.Playlist[1].Beatmap.Value != firstBeatmap);
+ AddAssert("first playlist item hasn't changed", () => MultiplayerClient.APIRoom?.Playlist[0].Beatmap == firstBeatmap);
+ AddAssert("second playlist item changed", () => MultiplayerClient.APIRoom?.Playlist[1].Beatmap != firstBeatmap);
}
[Test]
public void TestSettingsUpdatedWhenChangingQueueMode()
{
- AddStep("change queue mode", () => Client.ChangeSettings(new MultiplayerRoomSettings
+ AddStep("change queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings
{
QueueMode = QueueMode.AllPlayers
- }));
+ }).WaitSafely());
- AddUntilStep("api room updated", () => Client.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
+ AddUntilStep("api room updated", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
}
[Test]
@@ -81,7 +82,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
addItem(() => OtherBeatmap);
- AddAssert("playlist contains two items", () => Client.APIRoom?.Playlist.Count == 2);
+ AddAssert("playlist contains two items", () => MultiplayerClient.APIRoom?.Playlist.Count == 2);
}
private void selectNewItem(Func beatmap)
@@ -104,7 +105,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(otherBeatmap = beatmap()));
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
- AddUntilStep("selected item is new beatmap", () => (CurrentSubScreen as MultiplayerMatchSubScreen)?.SelectedItem.Value?.BeatmapID == otherBeatmap.OnlineID);
+ AddUntilStep("selected item is new beatmap", () => (CurrentSubScreen as MultiplayerMatchSubScreen)?.SelectedItem.Value?.Beatmap.OnlineID == otherBeatmap.OnlineID);
}
private void addItem(Func beatmap)
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index c3d5f7ec23..93cd281bc5 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -44,15 +44,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestBasicListChanges()
{
- AddStep("add rooms", () => RoomManager.AddRooms(3));
+ AddStep("add rooms", () => RoomManager.AddRooms(5, withSpotlightRooms: true));
- AddAssert("has 3 rooms", () => container.Rooms.Count == 3);
- AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.FirstOrDefault()));
- AddAssert("has 2 rooms", () => container.Rooms.Count == 2);
+ AddAssert("has 5 rooms", () => container.Rooms.Count == 5);
+
+ AddAssert("all spotlights at top", () => container.Rooms
+ .SkipWhile(r => r.Room.Category.Value == RoomCategory.Spotlight)
+ .All(r => r.Room.Category.Value == RoomCategory.Normal));
+
+ AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.FirstOrDefault(r => r.RoomID.Value == 0)));
+ AddAssert("has 4 rooms", () => container.Rooms.Count == 4);
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0));
AddStep("select first room", () => container.Rooms.First().TriggerClick());
- AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First()));
+ AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight)));
}
[Test]
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs
index 1d61a5d496..6f43511e8a 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs
@@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
+using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
@@ -31,16 +32,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void createNewItem()
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
ID = SelectedRoom.Value.Playlist.Count,
- Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
- RequiredMods =
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ RequiredMods = new[]
{
- new OsuModHardRock(),
- new OsuModDoubleTime(),
- new OsuModAutoplay()
+ new APIMod(new OsuModHardRock()),
+ new APIMod(new OsuModDoubleTime()),
+ new APIMod(new OsuModAutoplay())
}
});
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs
index 543e6a91d0..6b3573b3cb 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
foreach ((int userId, var _) in clocks)
{
SpectatorClient.StartPlay(userId, 0);
- OnlinePlayDependencies.Client.AddUser(new APIUser { Id = userId });
+ OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = userId });
}
});
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
index 56cb6036c7..7ce0c6a94d 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
@@ -60,8 +60,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("start players silently", () =>
{
- OnlinePlayDependencies.Client.AddUser(new APIUser { Id = PLAYER_1_ID }, true);
- OnlinePlayDependencies.Client.AddUser(new APIUser { Id = PLAYER_2_ID }, true);
+ OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = PLAYER_1_ID }, true);
+ OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = PLAYER_2_ID }, true);
playingUsers.Add(new MultiplayerRoomUser(PLAYER_1_ID));
playingUsers.Add(new MultiplayerRoomUser(PLAYER_2_ID));
@@ -121,13 +121,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("start players", () =>
{
- var player1 = OnlinePlayDependencies.Client.AddUser(new APIUser { Id = PLAYER_1_ID }, true);
+ var player1 = OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = PLAYER_1_ID }, true);
player1.MatchState = new TeamVersusUserState
{
TeamID = 0,
};
- var player2 = OnlinePlayDependencies.Client.AddUser(new APIUser { Id = PLAYER_2_ID }, true);
+ var player2 = OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = PLAYER_2_ID }, true);
player2.MatchState = new TeamVersusUserState
{
TeamID = 1,
@@ -396,7 +396,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
User = new APIUser { Id = id },
};
- OnlinePlayDependencies.Client.AddUser(user.User, true);
+ OnlinePlayDependencies.MultiplayerClient.AddUser(user.User, true);
SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId);
playingUsers.Add(user);
@@ -410,7 +410,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
var user = playingUsers.Single(u => u.UserID == userId);
- OnlinePlayDependencies.Client.RemoveUser(user.User.AsNonNull());
+ OnlinePlayDependencies.MultiplayerClient.RemoveUser(user.User.AsNonNull());
SpectatorClient.EndPlay(userId);
playingUsers.Remove(user);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
index 8f6ba6375f..211bcfeab2 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
@@ -17,8 +17,8 @@ using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
-using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
@@ -52,12 +52,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
private TestMultiplayerComponents multiplayerComponents;
- private TestMultiplayerClient client => multiplayerComponents.Client;
+ private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager;
- [Cached(typeof(UserLookupCache))]
- private UserLookupCache lookupCache = new TestUserLookupCache();
-
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
@@ -96,10 +93,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@@ -112,66 +108,66 @@ namespace osu.Game.Tests.Visual.Multiplayer
// all ready
AddUntilStep("all players ready", () =>
{
- var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
+ var nextUnready = multiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
if (nextUnready != null)
- client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
+ multiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
- return client.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true;
+ return multiplayerClient.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true;
});
AddStep("unready all players at once", () =>
{
- Debug.Assert(client.Room != null);
+ Debug.Assert(multiplayerClient.Room != null);
- foreach (var u in client.Room.Users) client.ChangeUserState(u.UserID, MultiplayerUserState.Idle);
+ foreach (var u in multiplayerClient.Room.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Idle);
});
AddStep("ready all players at once", () =>
{
- Debug.Assert(client.Room != null);
+ Debug.Assert(multiplayerClient.Room != null);
- foreach (var u in client.Room.Users) client.ChangeUserState(u.UserID, MultiplayerUserState.Ready);
+ foreach (var u in multiplayerClient.Room.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Ready);
});
}
private void addRandomPlayer()
{
int randomUser = RNG.Next(200000, 500000);
- client.AddUser(new APIUser { Id = randomUser, Username = $"user {randomUser}" });
+ multiplayerClient.AddUser(new APIUser { Id = randomUser, Username = $"user {randomUser}" });
}
private void removeLastUser()
{
- APIUser lastUser = client.Room?.Users.Last().User;
+ APIUser lastUser = multiplayerClient.Room?.Users.Last().User;
- if (lastUser == null || lastUser == client.LocalUser?.User)
+ if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User)
return;
- client.RemoveUser(lastUser);
+ multiplayerClient.RemoveUser(lastUser);
}
private void kickLastUser()
{
- APIUser lastUser = client.Room?.Users.Last().User;
+ APIUser lastUser = multiplayerClient.Room?.Users.Last().User;
- if (lastUser == null || lastUser == client.LocalUser?.User)
+ if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User)
return;
- client.KickUser(lastUser.Id);
+ multiplayerClient.KickUser(lastUser.Id);
}
private void markNextPlayerReady()
{
- var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
+ var nextUnready = multiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
if (nextUnready != null)
- client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
+ multiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
}
private void markNextPlayerIdle()
{
- var nextUnready = client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready);
+ var nextUnready = multiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready);
if (nextUnready != null)
- client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Idle);
+ multiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Idle);
}
private void performRandomAction()
@@ -221,7 +217,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("Press select", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true);
- AddUntilStep("wait for join", () => client.RoomJoined);
+ AddUntilStep("wait for join", () => multiplayerClient.RoomJoined);
}
[Test]
@@ -232,16 +228,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
- AddAssert("Check participant count correct", () => client.APIRoom?.ParticipantCount.Value == 1);
- AddAssert("Check participant list contains user", () => client.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
+ AddAssert("Check participant count correct", () => multiplayerClient.APIRoom?.ParticipantCount.Value == 1);
+ AddAssert("Check participant list contains user", () => multiplayerClient.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
}
[Test]
@@ -254,10 +249,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
}, API.LocalUser.Value);
@@ -284,10 +278,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
}, API.LocalUser.Value);
@@ -300,10 +293,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("join room", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true);
- AddUntilStep("wait for join", () => client.RoomJoined);
+ AddUntilStep("wait for join", () => multiplayerClient.RoomJoined);
- AddAssert("Check participant count correct", () => client.APIRoom?.ParticipantCount.Value == 1);
- AddAssert("Check participant list contains user", () => client.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
+ AddAssert("Check participant count correct", () => multiplayerClient.APIRoom?.ParticipantCount.Value == 1);
+ AddAssert("Check participant list contains user", () => multiplayerClient.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
}
[Test]
@@ -315,15 +308,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
Password = { Value = "password" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
- AddAssert("room has password", () => client.APIRoom?.Password.Value == "password");
+ AddAssert("room has password", () => multiplayerClient.APIRoom?.Password.Value == "password");
}
[Test]
@@ -337,10 +329,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Password = { Value = "password" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
}, API.LocalUser.Value);
@@ -358,7 +349,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick());
AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true);
- AddUntilStep("wait for join", () => client.RoomJoined);
+ AddUntilStep("wait for join", () => multiplayerClient.RoomJoined);
}
[Test]
@@ -370,16 +361,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
Password = { Value = "password" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
- AddStep("change password", () => client.ChangeSettings(password: "password2"));
- AddUntilStep("local password changed", () => client.APIRoom?.Password.Value == "password2");
+ AddStep("change password", () => multiplayerClient.ChangeSettings(password: "password2"));
+ AddUntilStep("local password changed", () => multiplayerClient.APIRoom?.Password.Value == "password2");
}
[Test]
@@ -390,10 +380,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@@ -401,7 +390,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
pressReadyButton();
AddStep("delete beatmap", () => beatmaps.Delete(importedSet));
- AddUntilStep("user state is idle", () => client.LocalUser?.State == MultiplayerUserState.Idle);
+ AddUntilStep("user state is idle", () => multiplayerClient.LocalUser?.State == MultiplayerUserState.Idle);
}
[Test]
@@ -412,10 +401,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@@ -425,22 +413,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("Enter song select", () =>
{
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
- ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.Room?.Settings.PlaylistItemId);
+ ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.Room?.Settings.PlaylistItemId);
});
AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true);
- AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == client.Room?.Playlist.First().BeatmapID);
+ AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.Room?.Playlist.First().BeatmapID);
AddStep("Select next beatmap", () => InputManager.Key(Key.Down));
- AddUntilStep("Beatmap doesn't match current item", () => Beatmap.Value.BeatmapInfo.OnlineID != client.Room?.Playlist.First().BeatmapID);
+ AddUntilStep("Beatmap doesn't match current item", () => Beatmap.Value.BeatmapInfo.OnlineID != multiplayerClient.Room?.Playlist.First().BeatmapID);
- AddStep("start match externally", () => client.StartMatch());
+ AddStep("start match externally", () => multiplayerClient.StartMatch());
AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player);
- AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == client.Room?.Playlist.First().BeatmapID);
+ AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.Room?.Playlist.First().BeatmapID);
}
[Test]
@@ -451,10 +439,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@@ -464,22 +451,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("Enter song select", () =>
{
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
- ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.Room?.Settings.PlaylistItemId);
+ ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.Room?.Settings.PlaylistItemId);
});
AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true);
- AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == client.Room?.Playlist.First().RulesetID);
+ AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.Room?.Playlist.First().RulesetID);
AddStep("Switch ruleset", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Ruleset.Value = new CatchRuleset().RulesetInfo);
- AddUntilStep("Ruleset doesn't match current item", () => Ruleset.Value.OnlineID != client.Room?.Playlist.First().RulesetID);
+ AddUntilStep("Ruleset doesn't match current item", () => Ruleset.Value.OnlineID != multiplayerClient.Room?.Playlist.First().RulesetID);
- AddStep("start match externally", () => client.StartMatch());
+ AddStep("start match externally", () => multiplayerClient.StartMatch());
AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player);
- AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == client.Room?.Playlist.First().RulesetID);
+ AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.Room?.Playlist.First().RulesetID);
}
[Test]
@@ -490,10 +477,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@@ -503,22 +489,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("Enter song select", () =>
{
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
- ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.Room?.Settings.PlaylistItemId);
+ ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.Room?.Settings.PlaylistItemId);
});
AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true);
- AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(client.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
+ AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
AddStep("Switch required mods", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Mods.Value = new Mod[] { new OsuModDoubleTime() });
- AddAssert("Mods don't match current item", () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(client.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
+ AddAssert("Mods don't match current item", () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
- AddStep("start match externally", () => client.StartMatch());
+ AddStep("start match externally", () => multiplayerClient.StartMatch());
AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player);
- AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(client.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
+ AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
}
[Test]
@@ -529,28 +515,27 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
AddStep("join other user (ready, host)", () =>
{
- client.AddUser(new APIUser { Id = MultiplayerTestScene.PLAYER_1_ID, Username = "Other" });
- client.TransferHost(MultiplayerTestScene.PLAYER_1_ID);
- client.ChangeUserState(MultiplayerTestScene.PLAYER_1_ID, MultiplayerUserState.Ready);
+ multiplayerClient.AddUser(new APIUser { Id = MultiplayerTestScene.PLAYER_1_ID, Username = "Other" });
+ multiplayerClient.TransferHost(MultiplayerTestScene.PLAYER_1_ID);
+ multiplayerClient.ChangeUserState(MultiplayerTestScene.PLAYER_1_ID, MultiplayerUserState.Ready);
});
AddStep("delete beatmap", () => beatmaps.Delete(importedSet));
ClickButtonWhenEnabled();
- AddUntilStep("wait for spectating user state", () => client.LocalUser?.State == MultiplayerUserState.Spectating);
+ AddUntilStep("wait for spectating user state", () => multiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating);
- AddStep("start match externally", () => client.StartMatch());
+ AddStep("start match externally", () => multiplayerClient.StartMatch());
AddAssert("play not started", () => multiplayerComponents.IsCurrentScreen());
}
@@ -563,10 +548,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@@ -575,16 +559,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("join other user (ready, host)", () =>
{
- client.AddUser(new APIUser { Id = MultiplayerTestScene.PLAYER_1_ID, Username = "Other" });
- client.TransferHost(MultiplayerTestScene.PLAYER_1_ID);
- client.ChangeUserState(MultiplayerTestScene.PLAYER_1_ID, MultiplayerUserState.Ready);
+ multiplayerClient.AddUser(new APIUser { Id = MultiplayerTestScene.PLAYER_1_ID, Username = "Other" });
+ multiplayerClient.TransferHost(MultiplayerTestScene.PLAYER_1_ID);
+ multiplayerClient.ChangeUserState(MultiplayerTestScene.PLAYER_1_ID, MultiplayerUserState.Ready);
});
ClickButtonWhenEnabled();
- AddUntilStep("wait for spectating user state", () => client.LocalUser?.State == MultiplayerUserState.Spectating);
+ AddUntilStep("wait for spectating user state", () => multiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating);
- AddStep("start match externally", () => client.StartMatch());
+ AddStep("start match externally", () => multiplayerClient.StartMatch());
AddStep("restore beatmap", () =>
{
@@ -603,15 +587,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
- AddStep("disconnect", () => client.Disconnect());
+ AddStep("disconnect", () => multiplayerClient.Disconnect());
AddUntilStep("back in lounge", () => this.ChildrenOfType().FirstOrDefault()?.IsCurrentScreen() == true);
}
@@ -623,11 +606,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
- AllowedMods = { new OsuModHidden() }
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ AllowedMods = new[] { new APIMod(new OsuModHidden()) }
}
}
});
@@ -663,10 +645,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
}
});
@@ -694,10 +675,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
QueueMode = { Value = QueueMode.AllPlayers },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
}
}, API.LocalUser.Value);
@@ -711,17 +691,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("change server-side settings", () =>
{
roomManager.ServerSideRooms[0].Name.Value = "New name";
- roomManager.ServerSideRooms[0].Playlist.Add(new PlaylistItem
+ roomManager.ServerSideRooms[0].Playlist.Add(new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
ID = 2,
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
});
});
AddStep("join room", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true);
- AddUntilStep("wait for join", () => client.RoomJoined);
+ AddUntilStep("wait for join", () => multiplayerClient.RoomJoined);
AddAssert("local room has correct settings", () =>
{
@@ -740,19 +719,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
QueueMode = { Value = QueueMode.AllPlayers },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
}
});
- AddStep("set spectating state", () => client.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating));
- AddUntilStep("state set to spectating", () => client.LocalUser?.State == MultiplayerUserState.Spectating);
+ AddStep("set spectating state", () => multiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating));
+ AddUntilStep("state set to spectating", () => multiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating);
- AddStep("join other user", () => client.AddUser(new APIUser { Id = 1234 }));
- AddStep("set other user ready", () => client.ChangeUserState(1234, MultiplayerUserState.Ready));
+ AddStep("join other user", () => multiplayerClient.AddUser(new APIUser { Id = 1234 }));
+ AddStep("set other user ready", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Ready));
pressReadyButton(1234);
AddUntilStep("wait for gameplay", () => (multiplayerComponents.CurrentScreen as MultiSpectatorScreen)?.IsLoaded == true);
@@ -764,7 +742,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
AddUntilStep("wait for return to match subscreen", () => multiplayerComponents.MultiplayerScreen.IsCurrentScreen());
- AddUntilStep("user state is idle", () => client.LocalUser?.State == MultiplayerUserState.Idle);
+ AddUntilStep("user state is idle", () => multiplayerClient.LocalUser?.State == MultiplayerUserState.Idle);
}
[Test]
@@ -776,24 +754,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
QueueMode = { Value = QueueMode.AllPlayers },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
}
});
- AddStep("set spectating state", () => client.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating));
- AddUntilStep("state set to spectating", () => client.LocalUser?.State == MultiplayerUserState.Spectating);
+ AddStep("set spectating state", () => multiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating));
+ AddUntilStep("state set to spectating", () => multiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating);
- AddStep("join other user", () => client.AddUser(new APIUser { Id = 1234 }));
- AddStep("set other user ready", () => client.ChangeUserState(1234, MultiplayerUserState.Ready));
+ AddStep("join other user", () => multiplayerClient.AddUser(new APIUser { Id = 1234 }));
+ AddStep("set other user ready", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Ready));
pressReadyButton(1234);
AddUntilStep("wait for gameplay", () => (multiplayerComponents.CurrentScreen as MultiSpectatorScreen)?.IsLoaded == true);
- AddStep("set other user loaded", () => client.ChangeUserState(1234, MultiplayerUserState.Loaded));
- AddStep("set other user finished play", () => client.ChangeUserState(1234, MultiplayerUserState.FinishedPlay));
+ AddStep("set other user loaded", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Loaded));
+ AddStep("set other user finished play", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.FinishedPlay));
AddStep("press back button and exit", () =>
{
@@ -803,7 +780,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for return to match subscreen", () => multiplayerComponents.MultiplayerScreen.IsCurrentScreen());
AddWaitStep("wait for possible state change", 5);
- AddUntilStep("user state is spectating", () => client.LocalUser?.State == MultiplayerUserState.Spectating);
+ AddUntilStep("user state is spectating", () => multiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating);
}
[Test]
@@ -815,23 +792,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
QueueMode = { Value = QueueMode.AllPlayers },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
}
});
enterGameplay();
+ AddStep("join other user", () => multiplayerClient.AddUser(new APIUser { Id = 1234 }));
+ AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem(
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ })).WaitSafely());
- AddStep("join other user", () => client.AddUser(new APIUser { Id = 1234 }));
- AddStep("add item as other user", () => client.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem(new PlaylistItem
- {
- BeatmapID = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo.OnlineID
- })));
-
- AddUntilStep("item arrived in playlist", () => client.Room?.Playlist.Count == 2);
+ AddUntilStep("item arrived in playlist", () => multiplayerClient.Room?.Playlist.Count == 2);
AddStep("exit gameplay as initial user", () => multiplayerComponents.MultiplayerScreen.MakeCurrent());
AddUntilStep("queue contains item", () => this.ChildrenOfType().Single().Items.Single().ID == 2);
@@ -846,26 +822,26 @@ namespace osu.Game.Tests.Visual.Multiplayer
QueueMode = { Value = QueueMode.AllPlayers },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
}
});
enterGameplay();
- AddStep("join other user", () => client.AddUser(new APIUser { Id = 1234 }));
- AddStep("add item as other user", () => client.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem(new PlaylistItem
- {
- BeatmapID = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo.OnlineID
- })));
+ AddStep("join other user", () => multiplayerClient.AddUser(new APIUser { Id = 1234 }));
+ AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem(
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ })).WaitSafely());
- AddUntilStep("item arrived in playlist", () => client.Room?.Playlist.Count == 2);
+ AddUntilStep("item arrived in playlist", () => multiplayerClient.Room?.Playlist.Count == 2);
- AddStep("delete item as other user", () => client.RemoveUserPlaylistItem(1234, 2));
- AddUntilStep("item removed from playlist", () => client.Room?.Playlist.Count == 1);
+ AddStep("delete item as other user", () => multiplayerClient.RemoveUserPlaylistItem(1234, 2).WaitSafely());
+ AddUntilStep("item removed from playlist", () => multiplayerClient.Room?.Playlist.Count == 1);
AddStep("exit gameplay as initial user", () => multiplayerComponents.MultiplayerScreen.MakeCurrent());
AddUntilStep("queue is empty", () => this.ChildrenOfType().Single().Items.Count == 0);
@@ -879,27 +855,26 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
AddStep("join other user and make host", () =>
{
- client.AddUser(new APIUser { Id = 1234 });
- client.TransferHost(1234);
+ multiplayerClient.AddUser(new APIUser { Id = 1234 });
+ multiplayerClient.TransferHost(1234);
});
- AddStep("set local user spectating", () => client.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating));
- AddUntilStep("wait for spectating state", () => client.LocalUser?.State == MultiplayerUserState.Spectating);
+ AddStep("set local user spectating", () => multiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating));
+ AddUntilStep("wait for spectating state", () => multiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating);
runGameplay();
- AddStep("exit gameplay for other user", () => client.ChangeUserState(1234, MultiplayerUserState.Idle));
- AddUntilStep("wait for room to be idle", () => client.Room?.State == MultiplayerRoomState.Open);
+ AddStep("exit gameplay for other user", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Idle));
+ AddUntilStep("wait for room to be idle", () => multiplayerClient.Room?.State == MultiplayerRoomState.Open);
runGameplay();
@@ -907,13 +882,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("start match by other user", () =>
{
- client.ChangeUserState(1234, MultiplayerUserState.Ready);
- client.StartMatch();
+ multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Ready);
+ multiplayerClient.StartMatch();
});
- AddUntilStep("wait for loading", () => client.Room?.State == MultiplayerRoomState.WaitingForLoad);
- AddStep("set player loaded", () => client.ChangeUserState(1234, MultiplayerUserState.Loaded));
- AddUntilStep("wait for gameplay to start", () => client.Room?.State == MultiplayerRoomState.Playing);
+ AddUntilStep("wait for loading", () => multiplayerClient.Room?.State == MultiplayerRoomState.WaitingForLoad);
+ AddStep("set player loaded", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Loaded));
+ AddUntilStep("wait for gameplay to start", () => multiplayerClient.Room?.State == MultiplayerRoomState.Playing);
AddUntilStep("wait for local user to enter spectator", () => multiplayerComponents.CurrentScreen is MultiSpectatorScreen);
}
}
@@ -938,7 +913,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("click ready button", () =>
{
- user = playingUserId == null ? client.LocalUser : client.Room?.Users.Single(u => u.UserID == playingUserId);
+ user = playingUserId == null ? multiplayerClient.LocalUser : multiplayerClient.Room?.Users.Single(u => u.UserID == playingUserId);
lastState = user?.State ?? MultiplayerUserState.Idle;
InputManager.MoveMouseTo(readyButton);
@@ -958,7 +933,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
ClickButtonWhenEnabled();
- AddUntilStep("wait for join", () => client.RoomJoined);
+ AddUntilStep("wait for join", () => multiplayerClient.RoomJoined);
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs
index 1322fbc96e..6605f82d5c 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
base.SetUpSteps();
- AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).GetResultSafely());
+ AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = UserLookupCache.GetUserAsync(1).GetResultSafely());
AddStep("create leaderboard", () =>
{
@@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
foreach (int user in users)
{
SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineID);
- multiplayerUsers.Add(OnlinePlayDependencies.Client.AddUser(new APIUser { Id = user }, true));
+ multiplayerUsers.Add(OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = user }, true));
}
Children = new Drawable[]
@@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
AddUntilStep("wait for load", () => leaderboard.IsLoaded);
- AddUntilStep("wait for user population", () => Client.CurrentMatchPlayingUserIds.Count > 0);
+ AddUntilStep("wait for user population", () => MultiplayerClient.CurrentMatchPlayingUserIds.Count > 0);
}
[Test]
@@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestUserQuit()
{
foreach (int user in users)
- AddStep($"mark user {user} quit", () => Client.RemoveUser(LookupCache.GetUserAsync(user).GetResultSafely().AsNonNull()));
+ AddStep($"mark user {user} quit", () => MultiplayerClient.RemoveUser(UserLookupCache.GetUserAsync(user).GetResultSafely().AsNonNull()));
}
[Test]
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs
index 8a78c12042..dabc1c1e5a 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
base.SetUpSteps();
- AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).GetResultSafely());
+ AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = UserLookupCache.GetUserAsync(1).GetResultSafely());
AddStep("create leaderboard", () =>
{
@@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
foreach (int user in users)
{
SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineID);
- var roomUser = OnlinePlayDependencies.Client.AddUser(new APIUser { Id = user }, true);
+ var roomUser = OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = user }, true);
roomUser.MatchState = new TeamVersusUserState
{
@@ -105,7 +105,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
AddUntilStep("wait for load", () => leaderboard.IsLoaded);
- AddUntilStep("wait for user population", () => Client.CurrentMatchPlayingUserIds.Count > 0);
+ AddUntilStep("wait for user population", () => MultiplayerClient.CurrentMatchPlayingUserIds.Count > 0);
}
[Test]
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
index 869fb17317..12c1757c86 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
@@ -10,14 +10,18 @@ using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
+using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.UI;
+using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
@@ -69,10 +73,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
- Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@@ -86,11 +89,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo)
{
- Beatmap = { Value = new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo },
- Ruleset = { Value = new TaikoRuleset().RulesetInfo },
- AllowedMods = { new TaikoModSwap() }
+ RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
+ AllowedMods = new[] { new APIMod(new TaikoModSwap()) }
});
});
@@ -98,7 +100,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for join", () => RoomJoined);
- AddStep("select swap mod", () => Client.ChangeUserMods(API.LocalUser.Value.OnlineID, new[] { new TaikoModSwap() }));
+ AddStep("select swap mod", () => MultiplayerClient.ChangeUserMods(API.LocalUser.Value.OnlineID, new[] { new TaikoModSwap() }));
AddUntilStep("participant panel has mod", () => this.ChildrenOfType().Any(p => p.ChildrenOfType().Any(m => m.Mod is TaikoModSwap)));
}
@@ -109,10 +111,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set playlist", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
- Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@@ -124,10 +125,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("set playlist", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@@ -137,17 +137,39 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("join other user (ready)", () =>
{
- Client.AddUser(new APIUser { Id = PLAYER_1_ID });
- Client.ChangeUserState(PLAYER_1_ID, MultiplayerUserState.Ready);
+ MultiplayerClient.AddUser(new APIUser { Id = PLAYER_1_ID });
+ MultiplayerClient.ChangeUserState(PLAYER_1_ID, MultiplayerUserState.Ready);
});
ClickButtonWhenEnabled();
- AddUntilStep("wait for spectating user state", () => Client.LocalUser?.State == MultiplayerUserState.Spectating);
+ AddUntilStep("wait for spectating user state", () => MultiplayerClient.LocalUser?.State == MultiplayerUserState.Spectating);
ClickButtonWhenEnabled();
- AddUntilStep("match started", () => Client.Room?.State == MultiplayerRoomState.WaitingForLoad);
+ AddUntilStep("match started", () => MultiplayerClient.Room?.State == MultiplayerRoomState.WaitingForLoad);
+ }
+
+ [Test]
+ public void TestFreeModSelectionHasAllowedMods()
+ {
+ AddStep("add playlist item with allowed mod", () =>
+ {
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
+ });
+ });
+
+ ClickButtonWhenEnabled();
+
+ AddUntilStep("wait for join", () => RoomJoined);
+
+ ClickButtonWhenEnabled();
+
+ AddUntilStep("mod select contains only double time mod",
+ () => this.ChildrenOfType().SingleOrDefault()?.ChildrenOfType().SingleOrDefault()?.Mod is OsuModDoubleTime);
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs
index 671b85164b..292319171d 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddAssert("one unique panel", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 1);
- AddStep("add user", () => Client.AddUser(new APIUser
+ AddStep("add user", () => MultiplayerClient.AddUser(new APIUser
{
Id = 3,
Username = "Second",
@@ -50,15 +50,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddAssert("one unique panel", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 1);
- AddStep("add non-resolvable user", () => Client.TestAddUnresolvedUser());
- AddAssert("null user added", () => Client.Room.AsNonNull().Users.Count(u => u.User == null) == 1);
+ AddStep("add non-resolvable user", () => MultiplayerClient.TestAddUnresolvedUser());
+ AddAssert("null user added", () => MultiplayerClient.Room.AsNonNull().Users.Count(u => u.User == null) == 1);
AddUntilStep("two unique panels", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 2);
AddStep("kick null user", () => this.ChildrenOfType().Single(p => p.User.User == null)
.ChildrenOfType().Single().TriggerClick());
- AddAssert("null user kicked", () => Client.Room.AsNonNull().Users.Count == 1);
+ AddAssert("null user kicked", () => MultiplayerClient.Room.AsNonNull().Users.Count == 1);
}
[Test]
@@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add a user", () =>
{
- Client.AddUser(secondUser = new APIUser
+ MultiplayerClient.AddUser(secondUser = new APIUser
{
Id = 3,
Username = "Second",
@@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
});
- AddStep("remove host", () => Client.RemoveUser(API.LocalUser.Value));
+ AddStep("remove host", () => MultiplayerClient.RemoveUser(API.LocalUser.Value));
AddAssert("single panel is for second user", () => this.ChildrenOfType().Single().User.User == secondUser);
}
@@ -84,21 +84,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestGameStateHasPriorityOverDownloadState()
{
- AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
+ AddStep("set to downloading map", () => MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
checkProgressBarVisibility(true);
- AddStep("make user ready", () => Client.ChangeState(MultiplayerUserState.Results));
+ AddStep("make user ready", () => MultiplayerClient.ChangeState(MultiplayerUserState.Results));
checkProgressBarVisibility(false);
AddUntilStep("ready mark visible", () => this.ChildrenOfType().Single().IsPresent);
- AddStep("make user ready", () => Client.ChangeState(MultiplayerUserState.Idle));
+ AddStep("make user ready", () => MultiplayerClient.ChangeState(MultiplayerUserState.Idle));
checkProgressBarVisibility(true);
}
[Test]
public void TestCorrectInitialState()
{
- AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
+ AddStep("set to downloading map", () => MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
createNewParticipantsList();
checkProgressBarVisibility(true);
}
@@ -106,23 +106,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestBeatmapDownloadingStates()
{
- AddStep("set to no map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.NotDownloaded()));
- AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
+ AddStep("set to no map", () => MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.NotDownloaded()));
+ AddStep("set to downloading map", () => MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
checkProgressBarVisibility(true);
AddRepeatStep("increment progress", () =>
{
float progress = this.ChildrenOfType().Single().User.BeatmapAvailability.DownloadProgress ?? 0;
- Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(progress + RNG.NextSingle(0.1f)));
+ MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(progress + RNG.NextSingle(0.1f)));
}, 25);
AddAssert("progress bar increased", () => this.ChildrenOfType().Single().Current.Value > 0);
- AddStep("set to importing map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Importing()));
+ AddStep("set to importing map", () => MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.Importing()));
checkProgressBarVisibility(false);
- AddStep("set to available", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.LocallyAvailable()));
+ AddStep("set to available", () => MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.LocallyAvailable()));
}
[Test]
@@ -130,24 +130,24 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddAssert("ready mark invisible", () => !this.ChildrenOfType().Single().IsPresent);
- AddStep("make user ready", () => Client.ChangeState(MultiplayerUserState.Ready));
+ AddStep("make user ready", () => MultiplayerClient.ChangeState(MultiplayerUserState.Ready));
AddUntilStep("ready mark visible", () => this.ChildrenOfType().Single().IsPresent);
- AddStep("make user idle", () => Client.ChangeState(MultiplayerUserState.Idle));
+ AddStep("make user idle", () => MultiplayerClient.ChangeState(MultiplayerUserState.Idle));
AddUntilStep("ready mark invisible", () => !this.ChildrenOfType().Single().IsPresent);
}
[Test]
public void TestToggleSpectateState()
{
- AddStep("make user spectating", () => Client.ChangeState(MultiplayerUserState.Spectating));
- AddStep("make user idle", () => Client.ChangeState(MultiplayerUserState.Idle));
+ AddStep("make user spectating", () => MultiplayerClient.ChangeState(MultiplayerUserState.Spectating));
+ AddStep("make user idle", () => MultiplayerClient.ChangeState(MultiplayerUserState.Idle));
}
[Test]
public void TestCrownChangesStateWhenHostTransferred()
{
- AddStep("add user", () => Client.AddUser(new APIUser
+ AddStep("add user", () => MultiplayerClient.AddUser(new APIUser
{
Id = 3,
Username = "Second",
@@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("first user crown visible", () => this.ChildrenOfType().ElementAt(0).ChildrenOfType().First().Alpha == 1);
AddUntilStep("second user crown hidden", () => this.ChildrenOfType().ElementAt(1).ChildrenOfType().First().Alpha == 0);
- AddStep("make second user host", () => Client.TransferHost(3));
+ AddStep("make second user host", () => MultiplayerClient.TransferHost(3));
AddUntilStep("first user crown hidden", () => this.ChildrenOfType().ElementAt(0).ChildrenOfType().First().Alpha == 0);
AddUntilStep("second user crown visible", () => this.ChildrenOfType().ElementAt(1).ChildrenOfType().First().Alpha == 1);
@@ -166,7 +166,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestKickButtonOnlyPresentWhenHost()
{
- AddStep("add user", () => Client.AddUser(new APIUser
+ AddStep("add user", () => MultiplayerClient.AddUser(new APIUser
{
Id = 3,
Username = "Second",
@@ -175,11 +175,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("kick buttons visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 1);
- AddStep("make second user host", () => Client.TransferHost(3));
+ AddStep("make second user host", () => MultiplayerClient.TransferHost(3));
AddUntilStep("kick buttons not visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 0);
- AddStep("make local user host again", () => Client.TransferHost(API.LocalUser.Value.Id));
+ AddStep("make local user host again", () => MultiplayerClient.TransferHost(API.LocalUser.Value.Id));
AddUntilStep("kick buttons visible", () => this.ChildrenOfType().Count(d => d.IsPresent) == 1);
}
@@ -187,7 +187,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestKickButtonKicks()
{
- AddStep("add user", () => Client.AddUser(new APIUser
+ AddStep("add user", () => MultiplayerClient.AddUser(new APIUser
{
Id = 3,
Username = "Second",
@@ -196,7 +196,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("kick second user", () => this.ChildrenOfType().Single(d => d.IsPresent).TriggerClick());
- AddAssert("second user kicked", () => Client.Room?.Users.Single().UserID == API.LocalUser.Value.Id);
+ AddAssert("second user kicked", () => MultiplayerClient.Room?.Users.Single().UserID == API.LocalUser.Value.Id);
}
[Test]
@@ -206,7 +206,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
for (int i = 0; i < 20; i++)
{
- Client.AddUser(new APIUser
+ MultiplayerClient.AddUser(new APIUser
{
Id = i,
Username = $"User {i}",
@@ -220,7 +220,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
});
- Client.ChangeUserState(i, (MultiplayerUserState)RNG.Next(0, (int)MultiplayerUserState.Results + 1));
+ MultiplayerClient.ChangeUserState(i, (MultiplayerUserState)RNG.Next(0, (int)MultiplayerUserState.Results + 1));
if (RNG.NextBool())
{
@@ -229,15 +229,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
switch (beatmapState)
{
case DownloadState.NotDownloaded:
- Client.ChangeUserBeatmapAvailability(i, BeatmapAvailability.NotDownloaded());
+ MultiplayerClient.ChangeUserBeatmapAvailability(i, BeatmapAvailability.NotDownloaded());
break;
case DownloadState.Downloading:
- Client.ChangeUserBeatmapAvailability(i, BeatmapAvailability.Downloading(RNG.NextSingle()));
+ MultiplayerClient.ChangeUserBeatmapAvailability(i, BeatmapAvailability.Downloading(RNG.NextSingle()));
break;
case DownloadState.Importing:
- Client.ChangeUserBeatmapAvailability(i, BeatmapAvailability.Importing());
+ MultiplayerClient.ChangeUserBeatmapAvailability(i, BeatmapAvailability.Importing());
break;
}
}
@@ -250,7 +250,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add user", () =>
{
- Client.AddUser(new APIUser
+ MultiplayerClient.AddUser(new APIUser
{
Id = 0,
Username = "User 0",
@@ -264,7 +264,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
});
- Client.ChangeUserMods(0, new Mod[]
+ MultiplayerClient.ChangeUserMods(0, new Mod[]
{
new OsuModHardRock(),
new OsuModDifficultyAdjust { ApproachRate = { Value = 1 } }
@@ -274,12 +274,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
for (var i = MultiplayerUserState.Idle; i < MultiplayerUserState.Results; i++)
{
var state = i;
- AddStep($"set state: {state}", () => Client.ChangeUserState(0, state));
+ AddStep($"set state: {state}", () => MultiplayerClient.ChangeUserState(0, state));
}
- AddStep("set state: downloading", () => Client.ChangeUserBeatmapAvailability(0, BeatmapAvailability.Downloading(0)));
+ AddStep("set state: downloading", () => MultiplayerClient.ChangeUserBeatmapAvailability(0, BeatmapAvailability.Downloading(0)));
- AddStep("set state: locally available", () => Client.ChangeUserBeatmapAvailability(0, BeatmapAvailability.LocallyAvailable()));
+ AddStep("set state: locally available", () => MultiplayerClient.ChangeUserBeatmapAvailability(0, BeatmapAvailability.LocallyAvailable()));
}
[Test]
@@ -287,7 +287,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add dummy mods", () =>
{
- Client.ChangeUserMods(new Mod[]
+ MultiplayerClient.ChangeUserMods(new Mod[]
{
new OsuModNoFail(),
new OsuModDoubleTime()
@@ -296,7 +296,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add user with mods", () =>
{
- Client.AddUser(new APIUser
+ MultiplayerClient.AddUser(new APIUser
{
Id = 0,
Username = "Baka",
@@ -309,34 +309,34 @@ namespace osu.Game.Tests.Visual.Multiplayer
},
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
});
- Client.ChangeUserMods(0, new Mod[]
+ MultiplayerClient.ChangeUserMods(0, new Mod[]
{
new OsuModHardRock(),
new OsuModDoubleTime()
});
});
- AddStep("set 0 ready", () => Client.ChangeState(MultiplayerUserState.Ready));
+ AddStep("set 0 ready", () => MultiplayerClient.ChangeState(MultiplayerUserState.Ready));
- AddStep("set 1 spectate", () => Client.ChangeUserState(0, MultiplayerUserState.Spectating));
+ AddStep("set 1 spectate", () => MultiplayerClient.ChangeUserState(0, MultiplayerUserState.Spectating));
// Have to set back to idle due to status priority.
AddStep("set 0 no map, 1 ready", () =>
{
- Client.ChangeState(MultiplayerUserState.Idle);
- Client.ChangeBeatmapAvailability(BeatmapAvailability.NotDownloaded());
- Client.ChangeUserState(0, MultiplayerUserState.Ready);
+ MultiplayerClient.ChangeState(MultiplayerUserState.Idle);
+ MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.NotDownloaded());
+ MultiplayerClient.ChangeUserState(0, MultiplayerUserState.Ready);
});
- AddStep("set 0 downloading", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
+ AddStep("set 0 downloading", () => MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
- AddStep("set 0 spectate", () => Client.ChangeUserState(0, MultiplayerUserState.Spectating));
+ AddStep("set 0 spectate", () => MultiplayerClient.ChangeUserState(0, MultiplayerUserState.Spectating));
AddStep("make both default", () =>
{
- Client.ChangeBeatmapAvailability(BeatmapAvailability.LocallyAvailable());
- Client.ChangeUserState(0, MultiplayerUserState.Idle);
- Client.ChangeState(MultiplayerUserState.Idle);
+ MultiplayerClient.ChangeBeatmapAvailability(BeatmapAvailability.LocallyAvailable());
+ MultiplayerClient.ChangeUserState(0, MultiplayerUserState.Idle);
+ MultiplayerClient.ChangeState(MultiplayerUserState.Idle);
});
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
index 73f2ed5b39..312281ac18 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
@@ -28,15 +28,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("initialise gameplay", () =>
{
- Stack.Push(player = new MultiplayerPlayer(Client.APIRoom, new PlaylistItem
+ Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.APIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo)
{
- Beatmap = { Value = Beatmap.Value.BeatmapInfo },
- Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset }
- }, Client.Room?.Users.ToArray()));
+ RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID,
+ }, MultiplayerClient.Room?.Users.ToArray()));
});
AddUntilStep("wait for player to be current", () => player.IsCurrentScreen() && player.IsLoaded);
- AddStep("start gameplay", () => ((IMultiplayerClient)Client).MatchStarted());
+ AddStep("start gameplay", () => ((IMultiplayerClient)MultiplayerClient).MatchStarted());
}
[Test]
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
index 936798e6b4..2b53e7ca87 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
importedBeatmap = importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0);
});
- AddStep("change to all players mode", () => Client.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }));
+ AddStep("change to all players mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
}
[Test]
@@ -97,19 +97,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
addItemStep();
addItemStep();
- AddStep("finish current item", () => Client.FinishCurrentItem());
+ AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
assertItemInHistoryListStep(1, 0);
assertItemInQueueListStep(2, 0);
assertItemInQueueListStep(3, 1);
- AddStep("finish current item", () => Client.FinishCurrentItem());
+ AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
assertItemInHistoryListStep(2, 0);
assertItemInHistoryListStep(1, 1);
assertItemInQueueListStep(3, 0);
- AddStep("finish current item", () => Client.FinishCurrentItem());
+ AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
assertItemInHistoryListStep(3, 0);
assertItemInHistoryListStep(2, 1);
@@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestListsClearedWhenRoomLeft()
{
addItemStep();
- AddStep("finish current item", () => Client.FinishCurrentItem());
+ AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
AddStep("leave room", () => RoomManager.PartRoom());
AddUntilStep("wait for room part", () => !RoomJoined);
@@ -143,15 +143,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "test name" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
{
- Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
- Ruleset = { Value = Ruleset.Value }
+ RulesetID = Ruleset.Value.OnlineID
},
- new PlaylistItem
+ new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
{
- Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
- Ruleset = { Value = Ruleset.Value },
+ RulesetID = Ruleset.Value.OnlineID,
Expired = true
}
}
@@ -167,10 +165,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
///
/// Adds a step to create a new playlist item.
///
- private void addItemStep(bool expired = false) => AddStep("add item", () => Client.AddPlaylistItem(new MultiplayerPlaylistItem(new PlaylistItem
+ private void addItemStep(bool expired = false) => AddStep("add item", () => MultiplayerClient.AddPlaylistItem(new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap)
{
- Beatmap = { Value = importedBeatmap },
- BeatmapID = importedBeatmap.OnlineID,
Expired = expired,
PlayedAt = DateTimeOffset.Now
})));
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs
index ddf794b437..be9b317788 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs
@@ -12,7 +12,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
-using osu.Game.Database;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
@@ -26,9 +25,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMultiplayerQueueList : MultiplayerTestScene
{
- [Cached(typeof(UserLookupCache))]
- private readonly TestUserLookupCache userLookupCache = new TestUserLookupCache();
-
private MultiplayerQueueList playlist;
private BeatmapManager beatmaps;
private RulesetStore rulesets;
@@ -54,7 +50,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 300),
- Items = { BindTarget = Client.APIRoom!.Playlist }
+ Items = { BindTarget = MultiplayerClient.APIRoom!.Playlist }
};
});
@@ -65,14 +61,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
importedBeatmap = importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0);
});
- AddStep("change to all players mode", () => Client.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }));
+ AddStep("change to all players mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
}
[Test]
public void TestDeleteButtonAlwaysVisibleForHost()
{
- AddStep("set all players queue mode", () => Client.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }));
- AddUntilStep("wait for queue mode change", () => Client.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
+ AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
+ AddUntilStep("wait for queue mode change", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
assertDeleteButtonVisibility(1, true);
@@ -83,18 +79,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestDeleteButtonOnlyVisibleForItemOwnerIfNotHost()
{
- AddStep("set all players queue mode", () => Client.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }));
- AddUntilStep("wait for queue mode change", () => Client.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
+ AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
+ AddUntilStep("wait for queue mode change", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
- AddStep("join other user", () => Client.AddUser(new APIUser { Id = 1234 }));
- AddStep("set other user as host", () => Client.TransferHost(1234));
+ AddStep("join other user", () => MultiplayerClient.AddUser(new APIUser { Id = 1234 }));
+ AddStep("set other user as host", () => MultiplayerClient.TransferHost(1234));
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
assertDeleteButtonVisibility(1, true);
addPlaylistItem(() => 1234);
assertDeleteButtonVisibility(2, false);
- AddStep("set local user as host", () => Client.TransferHost(API.LocalUser.Value.OnlineID));
+ AddStep("set local user as host", () => MultiplayerClient.TransferHost(API.LocalUser.Value.OnlineID));
assertDeleteButtonVisibility(1, true);
assertDeleteButtonVisibility(2, true);
}
@@ -102,16 +98,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestCurrentItemDoesNotHaveDeleteButton()
{
- AddStep("set all players queue mode", () => Client.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }));
- AddUntilStep("wait for queue mode change", () => Client.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
+ AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
+ AddUntilStep("wait for queue mode change", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
assertDeleteButtonVisibility(0, false);
assertDeleteButtonVisibility(1, true);
- AddStep("finish current item", () => Client.FinishCurrentItem());
- AddUntilStep("wait for next item to be selected", () => Client.Room?.Settings.PlaylistItemId == 2);
+ AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
+ AddUntilStep("wait for next item to be selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == 2);
AddUntilStep("wait for two items in playlist", () => playlist.ChildrenOfType().Count() == 2);
assertDeleteButtonVisibility(0, false);
@@ -124,13 +120,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add playlist item", () =>
{
- MultiplayerPlaylistItem item = new MultiplayerPlaylistItem(new PlaylistItem
- {
- Beatmap = { Value = importedBeatmap },
- BeatmapID = importedBeatmap.OnlineID,
- });
+ MultiplayerPlaylistItem item = new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap));
- Client.AddUserPlaylistItem(userId(), item);
+ MultiplayerClient.AddUserPlaylistItem(userId(), item).WaitSafely();
itemId = item.ID;
});
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs
index 9867e5225e..d941f50c4b 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs
@@ -54,10 +54,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
- selectedItem.Value = new PlaylistItem
+ selectedItem.Value = new PlaylistItem(Beatmap.Value.BeatmapInfo)
{
- Beatmap = { Value = Beatmap.Value.BeatmapInfo },
- Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset },
+ RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID
};
if (button != null)
@@ -74,13 +73,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
Task.Run(async () =>
{
- if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready)
+ if (MultiplayerClient.IsHost && MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready)
{
- await Client.StartMatch();
+ await MultiplayerClient.StartMatch();
return;
}
- await Client.ToggleReady();
+ await MultiplayerClient.ToggleReady();
readyClickOperation.Dispose();
});
@@ -110,15 +109,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add second user as host", () =>
{
- Client.AddUser(new APIUser { Id = 2, Username = "Another user" });
- Client.TransferHost(2);
+ MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" });
+ MultiplayerClient.TransferHost(2);
});
ClickButtonWhenEnabled();
- AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
+ AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready);
ClickButtonWhenEnabled();
- AddUntilStep("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle);
+ AddUntilStep("user is idle", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle);
}
[TestCase(true)]
@@ -127,14 +126,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("setup", () =>
{
- Client.TransferHost(Client.Room?.Users[0].UserID ?? 0);
+ MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0);
if (!allReady)
- Client.AddUser(new APIUser { Id = 2, Username = "Another user" });
+ MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" });
});
ClickButtonWhenEnabled();
- AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
+ AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready);
verifyGameplayStartFlow();
}
@@ -144,12 +143,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add host", () =>
{
- Client.AddUser(new APIUser { Id = 2, Username = "Another user" });
- Client.TransferHost(2);
+ MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" });
+ MultiplayerClient.TransferHost(2);
});
ClickButtonWhenEnabled();
- AddStep("make user host", () => Client.TransferHost(Client.Room?.Users[0].UserID ?? 0));
+ AddStep("make user host", () => MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0));
verifyGameplayStartFlow();
}
@@ -159,17 +158,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("setup", () =>
{
- Client.TransferHost(Client.Room?.Users[0].UserID ?? 0);
- Client.AddUser(new APIUser { Id = 2, Username = "Another user" });
+ MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0);
+ MultiplayerClient.AddUser(new APIUser { Id = 2, Username = "Another user" });
});
ClickButtonWhenEnabled();
- AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
+ AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready);
- AddStep("transfer host", () => Client.TransferHost(Client.Room?.Users[1].UserID ?? 0));
+ AddStep("transfer host", () => MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[1].UserID ?? 0));
ClickButtonWhenEnabled();
- AddUntilStep("user is idle (match not started)", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle);
+ AddUntilStep("user is idle (match not started)", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle);
AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value);
}
@@ -180,42 +179,42 @@ namespace osu.Game.Tests.Visual.Multiplayer
const int users = 10;
AddStep("setup", () =>
{
- Client.TransferHost(Client.Room?.Users[0].UserID ?? 0);
+ MultiplayerClient.TransferHost(MultiplayerClient.Room?.Users[0].UserID ?? 0);
for (int i = 0; i < users; i++)
- Client.AddUser(new APIUser { Id = i, Username = "Another user" });
+ MultiplayerClient.AddUser(new APIUser { Id = i, Username = "Another user" });
});
if (!isHost)
- AddStep("transfer host", () => Client.TransferHost(2));
+ AddStep("transfer host", () => MultiplayerClient.TransferHost(2));
ClickButtonWhenEnabled();
AddRepeatStep("change user ready state", () =>
{
- Client.ChangeUserState(RNG.Next(0, users), RNG.NextBool() ? MultiplayerUserState.Ready : MultiplayerUserState.Idle);
+ MultiplayerClient.ChangeUserState(RNG.Next(0, users), RNG.NextBool() ? MultiplayerUserState.Ready : MultiplayerUserState.Idle);
}, 20);
AddRepeatStep("ready all users", () =>
{
- var nextUnready = Client.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
+ var nextUnready = MultiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
if (nextUnready != null)
- Client.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
+ MultiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
}, users);
}
private void verifyGameplayStartFlow()
{
- AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready);
+ AddUntilStep("user is ready", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Ready);
ClickButtonWhenEnabled();
- AddUntilStep("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad);
+ AddUntilStep("user waiting for load", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad);
AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value);
AddStep("transitioned to gameplay", () => readyClickOperation.Dispose());
AddStep("finish gameplay", () =>
{
- Client.ChangeUserState(Client.Room?.Users[0].UserID ?? 0, MultiplayerUserState.Loaded);
- Client.ChangeUserState(Client.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay);
+ MultiplayerClient.ChangeUserState(MultiplayerClient.Room?.Users[0].UserID ?? 0, MultiplayerUserState.Loaded);
+ MultiplayerClient.ChangeUserState(MultiplayerClient.Room?.Users[0].UserID ?? 0, MultiplayerUserState.FinishedPlay);
});
AddUntilStep("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs
index 44a1745eee..cc08135939 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs
@@ -22,12 +22,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo;
var score = TestResources.CreateTestScoreInfo(beatmapInfo);
- PlaylistItem playlistItem = new PlaylistItem
- {
- BeatmapID = beatmapInfo.OnlineID,
- };
-
- Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem));
+ Stack.Push(screen = new MultiplayerResultsScreen(score, 1, new PlaylistItem(beatmapInfo)));
});
AddUntilStep("wait for loaded", () => screen.IsLoaded);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
index 42ae279667..03daeaab64 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
@@ -55,10 +55,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
- selectedItem.Value = new PlaylistItem
+ selectedItem.Value = new PlaylistItem(Beatmap.Value.BeatmapInfo)
{
- Beatmap = { Value = Beatmap.Value.BeatmapInfo },
- Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset },
+ RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID,
};
Child = new FillFlowContainer
@@ -78,7 +77,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Task.Run(async () =>
{
- await Client.ToggleSpectate();
+ await MultiplayerClient.ToggleSpectate();
readyClickOperation.Dispose();
});
}
@@ -94,13 +93,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
Task.Run(async () =>
{
- if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready)
+ if (MultiplayerClient.IsHost && MultiplayerClient.LocalUser?.State == MultiplayerUserState.Ready)
{
- await Client.StartMatch();
+ await MultiplayerClient.StartMatch();
return;
}
- await Client.ToggleReady();
+ await MultiplayerClient.ToggleReady();
readyClickOperation.Dispose();
});
@@ -115,7 +114,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[TestCase(MultiplayerRoomState.Playing)]
public void TestEnabledWhenRoomOpenOrInGameplay(MultiplayerRoomState roomState)
{
- AddStep($"change room to {roomState}", () => Client.ChangeRoomState(roomState));
+ AddStep($"change room to {roomState}", () => MultiplayerClient.ChangeRoomState(roomState));
assertSpectateButtonEnablement(true);
}
@@ -124,16 +123,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestToggleWhenIdle(MultiplayerUserState initialState)
{
ClickButtonWhenEnabled();
- AddUntilStep("user is spectating", () => Client.Room?.Users[0].State == MultiplayerUserState.Spectating);
+ AddUntilStep("user is spectating", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Spectating);
ClickButtonWhenEnabled();
- AddUntilStep("user is idle", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle);
+ AddUntilStep("user is idle", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle);
}
[TestCase(MultiplayerRoomState.Closed)]
public void TestDisabledWhenClosed(MultiplayerRoomState roomState)
{
- AddStep($"change room to {roomState}", () => Client.ChangeRoomState(roomState));
+ AddStep($"change room to {roomState}", () => MultiplayerClient.ChangeRoomState(roomState));
assertSpectateButtonEnablement(false);
}
@@ -147,8 +146,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestReadyButtonEnabledWhenHostAndUsersReady()
{
- AddStep("add user", () => Client.AddUser(new APIUser { Id = PLAYER_1_ID }));
- AddStep("set user ready", () => Client.ChangeUserState(PLAYER_1_ID, MultiplayerUserState.Ready));
+ AddStep("add user", () => MultiplayerClient.AddUser(new APIUser { Id = PLAYER_1_ID }));
+ AddStep("set user ready", () => MultiplayerClient.ChangeUserState(PLAYER_1_ID, MultiplayerUserState.Ready));
ClickButtonWhenEnabled();
assertReadyButtonEnablement(true);
@@ -159,11 +158,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add user and transfer host", () =>
{
- Client.AddUser(new APIUser { Id = PLAYER_1_ID });
- Client.TransferHost(PLAYER_1_ID);
+ MultiplayerClient.AddUser(new APIUser { Id = PLAYER_1_ID });
+ MultiplayerClient.TransferHost(PLAYER_1_ID);
});
- AddStep("set user ready", () => Client.ChangeUserState(PLAYER_1_ID, MultiplayerUserState.Ready));
+ AddStep("set user ready", () => MultiplayerClient.ChangeUserState(PLAYER_1_ID, MultiplayerUserState.Ready));
ClickButtonWhenEnabled();
assertReadyButtonEnablement(false);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs
index dfc16c44f2..bdc348b043 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs
@@ -26,18 +26,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo;
var score = TestResources.CreateTestScoreInfo(beatmapInfo);
- PlaylistItem playlistItem = new PlaylistItem
- {
- BeatmapID = beatmapInfo.OnlineID,
- };
-
SortedDictionary teamScores = new SortedDictionary
{
{ 0, new BindableInt(team1Score) },
{ 1, new BindableInt(team2Score) }
};
- Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, playlistItem, teamScores));
+ Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, new PlaylistItem(beatmapInfo), teamScores));
});
AddUntilStep("wait for loaded", () => screen.IsLoaded);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs
index e63e58824f..98dc243ab5 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs
@@ -4,33 +4,29 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
-using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
-using osu.Game.Beatmaps.Drawables;
-using osu.Game.Database;
using osu.Game.Graphics.Containers;
using osu.Game.Models;
+using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.OnlinePlay;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Tests.Beatmaps;
+using osu.Game.Tests.Visual.OnlinePlay;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Multiplayer
{
- public class TestScenePlaylistsRoomSettingsPlaylist : OsuManualInputManagerTestScene
+ public class TestScenePlaylistsRoomSettingsPlaylist : OnlinePlayTestScene
{
private TestPlaylist playlist;
- [Cached(typeof(UserLookupCache))]
- private readonly TestUserLookupCache userLookupCache = new TestUserLookupCache();
-
[Test]
public void TestItemRemovedOnDeletion()
{
@@ -110,18 +106,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
}
- [Test]
- public void TestChangeBeatmapAndRemove()
- {
- createPlaylist();
-
- AddStep("change beatmap of first item", () => playlist.Items[0].BeatmapID = 30);
- moveToDeleteButton(0);
- AddStep("click delete button", () => InputManager.Click(MouseButton.Left));
- }
-
private void moveToItem(int index, Vector2? offset = null)
- => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset));
+ => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset));
private void moveToDeleteButton(int index, Vector2? offset = null) => AddStep($"move mouse to delete button {index}", () =>
{
@@ -142,31 +128,27 @@ namespace osu.Game.Tests.Visual.Multiplayer
for (int i = 0; i < 20; i++)
{
- playlist.Items.Add(new PlaylistItem
+ playlist.Items.Add(new PlaylistItem(i % 2 == 1
+ ? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo
+ : new BeatmapInfo
+ {
+ Metadata = new BeatmapMetadata
+ {
+ Artist = "Artist",
+ Author = new RealmUser { Username = "Creator name here" },
+ Title = "Long title used to check background colour",
+ },
+ BeatmapSet = new BeatmapSetInfo()
+ })
{
ID = i,
OwnerID = 2,
- Beatmap =
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ RequiredMods = new[]
{
- Value = i % 2 == 1
- ? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo
- : new BeatmapInfo
- {
- Metadata = new BeatmapMetadata
- {
- Artist = "Artist",
- Author = new RealmUser { Username = "Creator name here" },
- Title = "Long title used to check background colour",
- },
- BeatmapSet = new BeatmapSetInfo()
- }
- },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
- RequiredMods =
- {
- new OsuModHardRock(),
- new OsuModDoubleTime(),
- new OsuModAutoplay()
+ new APIMod(new OsuModHardRock()),
+ new APIMod(new OsuModDoubleTime()),
+ new APIMod(new OsuModAutoplay())
}
});
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
index d933491ab6..3333afc88b 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
@@ -115,8 +115,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("change mod rate", () => ((OsuModDoubleTime)SelectedMods.Value[0]).SpeedChange.Value = 2);
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
- AddAssert("item 1 has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0]).SpeedChange.Value));
- AddAssert("item 2 has rate 2", () => Precision.AlmostEquals(2, ((OsuModDoubleTime)SelectedRoom.Value.Playlist.Last().RequiredMods[0]).SpeedChange.Value));
+ AddAssert("item 1 has rate 1.5", () =>
+ {
+ var mod = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
+ return Precision.AlmostEquals(1.5, mod.SpeedChange.Value);
+ });
+
+ AddAssert("item 2 has rate 2", () =>
+ {
+ var mod = (OsuModDoubleTime)SelectedRoom.Value.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
+ return Precision.AlmostEquals(2, mod.SpeedChange.Value);
+ });
}
///
@@ -138,7 +147,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
- AddAssert("item has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0]).SpeedChange.Value));
+ AddAssert("item has rate 1.5", () =>
+ {
+ var m = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
+ return Precision.AlmostEquals(1.5, m.SpeedChange.Value);
+ });
}
private class TestPlaylistsSongSelect : PlaylistsSongSelect
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs
index 823ac07cf7..f95e73ff3c 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs
@@ -25,14 +25,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add user", () =>
{
- Client.AddUser(new APIUser
+ MultiplayerClient.AddUser(new APIUser
{
Id = 2,
Statistics = { GlobalRank = 1234 }
});
// Remove the local user so only the one above is displayed.
- Client.RemoveUser(API.LocalUser.Value);
+ MultiplayerClient.RemoveUser(API.LocalUser.Value);
});
}
@@ -41,26 +41,26 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add users", () =>
{
- Client.AddUser(new APIUser
+ MultiplayerClient.AddUser(new APIUser
{
Id = 2,
Statistics = { GlobalRank = 1234 }
});
- Client.AddUser(new APIUser
+ MultiplayerClient.AddUser(new APIUser
{
Id = 3,
Statistics = { GlobalRank = 3333 }
});
- Client.AddUser(new APIUser
+ MultiplayerClient.AddUser(new APIUser
{
Id = 4,
Statistics = { GlobalRank = 4321 }
});
// Remove the local user so only the ones above are displayed.
- Client.RemoveUser(API.LocalUser.Value);
+ MultiplayerClient.RemoveUser(API.LocalUser.Value);
});
}
@@ -75,20 +75,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add users", () =>
{
- Client.AddUser(new APIUser
+ MultiplayerClient.AddUser(new APIUser
{
Id = 2,
Statistics = { GlobalRank = min }
});
- Client.AddUser(new APIUser
+ MultiplayerClient.AddUser(new APIUser
{
Id = 3,
Statistics = { GlobalRank = max }
});
// Remove the local user so only the ones above are displayed.
- Client.RemoveUser(API.LocalUser.Value);
+ MultiplayerClient.RemoveUser(API.LocalUser.Value);
});
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs
index 20db922122..5e4013b0f1 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs
@@ -31,8 +31,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
SelectedRoom.Value.Playlist.AddRange(new[]
{
- new PlaylistItem { Beatmap = { Value = new BeatmapInfo { StarRating = min } } },
- new PlaylistItem { Beatmap = { Value = new BeatmapInfo { StarRating = max } } },
+ new PlaylistItem(new BeatmapInfo { StarRating = min }),
+ new PlaylistItem(new BeatmapInfo { StarRating = max }),
});
});
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs
index 781f0a1824..002ae3141a 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs
@@ -10,7 +10,6 @@ using osu.Framework.Extensions;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
-using osu.Game.Database;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
@@ -34,10 +33,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private TestMultiplayerComponents multiplayerComponents;
- private TestMultiplayerClient client => multiplayerComponents.Client;
-
- [Cached(typeof(UserLookupCache))]
- private UserLookupCache lookupCache = new TestUserLookupCache();
+ private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -71,16 +67,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
Type = { Value = MatchType.TeamVersus },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
- AddUntilStep("room type is team vs", () => client.Room?.Settings.MatchType == MatchType.TeamVersus);
- AddAssert("user state arrived", () => client.Room?.Users.FirstOrDefault()?.MatchState is TeamVersusUserState);
+ AddUntilStep("room type is team vs", () => multiplayerClient.Room?.Settings.MatchType == MatchType.TeamVersus);
+ AddAssert("user state arrived", () => multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState is TeamVersusUserState);
}
[Test]
@@ -92,33 +87,32 @@ namespace osu.Game.Tests.Visual.Multiplayer
Type = { Value = MatchType.TeamVersus },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
- AddAssert("user on team 0", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
- AddStep("add another user", () => client.AddUser(new APIUser { Username = "otheruser", Id = 44 }));
+ AddAssert("user on team 0", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
+ AddStep("add another user", () => multiplayerClient.AddUser(new APIUser { Username = "otheruser", Id = 44 }));
AddStep("press own button", () =>
{
InputManager.MoveMouseTo(multiplayerComponents.ChildrenOfType().First());
InputManager.Click(MouseButton.Left);
});
- AddAssert("user on team 1", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1);
+ AddAssert("user on team 1", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1);
AddStep("press own button again", () => InputManager.Click(MouseButton.Left));
- AddAssert("user on team 0", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
+ AddAssert("user on team 0", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
AddStep("press other user's button", () =>
{
InputManager.MoveMouseTo(multiplayerComponents.ChildrenOfType().ElementAt(1));
InputManager.Click(MouseButton.Left);
});
- AddAssert("user still on team 0", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
+ AddAssert("user still on team 0", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
}
[Test]
@@ -130,22 +124,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
Type = { Value = MatchType.HeadToHead },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
}
});
- AddUntilStep("match type head to head", () => client.APIRoom?.Type.Value == MatchType.HeadToHead);
+ AddUntilStep("match type head to head", () => multiplayerClient.APIRoom?.Type.Value == MatchType.HeadToHead);
- AddStep("change match type", () => client.ChangeSettings(new MultiplayerRoomSettings
+ AddStep("change match type", () => multiplayerClient.ChangeSettings(new MultiplayerRoomSettings
{
MatchType = MatchType.TeamVersus
- }));
+ }).WaitSafely());
- AddUntilStep("api room updated to team versus", () => client.APIRoom?.Type.Value == MatchType.TeamVersus);
+ AddUntilStep("api room updated to team versus", () => multiplayerClient.APIRoom?.Type.Value == MatchType.TeamVersus);
}
[Test]
@@ -156,21 +149,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Test Room" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
}
});
- AddUntilStep("room type is head to head", () => client.Room?.Settings.MatchType == MatchType.HeadToHead);
+ AddUntilStep("room type is head to head", () => multiplayerClient.Room?.Settings.MatchType == MatchType.HeadToHead);
AddUntilStep("team displays are not displaying teams", () => multiplayerComponents.ChildrenOfType().All(d => d.DisplayedTeam == null));
- AddStep("change to team vs", () => client.ChangeSettings(matchType: MatchType.TeamVersus));
+ AddStep("change to team vs", () => multiplayerClient.ChangeSettings(matchType: MatchType.TeamVersus));
- AddUntilStep("room type is team vs", () => client.Room?.Settings.MatchType == MatchType.TeamVersus);
+ AddUntilStep("room type is team vs", () => multiplayerClient.Room?.Settings.MatchType == MatchType.TeamVersus);
AddUntilStep("team displays are displaying teams", () => multiplayerComponents.ChildrenOfType().All(d => d.DisplayedTeam != null));
}
@@ -189,7 +181,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.Click(MouseButton.Left);
});
- AddUntilStep("wait for join", () => client.RoomJoined);
+ AddUntilStep("wait for join", () => multiplayerClient.RoomJoined);
}
}
}
diff --git a/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs b/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs
new file mode 100644
index 0000000000..00a06d420e
--- /dev/null
+++ b/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs
@@ -0,0 +1,43 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.IO;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Database;
+using osu.Game.Models;
+using osu.Game.Scoring;
+using osu.Game.Skinning;
+using osu.Game.Tests.Resources;
+
+namespace osu.Game.Tests.Visual.Navigation
+{
+ public class TestEFToRealmMigration : OsuGameTestScene
+ {
+ public override void RecycleLocalStorage(bool isDisposing)
+ {
+ base.RecycleLocalStorage(isDisposing);
+
+ if (isDisposing)
+ return;
+
+ using (var outStream = LocalStorage.GetStream(DatabaseContextFactory.DATABASE_NAME, FileAccess.Write, FileMode.Create))
+ using (var stream = TestResources.OpenResource(DatabaseContextFactory.DATABASE_NAME))
+ stream.CopyTo(outStream);
+ }
+
+ [Test]
+ public void TestMigration()
+ {
+ // Numbers are taken from the test database (see commit f03de16ee5a46deac3b5f2ca1edfba5c4c5dca7d).
+ AddAssert("Check beatmaps", () => Game.Dependencies.Get().Run(r => r.All().Count(s => !s.Protected) == 1));
+ AddAssert("Check skins", () => Game.Dependencies.Get().Run(r => r.All().Count(s => !s.Protected) == 1));
+ AddAssert("Check scores", () => Game.Dependencies.Get().Run(r => r.All().Count() == 1));
+
+ // One extra file is created during realm migration / startup due to the circles intro import.
+ AddAssert("Check files", () => Game.Dependencies.Get().Run(r => r.All().Count() == 271));
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
index ca3387392a..666e32d1d0 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
@@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("set name", () => SelectedRoom.Value.Name.Value = "Room name");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
- AddStep("set beatmap", () => SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } }));
+ AddStep("set beatmap", () => SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)));
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
AddStep("clear name", () => SelectedRoom.Value.Name.Value = "");
@@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
settings.NameField.Current.Value = expected_name;
settings.DurationField.Current.Value = expectedDuration;
- SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } });
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo));
RoomManager.CreateRequested = r =>
{
@@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Playlists
var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo;
SelectedRoom.Value.Name.Value = "Test Room";
- SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = beatmap } });
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmap));
errorMessage = $"{not_found_prefix} {beatmap.OnlineID}";
@@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("setup", () =>
{
SelectedRoom.Value.Name.Value = "Test Room";
- SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } });
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo));
RoomManager.CreateRequested = _ => failText;
});
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
index 11df115b1a..161624413d 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
@@ -165,10 +165,9 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("load results", () =>
{
- LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem
+ LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
- Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo }
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}));
});
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
index 68225f6d64..28b1f6eff5 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
@@ -64,10 +64,9 @@ namespace osu.Game.Tests.Visual.Playlists
room.Host.Value = API.LocalUser.Value;
room.RecentParticipants.Add(room.Host.Value);
room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
- room.Playlist.Add(new PlaylistItem
+ room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
{
- Beatmap = { Value = importedBeatmap.Beatmaps.First() },
- Ruleset = { Value = new OsuRuleset().RulesetInfo }
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@@ -89,10 +88,9 @@ namespace osu.Game.Tests.Visual.Playlists
room.Host.Value = API.LocalUser.Value;
room.RecentParticipants.Add(room.Host.Value);
room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
- room.Playlist.Add(new PlaylistItem
+ room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
{
- Beatmap = { Value = importedBeatmap.Beatmaps.First() },
- Ruleset = { Value = new OsuRuleset().RulesetInfo }
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@@ -106,10 +104,9 @@ namespace osu.Game.Tests.Visual.Playlists
{
room.Name.Value = "my awesome room";
room.Host.Value = API.LocalUser.Value;
- room.Playlist.Add(new PlaylistItem
+ room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
{
- Beatmap = { Value = importedBeatmap.Beatmaps.First() },
- Ruleset = { Value = new OsuRuleset().RulesetInfo }
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@@ -158,22 +155,18 @@ namespace osu.Game.Tests.Visual.Playlists
{
room.Name.Value = "my awesome room";
room.Host.Value = API.LocalUser.Value;
- room.Playlist.Add(new PlaylistItem
+ room.Playlist.Add(new PlaylistItem(new BeatmapInfo
{
- Beatmap =
+ MD5Hash = realHash,
+ OnlineID = realOnlineId,
+ Metadata = new BeatmapMetadata(),
+ BeatmapSet = new BeatmapSetInfo
{
- Value = new BeatmapInfo
- {
- MD5Hash = realHash,
- OnlineID = realOnlineId,
- Metadata = new BeatmapMetadata(),
- BeatmapSet = new BeatmapSetInfo
- {
- OnlineID = realOnlineSetId,
- }
- }
- },
- Ruleset = { Value = new OsuRuleset().RulesetInfo }
+ OnlineID = realOnlineSetId,
+ }
+ })
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
index 667fd08084..c4178143f8 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
@@ -197,6 +197,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now,
Mods = new Mod[]
{
new OsuModHidden(),
@@ -234,6 +235,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddSeconds(-30),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -254,6 +256,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddSeconds(-70),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -275,6 +278,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddMinutes(-40),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -296,6 +300,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddHours(-2),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -317,6 +322,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.9826,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddHours(-25),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -338,6 +344,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.9654,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddHours(-50),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -359,6 +366,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddHours(-72),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -380,6 +388,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddMonths(-3),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -401,6 +410,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddYears(-2),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
diff --git a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs
index cd7a936778..c43ed744bd 100644
--- a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs
+++ b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs
@@ -4,6 +4,8 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Screens;
+using osu.Game.Database;
+using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens;
@@ -37,7 +39,16 @@ namespace osu.Game.Tests.Visual
public new bool IsLoaded => base.IsLoaded && MultiplayerScreen.IsLoaded;
[Cached(typeof(MultiplayerClient))]
- public readonly TestMultiplayerClient Client;
+ public readonly TestMultiplayerClient MultiplayerClient;
+
+ [Cached(typeof(UserLookupCache))]
+ private readonly UserLookupCache userLookupCache = new TestUserLookupCache();
+
+ [Cached]
+ private readonly BeatmapLookupCache beatmapLookupCache = new BeatmapLookupCache();
+
+ [Resolved]
+ private BeatmapManager beatmapManager { get; set; }
private readonly OsuScreenStack screenStack;
private readonly TestMultiplayer multiplayerScreen;
@@ -48,7 +59,9 @@ namespace osu.Game.Tests.Visual
InternalChildren = new Drawable[]
{
- Client = new TestMultiplayerClient(RoomManager),
+ userLookupCache,
+ beatmapLookupCache,
+ MultiplayerClient = new TestMultiplayerClient(RoomManager),
screenStack = new OsuScreenStack
{
Name = nameof(TestMultiplayerComponents),
@@ -60,9 +73,9 @@ namespace osu.Game.Tests.Visual
}
[BackgroundDependencyLoader]
- private void load(IAPIProvider api, OsuGameBase game)
+ private void load(IAPIProvider api)
{
- ((DummyAPIAccess)api).HandleRequest = request => multiplayerScreen.RequestsHandler.HandleRequest(request, api.LocalUser.Value, game);
+ ((DummyAPIAccess)api).HandleRequest = request => multiplayerScreen.RequestsHandler.HandleRequest(request, api.LocalUser.Value, beatmapManager);
}
public override bool OnBackButton() => (screenStack.CurrentScreen as OsuScreen)?.OnBackButton() ?? base.OnBackButton();
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs
new file mode 100644
index 0000000000..f4920b4412
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneExpandingContainer.cs
@@ -0,0 +1,158 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Settings.Sections;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneExpandingContainer : OsuManualInputManagerTestScene
+ {
+ private TestExpandingContainer container;
+ private SettingsToolboxGroup toolboxGroup;
+
+ private ExpandableSlider slider1;
+ private ExpandableSlider slider2;
+
+ [SetUp]
+ public void SetUp() => Schedule(() =>
+ {
+ Child = container = new TestExpandingContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Height = 0.33f,
+ Child = toolboxGroup = new SettingsToolboxGroup("sliders")
+ {
+ RelativeSizeAxes = Axes.X,
+ Width = 1,
+ Children = new Drawable[]
+ {
+ slider1 = new ExpandableSlider
+ {
+ Current = new BindableFloat
+ {
+ Default = 1.0f,
+ MinValue = 1.0f,
+ MaxValue = 10.0f,
+ Precision = 0.01f,
+ },
+ },
+ slider2 = new ExpandableSlider
+ {
+ Current = new BindableDouble
+ {
+ Default = 1.0,
+ MinValue = 1.0,
+ MaxValue = 10.0,
+ Precision = 0.01,
+ },
+ },
+ }
+ }
+ };
+
+ slider1.Current.BindValueChanged(v =>
+ {
+ slider1.ExpandedLabelText = $"Slider One ({v.NewValue:0.##x})";
+ slider1.ContractedLabelText = $"S. 1. ({v.NewValue:0.##x})";
+ }, true);
+
+ slider2.Current.BindValueChanged(v =>
+ {
+ slider2.ExpandedLabelText = $"Slider Two ({v.NewValue:N2})";
+ slider2.ContractedLabelText = $"S. 2. ({v.NewValue:N2})";
+ }, true);
+ });
+
+ [Test]
+ public void TestDisplay()
+ {
+ AddStep("switch to contracted", () => container.Expanded.Value = false);
+ AddStep("switch to expanded", () => container.Expanded.Value = true);
+ AddStep("set left origin", () => container.Origin = Anchor.CentreLeft);
+ AddStep("set centre origin", () => container.Origin = Anchor.Centre);
+ AddStep("set right origin", () => container.Origin = Anchor.CentreRight);
+ }
+
+ ///
+ /// Tests hovering expands the container and does not contract until hover is lost.
+ ///
+ [Test]
+ public void TestHoveringExpandsContainer()
+ {
+ AddAssert("ensure container contracted", () => !container.Expanded.Value);
+
+ AddStep("hover container", () => InputManager.MoveMouseTo(container));
+ AddAssert("container expanded", () => container.Expanded.Value);
+ AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
+
+ AddStep("hover away", () => InputManager.MoveMouseTo(Vector2.Zero));
+ AddAssert("container contracted", () => !container.Expanded.Value);
+ AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.Expanded.Value);
+ }
+
+ ///
+ /// Tests expanding a container will expand underlying groups if contracted.
+ ///
+ [Test]
+ public void TestExpandingContainerExpandsContractedGroup()
+ {
+ AddStep("contract group", () => toolboxGroup.Expanded.Value = false);
+
+ AddStep("expand container", () => container.Expanded.Value = true);
+ AddAssert("group expanded", () => toolboxGroup.Expanded.Value);
+ AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
+
+ AddStep("contract container", () => container.Expanded.Value = false);
+ AddAssert("group contracted", () => !toolboxGroup.Expanded.Value);
+ AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.Expanded.Value);
+ }
+
+ ///
+ /// Tests contracting a container does not contract underlying groups if expanded by user (i.e. by setting directly).
+ ///
+ [Test]
+ public void TestContractingContainerDoesntContractUserExpandedGroup()
+ {
+ AddAssert("ensure group expanded", () => toolboxGroup.Expanded.Value);
+
+ AddStep("expand container", () => container.Expanded.Value = true);
+ AddAssert("group still expanded", () => toolboxGroup.Expanded.Value);
+ AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
+
+ AddStep("contract container", () => container.Expanded.Value = false);
+ AddAssert("group still expanded", () => toolboxGroup.Expanded.Value);
+ AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.Expanded.Value);
+ }
+
+ ///
+ /// Tests expanding a container via does not get contracted by losing hover.
+ ///
+ [Test]
+ public void TestExpandingContainerDoesntGetContractedByHover()
+ {
+ AddStep("expand container", () => container.Expanded.Value = true);
+
+ AddStep("hover container", () => InputManager.MoveMouseTo(container));
+ AddAssert("container still expanded", () => container.Expanded.Value);
+
+ AddStep("hover away", () => InputManager.MoveMouseTo(Vector2.Zero));
+ AddAssert("container still expanded", () => container.Expanded.Value);
+ }
+
+ private class TestExpandingContainer : ExpandingContainer
+ {
+ public TestExpandingContainer()
+ : base(120, 250)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 3b115d43e5..acf1e8470b 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -12,7 +12,7 @@
WinExe
- net5.0
+ net6.0
tests.ruleset
diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
index 130fcfaca1..c7314a4969 100644
--- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
+++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
@@ -11,7 +11,7 @@
WinExe
- net5.0
+ net6.0
@@ -20,4 +20,4 @@
-
\ No newline at end of file
+
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 633eb8f15e..5f7de0d762 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -22,6 +22,7 @@ using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using osu.Game.Skinning;
using osu.Game.Stores;
+using osu.Game.Utils;
#nullable enable
@@ -108,39 +109,78 @@ namespace osu.Game.Beatmaps
}
///
- /// Add a new difficulty to the beatmap set represented by the provided .
+ /// Add a new difficulty to the provided based on the provided .
/// The new difficulty will be backed by a model
/// and represented by the returned .
///
- public virtual WorkingBeatmap CreateNewBlankDifficulty(BeatmapSetInfo beatmapSetInfo, RulesetInfo rulesetInfo)
+ ///
+ /// Contrary to , this method does not preserve hitobjects and beatmap-level settings from .
+ /// The created beatmap will have zero hitobjects and will have default settings (including difficulty settings), but will preserve metadata and existing timing points.
+ ///
+ /// The to add the new difficulty to.
+ /// The to use as a baseline reference when creating the new difficulty.
+ /// The ruleset with which the new difficulty should be created.
+ public virtual WorkingBeatmap CreateNewDifficulty(BeatmapSetInfo targetBeatmapSet, WorkingBeatmap referenceWorkingBeatmap, RulesetInfo rulesetInfo)
{
- // fetch one of the existing difficulties to copy timing points and metadata from,
- // so that the user doesn't have to fill all of that out again.
- // this silently assumes that all difficulties have the same timing points and metadata,
- // but cases where this isn't true seem rather rare / pathological.
- var referenceBeatmap = GetWorkingBeatmap(beatmapSetInfo.Beatmaps.First());
+ var playableBeatmap = referenceWorkingBeatmap.GetPlayableBeatmap(rulesetInfo);
- var newBeatmapInfo = new BeatmapInfo(rulesetInfo, new BeatmapDifficulty(), referenceBeatmap.Metadata.DeepClone());
+ var newBeatmapInfo = new BeatmapInfo(rulesetInfo, new BeatmapDifficulty(), playableBeatmap.Metadata.DeepClone())
+ {
+ DifficultyName = NamingUtils.GetNextBestName(targetBeatmapSet.Beatmaps.Select(b => b.DifficultyName), "New Difficulty")
+ };
+ var newBeatmap = new Beatmap { BeatmapInfo = newBeatmapInfo };
+ foreach (var timingPoint in playableBeatmap.ControlPointInfo.TimingPoints)
+ newBeatmap.ControlPointInfo.Add(timingPoint.Time, timingPoint.DeepClone());
+ return addDifficultyToSet(targetBeatmapSet, newBeatmap, referenceWorkingBeatmap.Skin);
+ }
+
+ ///
+ /// Add a copy of the provided to the provided .
+ /// The new difficulty will be backed by a model
+ /// and represented by the returned .
+ ///
+ ///
+ /// Contrary to , this method creates a nearly-exact copy of
+ /// (with the exception of a few key properties that cannot be copied under any circumstance, like difficulty name, beatmap hash, or online status).
+ ///
+ /// The to add the copy to.
+ /// The to be copied.
+ public virtual WorkingBeatmap CopyExistingDifficulty(BeatmapSetInfo targetBeatmapSet, WorkingBeatmap referenceWorkingBeatmap)
+ {
+ var newBeatmap = referenceWorkingBeatmap.GetPlayableBeatmap(referenceWorkingBeatmap.BeatmapInfo.Ruleset).Clone();
+ BeatmapInfo newBeatmapInfo;
+
+ newBeatmap.BeatmapInfo = newBeatmapInfo = referenceWorkingBeatmap.BeatmapInfo.Clone();
+ // assign a new ID to the clone.
+ newBeatmapInfo.ID = Guid.NewGuid();
+ // add "(copy)" suffix to difficulty name, and additionally ensure that it doesn't conflict with any other potentially pre-existing copies.
+ newBeatmapInfo.DifficultyName = NamingUtils.GetNextBestName(
+ targetBeatmapSet.Beatmaps.Select(b => b.DifficultyName),
+ $"{newBeatmapInfo.DifficultyName} (copy)");
+ // clear the hash, as that's what is used to match .osu files with their corresponding realm beatmaps.
+ newBeatmapInfo.Hash = string.Empty;
+ // clear online properties.
+ newBeatmapInfo.OnlineID = -1;
+ newBeatmapInfo.Status = BeatmapOnlineStatus.None;
+
+ return addDifficultyToSet(targetBeatmapSet, newBeatmap, referenceWorkingBeatmap.Skin);
+ }
+
+ private WorkingBeatmap addDifficultyToSet(BeatmapSetInfo targetBeatmapSet, IBeatmap newBeatmap, ISkin beatmapSkin)
+ {
// populate circular beatmap set info <-> beatmap info references manually.
// several places like `BeatmapModelManager.Save()` or `GetWorkingBeatmap()`
// rely on them being freely traversable in both directions for correct operation.
- beatmapSetInfo.Beatmaps.Add(newBeatmapInfo);
- newBeatmapInfo.BeatmapSet = beatmapSetInfo;
+ targetBeatmapSet.Beatmaps.Add(newBeatmap.BeatmapInfo);
+ newBeatmap.BeatmapInfo.BeatmapSet = targetBeatmapSet;
- var newBeatmap = new Beatmap { BeatmapInfo = newBeatmapInfo };
- foreach (var timingPoint in referenceBeatmap.Beatmap.ControlPointInfo.TimingPoints)
- newBeatmap.ControlPointInfo.Add(timingPoint.Time, timingPoint.DeepClone());
+ beatmapModelManager.Save(newBeatmap.BeatmapInfo, newBeatmap, beatmapSkin);
- beatmapModelManager.Save(newBeatmapInfo, newBeatmap);
-
- workingBeatmapCache.Invalidate(beatmapSetInfo);
+ workingBeatmapCache.Invalidate(targetBeatmapSet);
return GetWorkingBeatmap(newBeatmap.BeatmapInfo);
}
- // TODO: add back support for making a copy of another difficulty
- // (likely via a separate `CopyDifficulty()` method).
-
///
/// Delete a beatmap difficulty.
///
diff --git a/osu.Game/Database/EFToRealmMigrator.cs b/osu.Game/Database/EFToRealmMigrator.cs
index 0bb5388d55..c9deee19fe 100644
--- a/osu.Game/Database/EFToRealmMigrator.cs
+++ b/osu.Game/Database/EFToRealmMigrator.cs
@@ -215,7 +215,8 @@ namespace osu.Game.Database
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Files).ThenInclude(f => f.FileInfo)
- .Include(s => s.Metadata);
+ .Include(s => s.Metadata)
+ .AsSplitQuery();
log("Beginning beatmaps migration to realm");
@@ -344,7 +345,8 @@ namespace osu.Game.Database
.Include(s => s.Ruleset)
.Include(s => s.BeatmapInfo)
.Include(s => s.Files)
- .ThenInclude(f => f.FileInfo);
+ .ThenInclude(f => f.FileInfo)
+ .AsSplitQuery();
log("Beginning scores migration to realm");
@@ -434,6 +436,7 @@ namespace osu.Game.Database
var existingSkins = db.SkinInfo
.Include(s => s.Files)
.ThenInclude(f => f.FileInfo)
+ .AsSplitQuery()
.ToList();
// previous entries in EF are removed post migration.
diff --git a/osu.Game/Database/ImportTask.cs b/osu.Game/Database/ImportTask.cs
index cd9e396d13..d75c1a73e6 100644
--- a/osu.Game/Database/ImportTask.cs
+++ b/osu.Game/Database/ImportTask.cs
@@ -4,6 +4,7 @@
#nullable enable
using System.IO;
+using osu.Framework.Extensions;
using osu.Game.IO.Archives;
using osu.Game.Stores;
using osu.Game.Utils;
@@ -63,9 +64,7 @@ namespace osu.Game.Database
if (!(stream is MemoryStream memoryStream))
{
// This isn't used in any current path. May need to reconsider for performance reasons (ie. if we don't expect the incoming stream to be copied out).
- byte[] buffer = new byte[stream.Length];
- stream.Read(buffer, 0, (int)stream.Length);
- memoryStream = new MemoryStream(buffer);
+ memoryStream = new MemoryStream(stream.ReadAllBytesToArray());
}
if (ZipUtils.IsZipArchive(memoryStream))
diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs
index 441b090a6e..79183b6f0e 100644
--- a/osu.Game/Database/OsuDbContext.cs
+++ b/osu.Game/Database/OsuDbContext.cs
@@ -3,7 +3,6 @@
using System;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using osu.Framework.Logging;
using osu.Framework.Statistics;
@@ -12,8 +11,9 @@ using osu.Game.Configuration;
using osu.Game.IO;
using osu.Game.Rulesets;
using osu.Game.Scoring;
-using LogLevel = Microsoft.Extensions.Logging.LogLevel;
using osu.Game.Skinning;
+using SQLitePCL;
+using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace osu.Game.Database
{
@@ -40,10 +40,10 @@ namespace osu.Game.Database
static OsuDbContext()
{
// required to initialise native SQLite libraries on some platforms.
- SQLitePCL.Batteries_V2.Init();
+ Batteries_V2.Init();
// https://github.com/aspnet/EntityFrameworkCore/issues/9994#issuecomment-508588678
- SQLitePCL.raw.sqlite3_config(2 /*SQLITE_CONFIG_MULTITHREAD*/);
+ raw.sqlite3_config(2 /*SQLITE_CONFIG_MULTITHREAD*/);
}
///
@@ -116,7 +116,6 @@ namespace osu.Game.Database
optionsBuilder
// this is required for the time being due to the way we are querying in places like BeatmapStore.
// if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled.
- .ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning))
.UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10))
.UseLoggerFactory(logger.Value);
}
diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs
index dc05482a05..54af6a5942 100644
--- a/osu.Game/Extensions/TimeDisplayExtensions.cs
+++ b/osu.Game/Extensions/TimeDisplayExtensions.cs
@@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using Humanizer;
using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Extensions
{
@@ -47,5 +49,57 @@ namespace osu.Game.Extensions
return new LocalisableFormattableString(timeSpan, @"mm\:ss");
}
+
+ ///
+ /// Formats a provided date to a short relative string version for compact display.
+ ///
+ /// The time to be displayed.
+ /// A timespan denoting the time length beneath which "now" should be displayed.
+ /// A short relative string representing the input time.
+ public static string ToShortRelativeTime(this DateTimeOffset time, TimeSpan lowerCutoff)
+ {
+ if (time == default)
+ return "-";
+
+ var now = DateTime.Now;
+ var difference = now - time;
+
+ // web uses momentjs's custom locales to format the date for the purposes of the scoreboard.
+ // this is intended to be a best-effort, more legible approximation of that.
+ // compare:
+ // * https://github.com/ppy/osu-web/blob/a8f5a68fb435cb19a4faa4c7c4bce08c4f096933/resources/assets/lib/scoreboard-time.tsx
+ // * https://momentjs.com/docs/#/customization/ (reference for the customisation format)
+
+ // TODO: support localisation (probably via `CommonStrings.CountHours()` etc.)
+ // requires pluralisable string support framework-side
+
+ if (difference < lowerCutoff)
+ return CommonStrings.TimeNow.ToString();
+
+ if (difference.TotalMinutes < 1)
+ return "sec".ToQuantity((int)difference.TotalSeconds);
+ if (difference.TotalHours < 1)
+ return "min".ToQuantity((int)difference.TotalMinutes);
+ if (difference.TotalDays < 1)
+ return "hr".ToQuantity((int)difference.TotalHours);
+
+ // this is where this gets more complicated because of how the calendar works.
+ // since there's no `TotalMonths` / `TotalYears`, we have to iteratively add months/years
+ // and test against cutoff dates to determine how many months/years to show.
+
+ if (time > now.AddMonths(-1))
+ return difference.TotalDays < 2 ? "1dy" : $"{(int)difference.TotalDays}dys";
+
+ for (int months = 1; months <= 11; ++months)
+ {
+ if (time > now.AddMonths(-(months + 1)))
+ return months == 1 ? "1mo" : $"{months}mos";
+ }
+
+ int years = 1;
+ while (time <= now.AddYears(-(years + 1)))
+ years += 1;
+ return years == 1 ? "1yr" : $"{years}yrs";
+ }
}
}
diff --git a/osu.Game/Graphics/Containers/ExpandingButtonContainer.cs b/osu.Game/Graphics/Containers/ExpandingButtonContainer.cs
new file mode 100644
index 0000000000..859850e771
--- /dev/null
+++ b/osu.Game/Graphics/Containers/ExpandingButtonContainer.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.
+
+namespace osu.Game.Graphics.Containers
+{
+ ///
+ /// An with a long hover expansion delay.
+ ///
+ ///
+ /// Mostly used for buttons with explanatory labels, in which the label would display after a "long hover".
+ ///
+ public class ExpandingButtonContainer : ExpandingContainer
+ {
+ protected ExpandingButtonContainer(float contractedWidth, float expandedWidth)
+ : base(contractedWidth, expandedWidth)
+ {
+ }
+
+ protected override double HoverExpansionDelay => 400;
+ }
+}
diff --git a/osu.Game/Graphics/Containers/ExpandingContainer.cs b/osu.Game/Graphics/Containers/ExpandingContainer.cs
new file mode 100644
index 0000000000..b50e008362
--- /dev/null
+++ b/osu.Game/Graphics/Containers/ExpandingContainer.cs
@@ -0,0 +1,100 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Input.Events;
+using osu.Framework.Threading;
+
+namespace osu.Game.Graphics.Containers
+{
+ ///
+ /// Represents a with the ability to expand/contract on hover.
+ ///
+ public class ExpandingContainer : Container, IExpandingContainer
+ {
+ private readonly float contractedWidth;
+ private readonly float expandedWidth;
+
+ public BindableBool Expanded { get; } = new BindableBool();
+
+ ///
+ /// Delay before the container switches to expanded state from hover.
+ ///
+ protected virtual double HoverExpansionDelay => 0;
+
+ protected override Container Content => FillFlow;
+
+ protected FillFlowContainer FillFlow { get; }
+
+ protected ExpandingContainer(float contractedWidth, float expandedWidth)
+ {
+ this.contractedWidth = contractedWidth;
+ this.expandedWidth = expandedWidth;
+
+ RelativeSizeAxes = Axes.Y;
+ Width = contractedWidth;
+
+ InternalChild = new OsuScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ ScrollbarVisible = false,
+ Child = FillFlow = new FillFlowContainer
+ {
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ },
+ };
+ }
+
+ private ScheduledDelegate hoverExpandEvent;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Expanded.BindValueChanged(v =>
+ {
+ this.ResizeWidthTo(v.NewValue ? expandedWidth : contractedWidth, 500, Easing.OutQuint);
+ }, true);
+ }
+
+ protected override bool OnHover(HoverEvent e)
+ {
+ updateHoverExpansion();
+ return true;
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ updateHoverExpansion();
+ return base.OnMouseMove(e);
+ }
+
+ protected override void OnHoverLost(HoverLostEvent e)
+ {
+ if (hoverExpandEvent != null)
+ {
+ hoverExpandEvent?.Cancel();
+ hoverExpandEvent = null;
+
+ Expanded.Value = false;
+ return;
+ }
+
+ base.OnHoverLost(e);
+ }
+
+ private void updateHoverExpansion()
+ {
+ hoverExpandEvent?.Cancel();
+
+ if (IsHovered && !Expanded.Value)
+ hoverExpandEvent = Scheduler.AddDelayed(() => Expanded.Value = true, HoverExpansionDelay);
+ }
+ }
+}
diff --git a/osu.Game/Graphics/Containers/IExpandable.cs b/osu.Game/Graphics/Containers/IExpandable.cs
new file mode 100644
index 0000000000..593564a2f9
--- /dev/null
+++ b/osu.Game/Graphics/Containers/IExpandable.cs
@@ -0,0 +1,19 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+
+namespace osu.Game.Graphics.Containers
+{
+ ///
+ /// An interface for drawables with ability to expand/contract.
+ ///
+ public interface IExpandable : IDrawable
+ {
+ ///
+ /// Whether this drawable is in an expanded state.
+ ///
+ BindableBool Expanded { get; }
+ }
+}
diff --git a/osu.Game/Graphics/Containers/IExpandingContainer.cs b/osu.Game/Graphics/Containers/IExpandingContainer.cs
new file mode 100644
index 0000000000..eb186c96a8
--- /dev/null
+++ b/osu.Game/Graphics/Containers/IExpandingContainer.cs
@@ -0,0 +1,16 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Containers;
+
+namespace osu.Game.Graphics.Containers
+{
+ ///
+ /// A target expanding container that should be resolved by children s to propagate state changes.
+ ///
+ [Cached(typeof(IExpandingContainer))]
+ public interface IExpandingContainer : IContainer, IExpandable
+ {
+ }
+}
diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs
index f63bd53549..886ba7ef92 100644
--- a/osu.Game/Graphics/OsuColour.cs
+++ b/osu.Game/Graphics/OsuColour.cs
@@ -264,32 +264,58 @@ namespace osu.Game.Graphics
public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee");
public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff");
- ///
- /// Equivalent to 's .
- ///
- public readonly Color4 Pink3 = Color4Extensions.FromHex(@"cc3378");
+ #region "Basic" colour theme
- ///
- /// Equivalent to 's .
- ///
+ // Reference: https://www.figma.com/file/VIkXMYNPMtQem2RJg9k2iQ/Asset%2FColours?node-id=1838%3A3
+
+ // Note that the colours in this region are also defined in `OverlayColourProvider` as `Colour{0,1,2,3,4}`.
+ // The difference as to which should be used where comes down to context.
+ // If the colour in question is supposed to always match the view in which it is displayed theme-wise, use `OverlayColourProvider`.
+ // If the colour usage is special and in general differs from the surrounding view in choice of hue, use the `OsuColour` constants.
+
+ public readonly Color4 Pink0 = Color4Extensions.FromHex(@"ff99c7");
+ public readonly Color4 Pink1 = Color4Extensions.FromHex(@"ff66ab");
+ public readonly Color4 Pink2 = Color4Extensions.FromHex(@"eb4791");
+ public readonly Color4 Pink3 = Color4Extensions.FromHex(@"cc3378");
+ public readonly Color4 Pink4 = Color4Extensions.FromHex(@"6b2e49");
+
+ public readonly Color4 Purple0 = Color4Extensions.FromHex(@"b299ff");
+ public readonly Color4 Purple1 = Color4Extensions.FromHex(@"8c66ff");
+ public readonly Color4 Purple2 = Color4Extensions.FromHex(@"7047eb");
+ public readonly Color4 Purple3 = Color4Extensions.FromHex(@"5933cc");
+ public readonly Color4 Purple4 = Color4Extensions.FromHex(@"3d2e6b");
+
+ public readonly Color4 Blue0 = Color4Extensions.FromHex(@"99ddff");
+ public readonly Color4 Blue1 = Color4Extensions.FromHex(@"66ccff");
+ public readonly Color4 Blue2 = Color4Extensions.FromHex(@"47b4eb");
public readonly Color4 Blue3 = Color4Extensions.FromHex(@"3399cc");
+ public readonly Color4 Blue4 = Color4Extensions.FromHex(@"2e576b");
+
+ public readonly Color4 Green0 = Color4Extensions.FromHex(@"99ffa2");
+ public readonly Color4 Green1 = Color4Extensions.FromHex(@"66ff73");
+ public readonly Color4 Green2 = Color4Extensions.FromHex(@"47eb55");
+ public readonly Color4 Green3 = Color4Extensions.FromHex(@"33cc40");
+ public readonly Color4 Green4 = Color4Extensions.FromHex(@"2e6b33");
public readonly Color4 Lime0 = Color4Extensions.FromHex(@"ccff99");
-
- ///
- /// Equivalent to 's .
- ///
public readonly Color4 Lime1 = Color4Extensions.FromHex(@"b2ff66");
-
- ///
- /// Equivalent to 's .
- ///
+ public readonly Color4 Lime2 = Color4Extensions.FromHex(@"99eb47");
public readonly Color4 Lime3 = Color4Extensions.FromHex(@"7fcc33");
+ public readonly Color4 Lime4 = Color4Extensions.FromHex(@"4c6b2e");
- ///
- /// Equivalent to 's .
- ///
+ public readonly Color4 Orange0 = Color4Extensions.FromHex(@"ffe699");
public readonly Color4 Orange1 = Color4Extensions.FromHex(@"ffd966");
+ public readonly Color4 Orange2 = Color4Extensions.FromHex(@"ebc247");
+ public readonly Color4 Orange3 = Color4Extensions.FromHex(@"cca633");
+ public readonly Color4 Orange4 = Color4Extensions.FromHex(@"6b5c2e");
+
+ public readonly Color4 Red0 = Color4Extensions.FromHex(@"ff9b9b");
+ public readonly Color4 Red1 = Color4Extensions.FromHex(@"ff6666");
+ public readonly Color4 Red2 = Color4Extensions.FromHex(@"eb4747");
+ public readonly Color4 Red3 = Color4Extensions.FromHex(@"cc3333");
+ public readonly Color4 Red4 = Color4Extensions.FromHex(@"6b2e2e");
+
+ #endregion
// Content Background
public readonly Color4 B5 = Color4Extensions.FromHex(@"222a28");
diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs
index a39d7bfb47..b0f20de685 100644
--- a/osu.Game/Graphics/ScreenshotManager.cs
+++ b/osu.Game/Graphics/ScreenshotManager.cs
@@ -112,6 +112,8 @@ namespace osu.Game.Graphics
if (Interlocked.Decrement(ref screenShotTasks) == 0 && cursorVisibility.Value == false)
cursorVisibility.Value = true;
+ host.GetClipboard()?.SetImage(image);
+
string filename = getFilename();
if (filename == null) return;
diff --git a/osu.Game/Graphics/UserInterface/ExpandableSlider.cs b/osu.Game/Graphics/UserInterface/ExpandableSlider.cs
new file mode 100644
index 0000000000..60e83f9c81
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/ExpandableSlider.cs
@@ -0,0 +1,126 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Localisation;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osuTK;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ ///
+ /// An implementation for the UI slider bar control.
+ ///
+ public class ExpandableSlider : CompositeDrawable, IExpandable, IHasCurrentValue
+ where T : struct, IEquatable, IComparable, IConvertible
+ where TSlider : OsuSliderBar, new()
+ {
+ private readonly OsuSpriteText label;
+ private readonly TSlider slider;
+
+ private LocalisableString contractedLabelText;
+
+ ///
+ /// The label text to display when this slider is in a contracted state.
+ ///
+ public LocalisableString ContractedLabelText
+ {
+ get => contractedLabelText;
+ set
+ {
+ if (value == contractedLabelText)
+ return;
+
+ contractedLabelText = value;
+
+ if (!Expanded.Value)
+ label.Text = value;
+ }
+ }
+
+ private LocalisableString expandedLabelText;
+
+ ///
+ /// The label text to display when this slider is in an expanded state.
+ ///
+ public LocalisableString ExpandedLabelText
+ {
+ get => expandedLabelText;
+ set
+ {
+ if (value == expandedLabelText)
+ return;
+
+ expandedLabelText = value;
+
+ if (Expanded.Value)
+ label.Text = value;
+ }
+ }
+
+ public Bindable Current
+ {
+ get => slider.Current;
+ set => slider.Current = value;
+ }
+
+ public BindableBool Expanded { get; } = new BindableBool();
+
+ public override bool HandlePositionalInput => true;
+
+ public ExpandableSlider()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ InternalChild = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(0f, 10f),
+ Children = new Drawable[]
+ {
+ label = new OsuSpriteText(),
+ slider = new TSlider
+ {
+ RelativeSizeAxes = Axes.X,
+ },
+ }
+ };
+ }
+
+ [Resolved(canBeNull: true)]
+ private IExpandingContainer expandingContainer { get; set; }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
+ {
+ Expanded.Value = containerExpanded.NewValue;
+ }, true);
+
+ Expanded.BindValueChanged(v =>
+ {
+ label.Text = v.NewValue ? expandedLabelText : contractedLabelText;
+ slider.FadeTo(v.NewValue ? 1f : 0f, 500, Easing.OutQuint);
+ slider.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
+ }, true);
+ }
+ }
+
+ ///
+ /// An implementation for the UI slider bar control.
+ ///
+ public class ExpandableSlider : ExpandableSlider>
+ where T : struct, IEquatable, IComparable, IConvertible
+ {
+ }
+}
diff --git a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs
index deb2e6baf6..c6477d1781 100644
--- a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs
+++ b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs
@@ -114,7 +114,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
private class CircularBorderContainer : CircularContainer
{
- public void TransformBorderTo(SRGBColour colour)
+ public void TransformBorderTo(ColourInfo colour)
=> this.TransformTo(nameof(BorderColour), colour, 250, Easing.OutQuint);
}
}
diff --git a/osu.Game/IO/Archives/ArchiveReader.cs b/osu.Game/IO/Archives/ArchiveReader.cs
index 1d8da16c72..dab70eaf70 100644
--- a/osu.Game/IO/Archives/ArchiveReader.cs
+++ b/osu.Game/IO/Archives/ArchiveReader.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using osu.Framework.Extensions;
using osu.Framework.IO.Stores;
namespace osu.Game.IO.Archives
@@ -35,14 +36,7 @@ namespace osu.Game.IO.Archives
public virtual byte[] Get(string name)
{
using (Stream input = GetStream(name))
- {
- if (input == null)
- return null;
-
- byte[] buffer = new byte[input.Length];
- input.Read(buffer);
- return buffer;
- }
+ return input?.ReadAllBytesToArray();
}
public async Task GetAsync(string name, CancellationToken cancellationToken = default)
@@ -52,9 +46,7 @@ namespace osu.Game.IO.Archives
if (input == null)
return null;
- byte[] buffer = new byte[input.Length];
- await input.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
- return buffer;
+ return await input.ReadAllBytesToArrayAsync(cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/osu.Game/Online/API/Requests/GetBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapsRequest.cs
index 1d71e22b77..22af022659 100644
--- a/osu.Game/Online/API/Requests/GetBeatmapsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetBeatmapsRequest.cs
@@ -2,12 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
namespace osu.Game.Online.API.Requests
{
public class GetBeatmapsRequest : APIRequest
{
- private readonly int[] beatmapIds;
+ public readonly IReadOnlyList BeatmapIds;
private const int max_ids_per_request = 50;
@@ -16,9 +17,9 @@ namespace osu.Game.Online.API.Requests
if (beatmapIds.Length > max_ids_per_request)
throw new ArgumentException($"{nameof(GetBeatmapsRequest)} calls only support up to {max_ids_per_request} IDs at once");
- this.beatmapIds = beatmapIds;
+ BeatmapIds = beatmapIds;
}
- protected override string Target => "beatmaps/?ids[]=" + string.Join("&ids[]=", beatmapIds);
+ protected override string Target => "beatmaps/?ids[]=" + string.Join("&ids[]=", BeatmapIds);
}
}
diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs
index dca60e54cb..f5795141c5 100644
--- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs
@@ -112,7 +112,27 @@ namespace osu.Game.Online.API.Requests.Responses
public int OnlineID { get; set; } = -1;
public string Name => $@"{nameof(APIRuleset)} (ID: {OnlineID})";
- public string ShortName => nameof(APIRuleset);
+
+ public string ShortName
+ {
+ get
+ {
+ // TODO: this should really not exist.
+ switch (OnlineID)
+ {
+ case 0: return "osu";
+
+ case 1: return "taiko";
+
+ case 2: return "fruits";
+
+ case 3: return "mania";
+
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
public string InstantiationInfo => string.Empty;
public Ruleset CreateInstance() => throw new NotImplementedException();
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index c2393a5de5..ddd9d9a2b2 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.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 osu.Framework.Allocation;
@@ -16,6 +17,7 @@ using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Framework.Platform;
using osu.Game.Database;
+using osu.Game.Extensions;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
@@ -56,7 +58,7 @@ namespace osu.Game.Online.Leaderboards
public GlowingSpriteText ScoreText { get; private set; }
- private Container flagBadgeContainer;
+ private FillFlowContainer flagBadgeAndDateContainer;
private FillFlowContainer modsContainer;
private List statisticsLabels;
@@ -103,7 +105,7 @@ namespace osu.Game.Online.Leaderboards
content = new Container
{
RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = rank_width, },
+ Padding = new MarginPadding { Left = rank_width },
Children = new Drawable[]
{
new Container
@@ -158,32 +160,41 @@ namespace osu.Game.Online.Leaderboards
},
new FillFlowContainer
{
- Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
Children = new Drawable[]
{
- flagBadgeContainer = new Container
+ flagBadgeAndDateContainer = new FillFlowContainer
{
- Origin = Anchor.BottomLeft,
- Anchor = Anchor.BottomLeft,
- Size = new Vector2(87f, 20f),
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.Y,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(5f, 0f),
+ Width = 87f,
Masking = true,
Children = new Drawable[]
{
new UpdateableFlag(user.Country)
{
- Width = 30,
- RelativeSizeAxes = Axes.Y,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Size = new Vector2(30f, 20f),
+ },
+ new DateLabel(Score.Date)
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
},
},
},
new FillFlowContainer
{
- Origin = Anchor.BottomLeft,
- Anchor = Anchor.BottomLeft,
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Left = edge_margin },
@@ -243,7 +254,7 @@ namespace osu.Game.Online.Leaderboards
public override void Show()
{
- foreach (var d in new[] { avatar, nameLabel, ScoreText, scoreRank, flagBadgeContainer, modsContainer }.Concat(statisticsLabels))
+ foreach (var d in new[] { avatar, nameLabel, ScoreText, scoreRank, flagBadgeAndDateContainer, modsContainer }.Concat(statisticsLabels))
d.FadeOut();
Alpha = 0;
@@ -270,7 +281,7 @@ namespace osu.Game.Online.Leaderboards
using (BeginDelayedSequence(50))
{
- var drawables = new Drawable[] { flagBadgeContainer, modsContainer }.Concat(statisticsLabels).ToArray();
+ var drawables = new Drawable[] { flagBadgeAndDateContainer, modsContainer }.Concat(statisticsLabels).ToArray();
for (int i = 0; i < drawables.Length; i++)
drawables[i].FadeIn(100 + i * 50);
}
@@ -377,6 +388,17 @@ namespace osu.Game.Online.Leaderboards
public LocalisableString TooltipText { get; }
}
+ private class DateLabel : DrawableDate
+ {
+ public DateLabel(DateTimeOffset date)
+ : base(date)
+ {
+ Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold, italics: true);
+ }
+
+ protected override string Format() => Date.ToShortRelativeTime(TimeSpan.FromSeconds(30));
+ }
+
public class LeaderboardScoreStatistic
{
public IconUsage Icon;
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
index 903aaa89e3..a56cc7f8d6 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -727,38 +727,17 @@ namespace osu.Game.Online.Multiplayer
RoomUpdated?.Invoke();
}
- private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item)
+ private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item) => new PlaylistItem(new APIBeatmap { OnlineID = item.BeatmapID })
{
- var ruleset = Rulesets.GetRuleset(item.RulesetID);
-
- Debug.Assert(ruleset != null);
-
- var rulesetInstance = ruleset.CreateInstance();
-
- var playlistItem = new PlaylistItem
- {
- ID = item.ID,
- BeatmapID = item.BeatmapID,
- OwnerID = item.OwnerID,
- Ruleset = { Value = ruleset },
- Expired = item.Expired,
- PlaylistOrder = item.PlaylistOrder,
- PlayedAt = item.PlayedAt
- };
-
- playlistItem.RequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance)));
- playlistItem.AllowedMods.AddRange(item.AllowedMods.Select(m => m.ToMod(rulesetInstance)));
-
- return playlistItem;
- }
-
- ///
- /// Retrieves a from an online source.
- ///
- /// The beatmap ID.
- /// A token to cancel the request.
- /// The retrieval task.
- public abstract Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default);
+ ID = item.ID,
+ OwnerID = item.OwnerID,
+ RulesetID = item.RulesetID,
+ Expired = item.Expired,
+ PlaylistOrder = item.PlaylistOrder,
+ PlayedAt = item.PlayedAt,
+ RequiredMods = item.RequiredMods.ToArray(),
+ AllowedMods = item.AllowedMods.ToArray()
+ };
///
/// For the provided user ID, update whether the user is included in .
diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
index 3794bec228..7e62908ecd 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -4,14 +4,13 @@
#nullable enable
using System.Collections.Generic;
+using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osu.Game.Database;
using osu.Game.Online.API;
-using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
namespace osu.Game.Online.Multiplayer
@@ -29,9 +28,6 @@ namespace osu.Game.Online.Multiplayer
private HubConnection? connection => connector?.CurrentConnection;
- [Resolved]
- private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
-
public OnlineMultiplayerClient(EndpointConfiguration endpoints)
{
endpoint = endpoints.MultiplayerEndpointUrl;
@@ -79,6 +75,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.FromCanceled(new CancellationToken(true));
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.JoinRoomWithPassword), roomId, password ?? string.Empty);
}
@@ -87,6 +85,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.FromCanceled(new CancellationToken(true));
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.LeaveRoom));
}
@@ -95,6 +95,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.TransferHost), userId);
}
@@ -103,6 +105,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.KickUser), userId);
}
@@ -111,6 +115,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeSettings), settings);
}
@@ -119,6 +125,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeState), newState);
}
@@ -127,6 +135,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability);
}
@@ -135,6 +145,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeUserMods), newMods);
}
@@ -143,6 +155,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.SendMatchRequest), request);
}
@@ -151,6 +165,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.StartMatch));
}
@@ -159,6 +175,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.AbortGameplay));
}
@@ -167,6 +185,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.AddPlaylistItem), item);
}
@@ -175,6 +195,8 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.InvokeAsync(nameof(IMultiplayerServer.EditPlaylistItem), item);
}
@@ -183,12 +205,9 @@ namespace osu.Game.Online.Multiplayer
if (!IsConnected.Value)
return Task.CompletedTask;
- return connection.InvokeAsync(nameof(IMultiplayerServer.RemovePlaylistItem), playlistItemId);
- }
+ Debug.Assert(connection != null);
- public override Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default)
- {
- return beatmapLookupCache.GetBeatmapAsync(beatmapId, cancellationToken);
+ return connection.InvokeAsync(nameof(IMultiplayerServer.RemovePlaylistItem), playlistItemId);
}
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs
index 8ec073ff1e..388a02f798 100644
--- a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs
+++ b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs
@@ -63,11 +63,11 @@ namespace osu.Game.Online.Rooms
{
ID = item.ID;
OwnerID = item.OwnerID;
- BeatmapID = item.BeatmapID;
- BeatmapChecksum = item.Beatmap.Value?.MD5Hash ?? string.Empty;
+ BeatmapID = item.Beatmap.OnlineID;
+ BeatmapChecksum = item.Beatmap.MD5Hash;
RulesetID = item.RulesetID;
- RequiredMods = item.RequiredMods.Select(m => new APIMod(m)).ToArray();
- AllowedMods = item.AllowedMods.Select(m => new APIMod(m)).ToArray();
+ RequiredMods = item.RequiredMods.ToArray();
+ AllowedMods = item.AllowedMods.ToArray();
Expired = item.Expired;
PlaylistOrder = item.PlaylistOrder ?? 0;
PlayedAt = item.PlayedAt;
diff --git a/osu.Game/Online/Rooms/MultiplayerScore.cs b/osu.Game/Online/Rooms/MultiplayerScore.cs
index f1bb57bd9d..85327be037 100644
--- a/osu.Game/Online/Rooms/MultiplayerScore.cs
+++ b/osu.Game/Online/Rooms/MultiplayerScore.cs
@@ -65,7 +65,11 @@ namespace osu.Game.Online.Rooms
public ScoreInfo CreateScoreInfo(RulesetStore rulesets, PlaylistItem playlistItem, [NotNull] BeatmapInfo beatmap)
{
- var rulesetInstance = playlistItem.Ruleset.Value.CreateInstance();
+ var ruleset = rulesets.GetRuleset(playlistItem.RulesetID);
+ if (ruleset == null)
+ throw new InvalidOperationException($"Couldn't create score with unknown ruleset: {playlistItem.RulesetID}");
+
+ var rulesetInstance = ruleset.CreateInstance();
var scoreInfo = new ScoreInfo
{
diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs
index c67cbade6a..07506ba1f0 100644
--- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs
+++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs
@@ -4,14 +4,17 @@
using System;
using System.Diagnostics;
using System.Linq;
+using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Database;
+using osu.Game.Online.API.Requests.Responses;
using Realms;
namespace osu.Game.Online.Rooms
@@ -32,6 +35,9 @@ namespace osu.Game.Online.Rooms
[Resolved]
private RealmAccess realm { get; set; } = null!;
+ [Resolved]
+ private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
+
///
/// The availability state of the currently selected playlist item.
///
@@ -40,10 +46,9 @@ namespace osu.Game.Online.Rooms
private readonly Bindable availability = new Bindable(BeatmapAvailability.NotDownloaded());
private ScheduledDelegate progressUpdate;
-
private BeatmapDownloadTracker downloadTracker;
-
private IDisposable realmSubscription;
+ private APIBeatmap selectedBeatmap;
protected override void LoadComplete()
{
@@ -57,40 +62,55 @@ namespace osu.Game.Online.Rooms
return;
downloadTracker?.RemoveAndDisposeImmediately();
+ selectedBeatmap = null;
- Debug.Assert(item.NewValue.Beatmap.Value.BeatmapSet != null);
-
- downloadTracker = new BeatmapDownloadTracker(item.NewValue.Beatmap.Value.BeatmapSet);
-
- AddInternal(downloadTracker);
-
- downloadTracker.State.BindValueChanged(_ => Scheduler.AddOnce(updateAvailability), true);
- downloadTracker.Progress.BindValueChanged(_ =>
+ beatmapLookupCache.GetBeatmapAsync(item.NewValue.Beatmap.OnlineID).ContinueWith(task => Schedule(() =>
{
- if (downloadTracker.State.Value != DownloadState.Downloading)
- return;
+ var beatmap = task.GetResultSafely();
- // incoming progress changes are going to be at a very high rate.
- // we don't want to flood the network with this, so rate limit how often we send progress updates.
- if (progressUpdate?.Completed != false)
- progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500);
- }, true);
-
- // handles changes to hash that didn't occur from the import process (ie. a user editing the beatmap in the editor, somehow).
- realmSubscription?.Dispose();
- realmSubscription = realm.RegisterForNotifications(r => filteredBeatmaps(), (items, changes, ___) =>
- {
- if (changes == null)
- return;
-
- Scheduler.AddOnce(updateAvailability);
- });
+ if (SelectedItem.Value?.Beatmap.OnlineID == beatmap.OnlineID)
+ {
+ selectedBeatmap = beatmap;
+ beginTracking();
+ }
+ }), TaskContinuationOptions.OnlyOnRanToCompletion);
}, true);
}
+ private void beginTracking()
+ {
+ Debug.Assert(selectedBeatmap.BeatmapSet != null);
+
+ downloadTracker = new BeatmapDownloadTracker(selectedBeatmap.BeatmapSet);
+
+ AddInternal(downloadTracker);
+
+ downloadTracker.State.BindValueChanged(_ => Scheduler.AddOnce(updateAvailability), true);
+ downloadTracker.Progress.BindValueChanged(_ =>
+ {
+ if (downloadTracker.State.Value != DownloadState.Downloading)
+ return;
+
+ // incoming progress changes are going to be at a very high rate.
+ // we don't want to flood the network with this, so rate limit how often we send progress updates.
+ if (progressUpdate?.Completed != false)
+ progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500);
+ }, true);
+
+ // handles changes to hash that didn't occur from the import process (ie. a user editing the beatmap in the editor, somehow).
+ realmSubscription?.Dispose();
+ realmSubscription = realm.RegisterForNotifications(r => filteredBeatmaps(), (items, changes, ___) =>
+ {
+ if (changes == null)
+ return;
+
+ Scheduler.AddOnce(updateAvailability);
+ });
+ }
+
private void updateAvailability()
{
- if (downloadTracker == null || SelectedItem.Value == null)
+ if (downloadTracker == null || selectedBeatmap == null)
return;
switch (downloadTracker.State.Value)
@@ -108,12 +128,12 @@ namespace osu.Game.Online.Rooms
break;
case DownloadState.LocallyAvailable:
- bool hashMatches = filteredBeatmaps().Any();
+ bool available = filteredBeatmaps().Any();
- availability.Value = hashMatches ? BeatmapAvailability.LocallyAvailable() : BeatmapAvailability.NotDownloaded();
+ availability.Value = available ? BeatmapAvailability.LocallyAvailable() : BeatmapAvailability.NotDownloaded();
// only display a message to the user if a download seems to have just completed.
- if (!hashMatches && downloadTracker.Progress.Value == 1)
+ if (!available && downloadTracker.Progress.Value == 1)
Logger.Log("The imported beatmap set does not match the online version.", LoggingTarget.Runtime, LogLevel.Important);
break;
@@ -125,8 +145,8 @@ namespace osu.Game.Online.Rooms
private IQueryable filteredBeatmaps()
{
- int onlineId = SelectedItem.Value.Beatmap.Value.OnlineID;
- string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash;
+ int onlineId = selectedBeatmap.OnlineID;
+ string checksum = selectedBeatmap.MD5Hash;
return realm.Realm
.All()
diff --git a/osu.Game/Online/Rooms/PlaylistExtensions.cs b/osu.Game/Online/Rooms/PlaylistExtensions.cs
index e78f91f20b..34c93bd9e0 100644
--- a/osu.Game/Online/Rooms/PlaylistExtensions.cs
+++ b/osu.Game/Online/Rooms/PlaylistExtensions.cs
@@ -41,6 +41,6 @@ namespace osu.Game.Online.Rooms
}
public static string GetTotalDuration(this BindableList playlist) =>
- playlist.Select(p => p.Beatmap.Value.Length).Sum().Milliseconds().Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 2);
+ playlist.Select(p => p.Beatmap.Length).Sum().Milliseconds().Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 2);
}
}
diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs
index 83a70c405b..33718f050b 100644
--- a/osu.Game/Online/Rooms/PlaylistItem.cs
+++ b/osu.Game/Online/Rooms/PlaylistItem.cs
@@ -1,8 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+#nullable enable
+
using System;
-using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json;
@@ -10,11 +11,10 @@ using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
-using osu.Game.Rulesets;
-using osu.Game.Rulesets.Mods;
namespace osu.Game.Online.Rooms
{
+ [JsonObject(MemberSerialization.OptIn)]
public class PlaylistItem : IEquatable
{
[JsonProperty("id")]
@@ -23,9 +23,6 @@ namespace osu.Game.Online.Rooms
[JsonProperty("owner_id")]
public int OwnerID { get; set; }
- [JsonProperty("beatmap_id")]
- public int BeatmapID { get; set; }
-
[JsonProperty("ruleset_id")]
public int RulesetID { get; set; }
@@ -41,78 +38,50 @@ namespace osu.Game.Online.Rooms
[JsonProperty("played_at")]
public DateTimeOffset? PlayedAt { get; set; }
+ [JsonProperty("allowed_mods")]
+ public APIMod[] AllowedMods { get; set; } = Array.Empty();
+
+ [JsonProperty("required_mods")]
+ public APIMod[] RequiredMods { get; set; } = Array.Empty();
+
+ ///
+ /// Used for deserialising from the API.
+ ///
+ [JsonProperty("beatmap")]
+ private APIBeatmap apiBeatmap
+ {
+ // This getter is required/used internally by JSON.NET during deserialisation to do default-value comparisons. It is never used during serialisation (see: ShouldSerializeapiBeatmap()).
+ // It will always return a null value on deserialisation, which JSON.NET will handle gracefully.
+ get => (APIBeatmap)Beatmap;
+ set => Beatmap = value;
+ }
+
+ ///
+ /// Used for serialising to the API.
+ ///
+ [JsonProperty("beatmap_id")]
+ private int onlineBeatmapId => Beatmap.OnlineID;
+
+ [JsonIgnore]
+ public IBeatmapInfo Beatmap { get; set; } = null!;
+
[JsonIgnore]
public IBindable Valid => valid;
private readonly Bindable valid = new BindableBool(true);
- [JsonIgnore]
- public readonly Bindable Beatmap = new Bindable();
-
- [JsonIgnore]
- public readonly Bindable Ruleset = new Bindable();
-
- [JsonIgnore]
- public readonly BindableList AllowedMods = new BindableList();
-
- [JsonIgnore]
- public readonly BindableList RequiredMods = new BindableList();
-
- [JsonProperty("beatmap")]
- private APIBeatmap apiBeatmap { get; set; }
-
- private APIMod[] allowedModsBacking;
-
- [JsonProperty("allowed_mods")]
- private APIMod[] allowedMods
+ [JsonConstructor]
+ private PlaylistItem()
{
- get => AllowedMods.Select(m => new APIMod(m)).ToArray();
- set => allowedModsBacking = value;
}
- private APIMod[] requiredModsBacking;
-
- [JsonProperty("required_mods")]
- private APIMod[] requiredMods
+ public PlaylistItem(IBeatmapInfo beatmap)
{
- get => RequiredMods.Select(m => new APIMod(m)).ToArray();
- set => requiredModsBacking = value;
- }
-
- public PlaylistItem()
- {
- Beatmap.BindValueChanged(beatmap => BeatmapID = beatmap.NewValue?.OnlineID ?? -1);
- Ruleset.BindValueChanged(ruleset => RulesetID = ruleset.NewValue?.OnlineID ?? 0);
+ Beatmap = beatmap;
}
public void MarkInvalid() => valid.Value = false;
- public void MapObjects(IRulesetStore rulesets)
- {
- Beatmap.Value ??= apiBeatmap;
- Ruleset.Value ??= rulesets.GetRuleset(RulesetID);
-
- Debug.Assert(Ruleset.Value != null);
-
- Ruleset rulesetInstance = Ruleset.Value.CreateInstance();
-
- if (allowedModsBacking != null)
- {
- AllowedMods.Clear();
- AllowedMods.AddRange(allowedModsBacking.Select(m => m.ToMod(rulesetInstance)));
-
- allowedModsBacking = null;
- }
-
- if (requiredModsBacking != null)
- {
- RequiredMods.Clear();
- RequiredMods.AddRange(requiredModsBacking.Select(m => m.ToMod(rulesetInstance)));
-
- requiredModsBacking = null;
- }
- }
-
#region Newtonsoft.Json implicit ShouldSerialize() methods
// The properties in this region are used implicitly by Newtonsoft.Json to not serialise certain fields in some cases.
@@ -128,12 +97,25 @@ namespace osu.Game.Online.Rooms
#endregion
- public bool Equals(PlaylistItem other)
+ public PlaylistItem With(IBeatmapInfo beatmap) => new PlaylistItem(beatmap)
+ {
+ ID = ID,
+ OwnerID = OwnerID,
+ RulesetID = RulesetID,
+ Expired = Expired,
+ PlaylistOrder = PlaylistOrder,
+ PlayedAt = PlayedAt,
+ AllowedMods = AllowedMods,
+ RequiredMods = RequiredMods,
+ valid = { Value = Valid.Value },
+ };
+
+ public bool Equals(PlaylistItem? other)
=> ID == other?.ID
- && BeatmapID == other.BeatmapID
+ && Beatmap.OnlineID == other.Beatmap.OnlineID
&& RulesetID == other.RulesetID
&& Expired == other.Expired
- && allowedMods.SequenceEqual(other.allowedMods)
- && requiredMods.SequenceEqual(other.requiredMods);
+ && AllowedMods.SequenceEqual(other.AllowedMods)
+ && RequiredMods.SequenceEqual(other.RequiredMods);
}
}
diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs
index bbe854f2dd..a328f8e8c0 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -168,8 +168,7 @@ namespace osu.Game.Online.Rooms
RoomID.Value = other.RoomID.Value;
Name.Value = other.Name.Value;
- if (other.Category.Value != RoomCategory.Spotlight)
- Category.Value = other.Category.Value;
+ Category.Value = other.Category.Value;
if (other.Host.Value != null && Host.Value?.Id != other.Host.Value.Id)
Host.Value = other.Host.Value;
diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
index 753796158e..ddde69c627 100644
--- a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
+++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
@@ -3,6 +3,7 @@
#nullable enable
+using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using osu.Framework.Allocation;
@@ -51,6 +52,8 @@ namespace osu.Game.Online.Spectator
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state);
}
@@ -59,6 +62,8 @@ namespace osu.Game.Online.Spectator
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.SendAsync(nameof(ISpectatorServer.SendFrameData), data);
}
@@ -67,6 +72,8 @@ namespace osu.Game.Online.Spectator
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.SendAsync(nameof(ISpectatorServer.EndPlaySession), state);
}
@@ -75,6 +82,8 @@ namespace osu.Game.Online.Spectator
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.SendAsync(nameof(ISpectatorServer.StartWatchingUser), userId);
}
@@ -83,6 +92,8 @@ namespace osu.Game.Online.Spectator
if (!IsConnected.Value)
return Task.CompletedTask;
+ Debug.Assert(connection != null);
+
return connection.SendAsync(nameof(ISpectatorServer.EndWatchingUser), userId);
}
}
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs
index fb9e1c0420..51dad100c2 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Allocation;
+using osu.Game.Graphics;
using osu.Game.Resources.Localisation.Web;
using osuTK.Graphics;
@@ -33,7 +35,10 @@ namespace osu.Game.Overlays.BeatmapListing
{
}
- protected override Color4 GetStateColour() => OverlayColourProvider.Orange.Colour1;
+ [Resolved]
+ private OsuColour colours { get; set; }
+
+ protected override Color4 GetStateColour() => colours.Orange1;
}
}
}
diff --git a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs
index 9274cf20aa..52dfcad2cc 100644
--- a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs
+++ b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs
@@ -44,7 +44,14 @@ namespace osu.Game.Overlays.BeatmapListing
});
Enabled.Value = true;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
updateState();
+ FinishTransforms(true);
}
protected override bool OnHover(HoverEvent e)
diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs
index ba78592ed2..21d1d1172c 100644
--- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs
+++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f },
Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(),
Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold),
- Colour = OverlayColourProvider.Orange.Colour2,
+ Colour = colours.Orange2
}
}
};
diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs
index fdee0799ff..1be987cde2 100644
--- a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs
+++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f },
Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(),
Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold),
- Colour = OverlayColourProvider.Blue.Colour1,
+ Colour = colours.Blue1
}
}
};
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs
index ff1d3490b4..5018fb8c70 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs
@@ -2,9 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using Humanizer;
+using osu.Game.Extensions;
using osu.Game.Graphics;
-using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
@@ -16,41 +15,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
}
protected override string Format()
- {
- var now = DateTime.Now;
- var difference = now - Date;
-
- // web uses momentjs's custom locales to format the date for the purposes of the scoreboard.
- // this is intended to be a best-effort, more legible approximation of that.
- // compare:
- // * https://github.com/ppy/osu-web/blob/a8f5a68fb435cb19a4faa4c7c4bce08c4f096933/resources/assets/lib/scoreboard-time.tsx
- // * https://momentjs.com/docs/#/customization/ (reference for the customisation format)
-
- // TODO: support localisation (probably via `CommonStrings.CountHours()` etc.)
- // requires pluralisable string support framework-side
-
- if (difference.TotalHours < 1)
- return CommonStrings.TimeNow.ToString();
- if (difference.TotalDays < 1)
- return "hr".ToQuantity((int)difference.TotalHours);
-
- // this is where this gets more complicated because of how the calendar works.
- // since there's no `TotalMonths` / `TotalYears`, we have to iteratively add months/years
- // and test against cutoff dates to determine how many months/years to show.
-
- if (Date > now.AddMonths(-1))
- return difference.TotalDays < 2 ? "1dy" : $"{(int)difference.TotalDays}dys";
-
- for (int months = 1; months <= 11; ++months)
- {
- if (Date > now.AddMonths(-(months + 1)))
- return months == 1 ? "1mo" : $"{months}mos";
- }
-
- int years = 1;
- while (Date <= now.AddYears(-(years + 1)))
- years += 1;
- return years == 1 ? "1yr" : $"{years}yrs";
- }
+ => Date.ToShortRelativeTime(TimeSpan.FromHours(1));
}
}
diff --git a/osu.Game/Overlays/ExpandingButtonContainer.cs b/osu.Game/Overlays/ExpandingButtonContainer.cs
deleted file mode 100644
index 4eb8c47a1f..0000000000
--- a/osu.Game/Overlays/ExpandingButtonContainer.cs
+++ /dev/null
@@ -1,141 +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.Linq;
-using osu.Framework;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Input.Events;
-using osu.Framework.Testing;
-using osu.Framework.Threading;
-using osu.Game.Graphics.Containers;
-using osu.Game.Graphics.UserInterface;
-using osuTK;
-
-namespace osu.Game.Overlays
-{
- public abstract class ExpandingButtonContainer : Container, IStateful
- {
- private readonly float contractedWidth;
- private readonly float expandedWidth;
-
- public event Action StateChanged;
-
- protected override Container Content => FillFlow;
-
- protected FillFlowContainer FillFlow { get; }
-
- protected ExpandingButtonContainer(float contractedWidth, float expandedWidth)
- {
- this.contractedWidth = contractedWidth;
- this.expandedWidth = expandedWidth;
-
- RelativeSizeAxes = Axes.Y;
- Width = contractedWidth;
-
- InternalChildren = new Drawable[]
- {
- new SidebarScrollContainer
- {
- Children = new[]
- {
- FillFlow = new FillFlowContainer
- {
- Origin = Anchor.CentreLeft,
- Anchor = Anchor.CentreLeft,
- AutoSizeAxes = Axes.Y,
- RelativeSizeAxes = Axes.X,
- Direction = FillDirection.Vertical,
- }
- }
- },
- };
- }
-
- private ScheduledDelegate expandEvent;
- private ExpandedState state;
-
- protected override bool OnHover(HoverEvent e)
- {
- queueExpandIfHovering();
- return true;
- }
-
- protected override void OnHoverLost(HoverLostEvent e)
- {
- expandEvent?.Cancel();
- hoveredButton = null;
- State = ExpandedState.Contracted;
-
- base.OnHoverLost(e);
- }
-
- protected override bool OnMouseMove(MouseMoveEvent e)
- {
- queueExpandIfHovering();
- return base.OnMouseMove(e);
- }
-
- private class SidebarScrollContainer : OsuScrollContainer
- {
- public SidebarScrollContainer()
- {
- RelativeSizeAxes = Axes.Both;
- ScrollbarVisible = false;
- }
- }
-
- public ExpandedState State
- {
- get => state;
- set
- {
- expandEvent?.Cancel();
-
- if (state == value) return;
-
- state = value;
-
- switch (state)
- {
- default:
- this.ResizeTo(new Vector2(contractedWidth, Height), 500, Easing.OutQuint);
- break;
-
- case ExpandedState.Expanded:
- this.ResizeTo(new Vector2(expandedWidth, Height), 500, Easing.OutQuint);
- break;
- }
-
- StateChanged?.Invoke(State);
- }
- }
-
- private Drawable hoveredButton;
-
- private void queueExpandIfHovering()
- {
- // if the same button is hovered, let the scheduled expand play out..
- if (hoveredButton?.IsHovered == true)
- return;
-
- // ..otherwise check whether a new button is hovered, and if so, queue a new hover operation.
-
- // usually we wouldn't use ChildrenOfType in implementations, but this is the simplest way
- // to handle cases like the editor where the buttons may be nested within a child hierarchy.
- hoveredButton = FillFlow.ChildrenOfType().FirstOrDefault(c => c.IsHovered);
-
- expandEvent?.Cancel();
-
- if (hoveredButton?.IsHovered == true && State != ExpandedState.Expanded)
- expandEvent = Scheduler.AddDelayed(() => State = ExpandedState.Expanded, 750);
- }
- }
-
- public enum ExpandedState
- {
- Contracted,
- Expanded,
- }
-}
diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs
index d1e5bfe809..481abd48ab 100644
--- a/osu.Game/Overlays/Login/LoginPanel.cs
+++ b/osu.Game/Overlays/Login/LoginPanel.cs
@@ -183,7 +183,8 @@ namespace osu.Game.Overlays.Login
break;
}
- if (form != null) GetContainingInputManager()?.ChangeFocus(form);
+ if (form != null)
+ ScheduleAfterChildren(() => GetContainingInputManager()?.ChangeFocus(form));
});
public override bool AcceptsFocus => true;
diff --git a/osu.Game/Overlays/LoginOverlay.cs b/osu.Game/Overlays/LoginOverlay.cs
index f3562aa6d9..9b2d7ca1ee 100644
--- a/osu.Game/Overlays/LoginOverlay.cs
+++ b/osu.Game/Overlays/LoginOverlay.cs
@@ -78,7 +78,7 @@ namespace osu.Game.Overlays
panel.Bounding = true;
this.FadeIn(transition_time, Easing.OutQuint);
- GetContainingInputManager().ChangeFocus(panel);
+ ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(panel));
}
protected override void PopOut()
diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs
index e7b3e6d873..7bddb924a0 100644
--- a/osu.Game/Overlays/OverlayColourProvider.cs
+++ b/osu.Game/Overlays/OverlayColourProvider.cs
@@ -11,20 +11,16 @@ namespace osu.Game.Overlays
{
private readonly OverlayColourScheme colourScheme;
- public static OverlayColourProvider Red { get; } = new OverlayColourProvider(OverlayColourScheme.Red);
- public static OverlayColourProvider Pink { get; } = new OverlayColourProvider(OverlayColourScheme.Pink);
- public static OverlayColourProvider Orange { get; } = new OverlayColourProvider(OverlayColourScheme.Orange);
- public static OverlayColourProvider Lime { get; } = new OverlayColourProvider(OverlayColourScheme.Lime);
- public static OverlayColourProvider Green { get; } = new OverlayColourProvider(OverlayColourScheme.Green);
- public static OverlayColourProvider Purple { get; } = new OverlayColourProvider(OverlayColourScheme.Purple);
- public static OverlayColourProvider Blue { get; } = new OverlayColourProvider(OverlayColourScheme.Blue);
- public static OverlayColourProvider Plum { get; } = new OverlayColourProvider(OverlayColourScheme.Plum);
-
public OverlayColourProvider(OverlayColourScheme colourScheme)
{
this.colourScheme = colourScheme;
}
+ // Note that the following five colours are also defined in `OsuColour` as `{colourScheme}{0,1,2,3,4}`.
+ // The difference as to which should be used where comes down to context.
+ // If the colour in question is supposed to always match the view in which it is displayed theme-wise, use `OverlayColourProvider`.
+ // If the colour usage is special and in general differs from the surrounding view in choice of hue, use the `OsuColour` constants.
+ public Color4 Colour0 => getColour(1, 0.8f);
public Color4 Colour1 => getColour(1, 0.7f);
public Color4 Colour2 => getColour(0.8f, 0.6f);
public Color4 Colour3 => getColour(0.6f, 0.5f);
diff --git a/osu.Game/Overlays/Settings/SettingsSidebar.cs b/osu.Game/Overlays/Settings/SettingsSidebar.cs
index e6ce90c33e..4e6a1eb914 100644
--- a/osu.Game/Overlays/Settings/SettingsSidebar.cs
+++ b/osu.Game/Overlays/Settings/SettingsSidebar.cs
@@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Settings
{
diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs
index ba7118cffe..b11b6fde27 100644
--- a/osu.Game/Overlays/SettingsPanel.cs
+++ b/osu.Game/Overlays/SettingsPanel.cs
@@ -265,7 +265,7 @@ namespace osu.Game.Overlays
return;
SectionsContainer.ScrollTo(section);
- Sidebar.State = ExpandedState.Contracted;
+ Sidebar.Expanded.Value = false;
},
};
}
diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs
index ca0980a9c9..08321f68fe 100644
--- a/osu.Game/Overlays/SettingsToolboxGroup.cs
+++ b/osu.Game/Overlays/SettingsToolboxGroup.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics;
@@ -11,6 +12,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Framework.Layout;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
@@ -18,7 +20,7 @@ using osuTK.Graphics;
namespace osu.Game.Overlays
{
- public abstract class SettingsToolboxGroup : Container
+ public class SettingsToolboxGroup : Container, IExpandable
{
private const float transition_duration = 250;
private const int container_width = 270;
@@ -34,30 +36,7 @@ namespace osu.Game.Overlays
private readonly FillFlowContainer content;
private readonly IconButton button;
- private bool expanded = true;
-
- public bool Expanded
- {
- get => expanded;
- set
- {
- if (expanded == value) return;
-
- expanded = value;
-
- content.ClearTransforms();
-
- if (expanded)
- content.AutoSizeAxes = Axes.Y;
- else
- {
- content.AutoSizeAxes = Axes.None;
- content.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
- }
-
- updateExpanded();
- }
- }
+ public BindableBool Expanded { get; } = new BindableBool(true);
private Color4 expandedColour;
@@ -67,7 +46,7 @@ namespace osu.Game.Overlays
/// Create a new instance.
///
/// The title to be displayed in the header of this group.
- protected SettingsToolboxGroup(string title)
+ public SettingsToolboxGroup(string title)
{
AutoSizeAxes = Axes.Y;
Width = container_width;
@@ -115,7 +94,7 @@ namespace osu.Game.Overlays
Position = new Vector2(-15, 0),
Icon = FontAwesome.Solid.Bars,
Scale = new Vector2(0.75f),
- Action = () => Expanded = !Expanded,
+ Action = () => Expanded.Toggle(),
},
}
},
@@ -155,23 +134,58 @@ namespace osu.Game.Overlays
headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint);
}
+ [Resolved(canBeNull: true)]
+ private IExpandingContainer expandingContainer { get; set; }
+
+ private bool expandedByContainer;
+
protected override void LoadComplete()
{
base.LoadComplete();
- this.Delay(600).FadeTo(inactive_alpha, fade_duration, Easing.OutQuint);
- updateExpanded();
+ expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
+ {
+ if (containerExpanded.NewValue && !Expanded.Value)
+ {
+ Expanded.Value = true;
+ expandedByContainer = true;
+ }
+ else if (!containerExpanded.NewValue && expandedByContainer)
+ {
+ Expanded.Value = false;
+ expandedByContainer = false;
+ }
+
+ updateActiveState();
+ }, true);
+
+ Expanded.BindValueChanged(v =>
+ {
+ content.ClearTransforms();
+
+ if (v.NewValue)
+ content.AutoSizeAxes = Axes.Y;
+ else
+ {
+ content.AutoSizeAxes = Axes.None;
+ content.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
+ }
+
+ button.FadeColour(Expanded.Value ? expandedColour : Color4.White, 200, Easing.InOutQuint);
+ }, true);
+
+ this.Delay(600).Schedule(updateActiveState);
}
protected override bool OnHover(HoverEvent e)
{
- this.FadeIn(fade_duration, Easing.OutQuint);
+ updateActiveState();
return false;
}
protected override void OnHoverLost(HoverLostEvent e)
{
- this.FadeTo(inactive_alpha, fade_duration, Easing.OutQuint);
+ updateActiveState();
base.OnHoverLost(e);
}
@@ -181,7 +195,10 @@ namespace osu.Game.Overlays
expandedColour = colours.Yellow;
}
- private void updateExpanded() => button.FadeColour(expanded ? expandedColour : Color4.White, 200, Easing.InOutQuint);
+ private void updateActiveState()
+ {
+ this.FadeTo(IsHovered || expandingContainer?.Expanded.Value == true ? 1 : inactive_alpha, fade_duration, Easing.OutQuint);
+ }
protected override Container Content => content;
diff --git a/osu.Game/Overlays/Volume/MuteButton.cs b/osu.Game/Overlays/Volume/MuteButton.cs
index bcc9394aba..e9d3b31207 100644
--- a/osu.Game/Overlays/Volume/MuteButton.cs
+++ b/osu.Game/Overlays/Volume/MuteButton.cs
@@ -79,13 +79,13 @@ namespace osu.Game.Overlays.Volume
protected override bool OnHover(HoverEvent e)
{
- Content.TransformTo, SRGBColour>("BorderColour", hoveredColour, 500, Easing.OutQuint);
+ Content.TransformTo, ColourInfo>("BorderColour", hoveredColour, 500, Easing.OutQuint);
return false;
}
protected override void OnHoverLost(HoverLostEvent e)
{
- Content.TransformTo, SRGBColour>("BorderColour", unhoveredColour, 500, Easing.OutQuint);
+ Content.TransformTo, ColourInfo>("BorderColour", unhoveredColour, 500, Easing.OutQuint);
}
}
}
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
index 991b567f57..ec3d22b67a 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Difficulty
public Mod[] Mods { get; set; }
///
- /// The combined star rating of all skill.
+ /// The combined star rating of all skills.
///
[JsonProperty("star_rating", Order = -3)]
public double StarRating { get; set; }
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 92ea2db338..39783cc8bb 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -13,7 +13,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
-using osu.Game.Overlays;
+using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 79861c0ecc..d5a5aa4592 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Scoring
private double getScore(ScoringMode mode)
{
- return GetScore(mode, maxAchievableCombo,
+ return GetScore(mode,
calculateAccuracyRatio(baseScore),
calculateComboRatio(HighestCombo.Value),
scoreResultCounts);
@@ -222,12 +222,11 @@ namespace osu.Game.Rulesets.Scoring
/// Computes the total score.
///
/// The to compute the total score in.
- /// The maximum combo achievable in the beatmap.
/// The accuracy percentage achieved by the player.
- /// The proportion of achieved by the player.
+ /// The proportion of the max combo achieved by the player.
/// Any statistics to be factored in.
/// The total score.
- public double GetScore(ScoringMode mode, int maxCombo, double accuracyRatio, double comboRatio, Dictionary statistics)
+ public double GetScore(ScoringMode mode, double accuracyRatio, double comboRatio, Dictionary statistics)
{
switch (mode)
{
@@ -238,10 +237,16 @@ namespace osu.Game.Rulesets.Scoring
return (max_score * (accuracyScore + comboScore) + getBonusScore(statistics)) * scoreMultiplier;
case ScoringMode.Classic:
+ int totalHitObjects = statistics.Where(k => k.Key >= HitResult.Miss && k.Key <= HitResult.Perfect).Sum(k => k.Value);
+
+ // If there are no hitobjects then the beatmap can be composed of only ticks or spinners, so ensure we don't multiply by 0 at all times.
+ if (totalHitObjects == 0)
+ totalHitObjects = 1;
+
// This gives a similar feeling to osu!stable scoring (ScoreV1) while keeping classic scoring as only a constant multiple of standardised scoring.
// The invariant is important to ensure that scores don't get re-ordered on leaderboards between the two scoring modes.
- double scaledStandardised = GetScore(ScoringMode.Standardised, maxCombo, accuracyRatio, comboRatio, statistics) / max_score;
- return Math.Pow(scaledStandardised * (maxCombo + 1), 2) * 18;
+ double scaledStandardised = GetScore(ScoringMode.Standardised, accuracyRatio, comboRatio, statistics) / max_score;
+ return Math.Pow(scaledStandardised * totalHitObjects, 2) * 36;
}
}
@@ -265,7 +270,7 @@ namespace osu.Game.Rulesets.Scoring
computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value;
}
- return GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), statistics);
+ return GetScore(mode, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), statistics);
}
///
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 532c6b42a3..963c4a77ca 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -184,7 +184,7 @@ namespace osu.Game.Scoring
var scoreProcessor = ruleset.CreateScoreProcessor();
scoreProcessor.Mods.Value = score.Mods;
- return (long)Math.Round(scoreProcessor.GetScore(mode, beatmapMaxCombo, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics));
+ return (long)Math.Round(scoreProcessor.GetScore(mode, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics));
}
///
diff --git a/osu.Game/Screens/Edit/CreateNewDifficultyDialog.cs b/osu.Game/Screens/Edit/CreateNewDifficultyDialog.cs
new file mode 100644
index 0000000000..aa6ca280ee
--- /dev/null
+++ b/osu.Game/Screens/Edit/CreateNewDifficultyDialog.cs
@@ -0,0 +1,45 @@
+// 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.Graphics.Sprites;
+using osu.Game.Overlays.Dialog;
+
+namespace osu.Game.Screens.Edit
+{
+ public class CreateNewDifficultyDialog : PopupDialog
+ {
+ ///
+ /// Delegate used to create new difficulties.
+ /// A value of in the createCopy parameter
+ /// indicates that the new difficulty should be an exact copy of an existing one;
+ /// otherwise, the new difficulty should have its hitobjects and beatmap-level settings cleared.
+ ///
+ public delegate void CreateNewDifficulty(bool createCopy);
+
+ public CreateNewDifficultyDialog(CreateNewDifficulty createNewDifficulty)
+ {
+ HeaderText = "Would you like to create a blank difficulty?";
+
+ Icon = FontAwesome.Regular.Clone;
+
+ Buttons = new PopupDialogButton[]
+ {
+ new PopupDialogOkButton
+ {
+ Text = "Yeah, let's start from scratch!",
+ Action = () => createNewDifficulty.Invoke(false)
+ },
+ new PopupDialogCancelButton
+ {
+ Text = "No, create an exact copy of this difficulty",
+ Action = () => createNewDifficulty.Invoke(true)
+ },
+ new PopupDialogCancelButton
+ {
+ Text = "I changed my mind, I want to keep editing this difficulty",
+ Action = () => { }
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 2aec63fa65..c2775ae101 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -358,14 +358,14 @@ namespace osu.Game.Screens.Edit
///
/// Creates an instance representing the current state of the editor.
///
- ///
- /// The next beatmap to be shown, in the case of difficulty switch.
+ ///
+ /// The ruleset of the next beatmap to be shown, in the case of difficulty switch.
/// indicates that the beatmap will not be changing.
///
- public EditorState GetState([CanBeNull] BeatmapInfo nextBeatmap = null) => new EditorState
+ public EditorState GetState([CanBeNull] RulesetInfo nextRuleset = null) => new EditorState
{
Time = clock.CurrentTimeAccurate,
- ClipboardContent = nextBeatmap == null || editorBeatmap.BeatmapInfo.Ruleset.ShortName == nextBeatmap.Ruleset.ShortName ? Clipboard.Content.Value : string.Empty
+ ClipboardContent = nextRuleset == null || editorBeatmap.BeatmapInfo.Ruleset.ShortName == nextRuleset.ShortName ? Clipboard.Content.Value : string.Empty
};
///
@@ -841,7 +841,18 @@ namespace osu.Game.Screens.Edit
}
protected void CreateNewDifficulty(RulesetInfo rulesetInfo)
- => loader?.ScheduleSwitchToNewDifficulty(editorBeatmap.BeatmapInfo.BeatmapSet, rulesetInfo, GetState());
+ {
+ if (!rulesetInfo.Equals(editorBeatmap.BeatmapInfo.Ruleset))
+ {
+ switchToNewDifficulty(rulesetInfo, false);
+ return;
+ }
+
+ dialogOverlay.Push(new CreateNewDifficultyDialog(createCopy => switchToNewDifficulty(rulesetInfo, createCopy)));
+ }
+
+ private void switchToNewDifficulty(RulesetInfo rulesetInfo, bool createCopy)
+ => loader?.ScheduleSwitchToNewDifficulty(editorBeatmap.BeatmapInfo, rulesetInfo, createCopy, GetState(rulesetInfo));
private EditorMenuItem createDifficultySwitchMenu()
{
@@ -866,7 +877,7 @@ namespace osu.Game.Screens.Edit
return new EditorMenuItem("Change difficulty") { Items = difficultyItems };
}
- protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleSwitchToExistingDifficulty(nextBeatmap, GetState(nextBeatmap));
+ protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleSwitchToExistingDifficulty(nextBeatmap, GetState(nextBeatmap.Ruleset));
private void cancelExit()
{
diff --git a/osu.Game/Screens/Edit/EditorLoader.cs b/osu.Game/Screens/Edit/EditorLoader.cs
index de47411fdc..0a2b8437fa 100644
--- a/osu.Game/Screens/Edit/EditorLoader.cs
+++ b/osu.Game/Screens/Edit/EditorLoader.cs
@@ -4,6 +4,7 @@
using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
@@ -80,12 +81,18 @@ namespace osu.Game.Screens.Edit
}
}
- public void ScheduleSwitchToNewDifficulty(BeatmapSetInfo beatmapSetInfo, RulesetInfo rulesetInfo, EditorState editorState)
+ public void ScheduleSwitchToNewDifficulty(BeatmapInfo referenceBeatmapInfo, RulesetInfo rulesetInfo, bool createCopy, EditorState editorState)
=> scheduleDifficultySwitch(() =>
{
try
{
- return beatmapManager.CreateNewBlankDifficulty(beatmapSetInfo, rulesetInfo);
+ // fetch a fresh detached reference from database to avoid polluting model instances attached to cached working beatmaps.
+ var targetBeatmapSet = beatmapManager.QueryBeatmap(b => b.ID == referenceBeatmapInfo.ID).AsNonNull().BeatmapSet.AsNonNull();
+ var referenceWorkingBeatmap = beatmapManager.GetWorkingBeatmap(referenceBeatmapInfo);
+
+ return createCopy
+ ? beatmapManager.CopyExistingDifficulty(targetBeatmapSet, referenceWorkingBeatmap)
+ : beatmapManager.CreateNewDifficulty(targetBeatmapSet, referenceWorkingBeatmap, rulesetInfo);
}
catch (Exception ex)
{
diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs
index f0ca3e1bbc..571dfb3f6f 100644
--- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs
+++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs
@@ -71,7 +71,7 @@ namespace osu.Game.Screens.Edit.Setup
base.LoadComplete();
if (string.IsNullOrEmpty(ArtistTextBox.Current.Value))
- GetContainingInputManager().ChangeFocus(ArtistTextBox);
+ ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(ArtistTextBox));
ArtistTextBox.Current.BindValueChanged(artist => transferIfRomanised(artist.NewValue, RomanisedArtistTextBox));
TitleTextBox.Current.BindValueChanged(title => transferIfRomanised(title.NewValue, RomanisedTitleTextBox));
diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs
index 98c4b15f7f..afe75c5ef7 100644
--- a/osu.Game/Screens/Menu/IntroScreen.cs
+++ b/osu.Game/Screens/Menu/IntroScreen.cs
@@ -19,6 +19,7 @@ using osu.Game.Database;
using osu.Game.IO.Archives;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
+using osu.Game.Rulesets;
using osu.Game.Screens.Backgrounds;
using osuTK;
using osuTK.Graphics;
@@ -71,6 +72,9 @@ namespace osu.Game.Screens.Menu
[CanBeNull]
private readonly Func createNextScreen;
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
///
/// Whether the is provided by osu! resources, rather than a user beatmap.
/// Only valid during or after .
@@ -117,7 +121,11 @@ namespace osu.Game.Screens.Menu
// we generally want a song to be playing on startup, so use the intro music even if a user has specified not to if no other track is available.
if (initialBeatmap == null)
{
- if (!loadThemedIntro())
+ // Intro beatmaps are generally made using the osu! ruleset.
+ // It might not be present in test projects for other rulesets.
+ bool osuRulesetPresent = rulesets.GetRuleset(0) != null;
+
+ if (!loadThemedIntro() && osuRulesetPresent)
{
// if we detect that the theme track or beatmap is unavailable this is either first startup or things are in a bad state.
// this could happen if a user has nuked their files store. for now, reimport to repair this.
diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs
index e948c1adae..7cbe1a9017 100644
--- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs
@@ -68,14 +68,14 @@ namespace osu.Game.Screens.OnlinePlay.Components
}
else
{
- var metadataInfo = beatmap.Value.Metadata;
+ var metadataInfo = beatmap.Metadata;
string artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode;
string titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode;
var title = new RomanisableString($"{artistUnicode} - {titleUnicode}".Trim(), $"{metadataInfo.Artist} - {metadataInfo.Title}".Trim());
- textFlow.AddLink(title, LinkAction.OpenBeatmap, beatmap.Value.OnlineID.ToString(), "Open beatmap");
+ textFlow.AddLink(title, LinkAction.OpenBeatmap, beatmap.OnlineID.ToString(), "Open beatmap");
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs b/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs
index 2026106c42..8402619ebc 100644
--- a/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps.Drawables;
+using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Components
@@ -15,6 +16,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
private const float height = 28;
private const float transition_duration = 100;
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
private Container drawableRuleset;
public ModeTypeInfo()
@@ -56,11 +60,14 @@ namespace osu.Game.Screens.OnlinePlay.Components
private void updateBeatmap()
{
var item = Playlist.FirstOrDefault();
+ var ruleset = item == null ? null : rulesets.GetRuleset(item.RulesetID)?.CreateInstance();
- if (item?.Beatmap != null)
+ if (item?.Beatmap != null && ruleset != null)
{
+ var mods = item.RequiredMods.Select(m => m.ToMod(ruleset)).ToArray();
+
drawableRuleset.FadeIn(transition_duration);
- drawableRuleset.Child = new DifficultyIcon(item.Beatmap.Value, item.Ruleset.Value, item.RequiredMods) { Size = new Vector2(height) };
+ drawableRuleset.Child = new DifficultyIcon(item.Beatmap, ruleset.RulesetInfo, mods) { Size = new Vector2(height) };
}
else
drawableRuleset.FadeOut(transition_duration);
diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs
index ffc5c07d4e..8906bebf0e 100644
--- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
{
Schedule(() =>
{
- var beatmap = playlistItem?.Beatmap.Value;
+ var beatmap = playlistItem?.Beatmap;
string? lastCover = (background?.Beatmap?.BeatmapSet as IBeatmapSetOnlineInfo)?.Covers.Cover;
string? newCover = (beatmap?.BeatmapSet as IBeatmapSetOnlineInfo)?.Covers.Cover;
diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs
index d144e1e3a9..d46ff12279 100644
--- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
private void updateBeatmap()
{
- sprite.Beatmap.Value = Playlist.GetCurrentItem()?.Beatmap.Value;
+ sprite.Beatmap.Value = Playlist.GetCurrentItem()?.Beatmap;
}
protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
diff --git a/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs b/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs
index f3e90aa396..7e31591389 100644
--- a/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
public PlaylistItemBackground(PlaylistItem? playlistItem)
{
- Beatmap = playlistItem?.Beatmap.Value;
+ Beatmap = playlistItem?.Beatmap;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
index 238aa4059d..4242886e66 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
@@ -12,7 +12,6 @@ using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
-using osu.Game.Rulesets;
namespace osu.Game.Screens.OnlinePlay.Components
{
@@ -27,9 +26,6 @@ namespace osu.Game.Screens.OnlinePlay.Components
protected IBindable JoinedRoom => joinedRoom;
private readonly Bindable joinedRoom = new Bindable();
- [Resolved]
- private IRulesetStore rulesets { get; set; }
-
[Resolved]
private IAPIProvider api { get; set; }
@@ -116,9 +112,6 @@ namespace osu.Game.Screens.OnlinePlay.Components
try
{
- foreach (var pi in room.Playlist)
- pi.MapObjects(rulesets);
-
var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value);
if (existing == null)
rooms.Add(room);
diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs
index edf9c5d155..95ecadd21a 100644
--- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs
@@ -80,7 +80,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
private void updateRange(object sender, NotifyCollectionChangedEventArgs e)
{
- var orderedDifficulties = Playlist.Where(p => p.Beatmap.Value != null).Select(p => p.Beatmap.Value).OrderBy(b => b.StarRating).ToArray();
+ var orderedDifficulties = Playlist.Select(p => p.Beatmap).OrderBy(b => b.StarRating).ToArray();
StarDifficulty minDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[0].StarRating : 0, 0);
StarDifficulty maxDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[^1].StarRating : 0, 0);
diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs
index e1f7ea5e92..25b36e0774 100644
--- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs
+++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -25,7 +24,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online;
using osu.Game.Online.Chat;
-using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Rulesets;
@@ -68,9 +66,10 @@ namespace osu.Game.Screens.OnlinePlay
private readonly DelayedLoadWrapper onScreenLoader = new DelayedLoadWrapper(Empty) { RelativeSizeAxes = Axes.Both };
private readonly IBindable valid = new Bindable();
- private readonly Bindable beatmap = new Bindable();
- private readonly Bindable ruleset = new Bindable();
- private readonly BindableList requiredMods = new BindableList();
+
+ private IBeatmapInfo beatmap;
+ private IRulesetInfo ruleset;
+ private Mod[] requiredMods;
private Container maskingContainer;
private Container difficultyIconContainer;
@@ -86,16 +85,15 @@ namespace osu.Game.Screens.OnlinePlay
private PanelBackground panelBackground;
private FillFlowContainer mainFillFlow;
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
[Resolved]
private OsuColour colours { get; set; }
[Resolved]
private UserLookupCache userLookupCache { get; set; }
- [CanBeNull]
- [Resolved(CanBeNull = true)]
- private MultiplayerClient multiplayerClient { get; set; }
-
[Resolved]
private BeatmapLookupCache beatmapLookupCache { get; set; }
@@ -106,10 +104,7 @@ namespace osu.Game.Screens.OnlinePlay
{
Item = item;
- beatmap.BindTo(item.Beatmap);
valid.BindTo(item.Valid);
- ruleset.BindTo(item.Ruleset);
- requiredMods.BindTo(item.RequiredMods);
if (item.Expired)
Colour = OsuColour.Gray(0.5f);
@@ -119,6 +114,11 @@ namespace osu.Game.Screens.OnlinePlay
private void load()
{
maskingContainer.BorderColour = colours.Yellow;
+
+ ruleset = rulesets.GetRuleset(Item.RulesetID);
+ var rulesetInstance = ruleset?.CreateInstance();
+
+ requiredMods = Item.RequiredMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
}
protected override void LoadComplete()
@@ -144,10 +144,7 @@ namespace osu.Game.Screens.OnlinePlay
maskingContainer.BorderThickness = isCurrent ? 5 : 0;
}, true);
- beatmap.BindValueChanged(_ => Scheduler.AddOnce(refresh));
- ruleset.BindValueChanged(_ => Scheduler.AddOnce(refresh));
valid.BindValueChanged(_ => Scheduler.AddOnce(refresh));
- requiredMods.CollectionChanged += (_, __) => Scheduler.AddOnce(refresh);
onScreenLoader.DelayedLoadStarted += _ =>
{
@@ -161,19 +158,9 @@ namespace osu.Game.Screens.OnlinePlay
Schedule(() => ownerAvatar.User = foundUser);
}
- if (Item.Beatmap.Value == null)
- {
- IBeatmapInfo foundBeatmap;
+ beatmap = await beatmapLookupCache.GetBeatmapAsync(Item.Beatmap.OnlineID).ConfigureAwait(false);
- if (multiplayerClient != null)
- // This call can eventually go away (and use the else case below).
- // Currently required only due to the method being overridden to provide special behaviour in tests.
- foundBeatmap = await multiplayerClient.GetAPIBeatmap(Item.BeatmapID).ConfigureAwait(false);
- else
- foundBeatmap = await beatmapLookupCache.GetBeatmapAsync(Item.BeatmapID).ConfigureAwait(false);
-
- Schedule(() => Item.Beatmap.Value = foundBeatmap);
- }
+ Scheduler.AddOnce(refresh);
}
catch (Exception e)
{
@@ -275,32 +262,36 @@ namespace osu.Game.Screens.OnlinePlay
maskingContainer.BorderColour = colours.Red;
}
- if (Item.Beatmap.Value != null)
- difficultyIconContainer.Child = new DifficultyIcon(Item.Beatmap.Value, ruleset.Value, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(icon_height) };
+ if (beatmap != null)
+ difficultyIconContainer.Child = new DifficultyIcon(beatmap, ruleset, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(icon_height) };
else
difficultyIconContainer.Clear();
- panelBackground.Beatmap.Value = Item.Beatmap.Value;
+ panelBackground.Beatmap.Value = beatmap;
beatmapText.Clear();
- if (Item.Beatmap.Value != null)
+ if (beatmap != null)
{
- beatmapText.AddLink(Item.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineID.ToString(), null, text =>
- {
- text.Truncate = true;
- });
+ beatmapText.AddLink(beatmap.GetDisplayTitleRomanisable(includeCreator: false),
+ LinkAction.OpenBeatmap,
+ beatmap.OnlineID.ToString(),
+ null,
+ text =>
+ {
+ text.Truncate = true;
+ });
}
authorText.Clear();
- if (!string.IsNullOrEmpty(Item.Beatmap.Value?.Metadata.Author.Username))
+ if (!string.IsNullOrEmpty(beatmap?.Metadata.Author.Username))
{
authorText.AddText("mapped by ");
- authorText.AddUserLink(Item.Beatmap.Value.Metadata.Author);
+ authorText.AddUserLink(beatmap.Metadata.Author);
}
- bool hasExplicitContent = (Item.Beatmap.Value?.BeatmapSet as IBeatmapSetOnlineInfo)?.HasExplicitContent == true;
+ bool hasExplicitContent = (beatmap?.BeatmapSet as IBeatmapSetOnlineInfo)?.HasExplicitContent == true;
explicitContentPill.Alpha = hasExplicitContent ? 1 : 0;
modDisplay.Current.Value = requiredMods.ToArray();
@@ -452,7 +443,7 @@ namespace osu.Game.Screens.OnlinePlay
Alpha = AllowShowingResults ? 1 : 0,
TooltipText = "View results"
},
- Item.Beatmap.Value == null ? Empty() : new PlaylistDownloadButton(Item),
+ beatmap == null ? Empty() : new PlaylistDownloadButton(beatmap),
editButton = new PlaylistEditButton
{
Size = new Vector2(30, 30),
@@ -494,7 +485,7 @@ namespace osu.Game.Screens.OnlinePlay
private sealed class PlaylistDownloadButton : BeatmapDownloadButton
{
- private readonly PlaylistItem playlistItem;
+ private readonly IBeatmapInfo beatmap;
[Resolved]
private BeatmapManager beatmapManager { get; set; }
@@ -504,10 +495,10 @@ namespace osu.Game.Screens.OnlinePlay
private const float width = 50;
- public PlaylistDownloadButton(PlaylistItem playlistItem)
- : base(playlistItem.Beatmap.Value.BeatmapSet)
+ public PlaylistDownloadButton(IBeatmapInfo beatmap)
+ : base(beatmap.BeatmapSet)
{
- this.playlistItem = playlistItem;
+ this.beatmap = beatmap;
Size = new Vector2(width, 30);
Alpha = 0;
@@ -527,7 +518,7 @@ namespace osu.Game.Screens.OnlinePlay
{
case DownloadState.LocallyAvailable:
// Perform a local query of the beatmap by beatmap checksum, and reset the state if not matching.
- if (beatmapManager.QueryBeatmap(b => b.MD5Hash == playlistItem.Beatmap.Value.MD5Hash) == null)
+ if (beatmapManager.QueryBeatmap(b => b.MD5Hash == beatmap.MD5Hash) == null)
State.Value = DownloadState.NotDownloaded;
else
{
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs
index a87f21630c..a1a82c907a 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs
@@ -2,8 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@@ -12,6 +14,7 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
+using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
@@ -328,6 +331,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
[Resolved]
private OsuColour colours { get; set; }
+ [Resolved]
+ private BeatmapLookupCache beatmapLookupCache { get; set; }
+
private SpriteText statusText;
private LinkFlowContainer beatmapText;
@@ -385,8 +391,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
SelectedItem.BindValueChanged(onSelectedItemChanged, true);
}
+ private CancellationTokenSource beatmapLookupCancellation;
+
private void onSelectedItemChanged(ValueChangedEvent item)
{
+ beatmapLookupCancellation?.Cancel();
beatmapText.Clear();
if (Type.Value == MatchType.Playlists)
@@ -395,17 +404,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
return;
}
- if (item.NewValue?.Beatmap.Value != null)
- {
- statusText.Text = "Currently playing ";
- beatmapText.AddLink(item.NewValue.Beatmap.Value.GetDisplayTitleRomanisable(),
- LinkAction.OpenBeatmap,
- item.NewValue.Beatmap.Value.OnlineID.ToString(),
- creationParameters: s =>
- {
- s.Truncate = true;
- });
- }
+ var beatmap = item.NewValue?.Beatmap;
+ if (beatmap == null)
+ return;
+
+ var cancellationSource = beatmapLookupCancellation = new CancellationTokenSource();
+ beatmapLookupCache.GetBeatmapAsync(beatmap.OnlineID, cancellationSource.Token)
+ .ContinueWith(task => Schedule(() =>
+ {
+ if (cancellationSource.IsCancellationRequested)
+ return;
+
+ var retrievedBeatmap = task.GetResultSafely();
+
+ statusText.Text = "Currently playing ";
+ beatmapText.AddLink(retrievedBeatmap.GetDisplayTitleRomanisable(),
+ LinkAction.OpenBeatmap,
+ retrievedBeatmap.OnlineID.ToString(),
+ creationParameters: s => s.Truncate = true);
+ }), cancellationSource.Token);
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
index f4d7823fcc..3260427192 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
@@ -12,7 +12,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
-using osu.Game.Extensions;
using osu.Game.Graphics.Cursor;
using osu.Game.Input.Bindings;
using osu.Game.Online.Rooms;
@@ -78,7 +77,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
bool matchingFilter = true;
- matchingFilter &= r.Room.Playlist.Count == 0 || criteria.Ruleset == null || r.Room.Playlist.Any(i => i.Ruleset.Value.MatchesOnlineID(criteria.Ruleset));
+ matchingFilter &= r.Room.Playlist.Count == 0 || criteria.Ruleset == null || r.Room.Playlist.Any(i => i.RulesetID == criteria.Ruleset.OnlineID);
if (!string.IsNullOrEmpty(criteria.SearchString))
matchingFilter &= r.FilterTerms.Any(term => term.Contains(criteria.SearchString, StringComparison.InvariantCultureIgnoreCase));
@@ -125,7 +124,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
private void updateSorting()
{
foreach (var room in roomFlow)
- roomFlow.SetLayoutPosition(room, -(room.Room.RoomID.Value ?? 0));
+ {
+ roomFlow.SetLayoutPosition(room, room.Room.Category.Value == RoomCategory.Spotlight
+ // Always show spotlight playlists at the top of the listing.
+ ? float.MinValue
+ : -(room.Room.RoomID.Value ?? 0));
+ }
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
index 3fd56ece58..27743e709f 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
@@ -246,7 +246,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
base.LoadComplete();
- Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextBox));
+ ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(passwordTextBox));
passwordTextBox.OnCommit += (_, __) => performJoin();
}
diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs
index a7b907c7d2..cdd2ae0c9c 100644
--- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs
@@ -62,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
if (editButton != null)
host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true);
- SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap.Value, true);
+ SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true);
}
protected override Drawable CreateBackground() => background = new BackgroundSprite();
diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
index 2d5225639f..e297c90491 100644
--- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@@ -350,10 +351,12 @@ namespace osu.Game.Screens.OnlinePlay.Match
if (selected == null)
return;
+ var rulesetInstance = rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance();
+ Debug.Assert(rulesetInstance != null);
+ var allowedMods = SelectedItem.Value.AllowedMods.Select(m => m.ToMod(rulesetInstance));
+
// Remove any user mods that are no longer allowed.
- UserMods.Value = UserMods.Value
- .Where(m => selected.AllowedMods.Any(a => m.GetType() == a.GetType()))
- .ToList();
+ UserMods.Value = UserMods.Value.Where(m => allowedMods.Any(a => m.GetType() == a.GetType())).ToList();
UpdateMods();
updateRuleset();
@@ -367,13 +370,13 @@ namespace osu.Game.Screens.OnlinePlay.Match
else
{
UserModsSection?.Show();
- userModsSelectOverlay.IsValidMod = m => selected.AllowedMods.Any(a => a.GetType() == m.GetType());
+ userModsSelectOverlay.IsValidMod = m => allowedMods.Any(a => a.GetType() == m.GetType());
}
}
private void updateWorkingBeatmap()
{
- var beatmap = SelectedItem.Value?.Beatmap.Value;
+ var beatmap = SelectedItem.Value?.Beatmap;
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineID == beatmap.OnlineID);
@@ -386,7 +389,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
if (SelectedItem.Value == null || !this.IsCurrentScreen())
return;
- Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList();
+ var rulesetInstance = rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance();
+ Debug.Assert(rulesetInstance != null);
+ Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToList();
}
private void updateRuleset()
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs
index 073497e1ce..e30ec36e9c 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs
@@ -11,7 +11,6 @@ using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
@@ -68,11 +67,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
var multiplayerItem = new MultiplayerPlaylistItem
{
ID = itemToEdit ?? 0,
- BeatmapID = item.BeatmapID,
- BeatmapChecksum = item.Beatmap.Value.MD5Hash,
+ BeatmapID = item.Beatmap.OnlineID,
+ BeatmapChecksum = item.Beatmap.MD5Hash,
RulesetID = item.RulesetID,
- RequiredMods = item.RequiredMods.Select(m => new APIMod(m)).ToArray(),
- AllowedMods = item.AllowedMods.Select(m => new APIMod(m)).ToArray()
+ RequiredMods = item.RequiredMods.ToArray(),
+ AllowedMods = item.AllowedMods.ToArray()
};
Task task = itemToEdit != null ? client.EditPlaylistItem(multiplayerItem) : client.AddPlaylistItem(multiplayerItem);
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
index a397493bab..429b0ad89b 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -247,7 +246,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
// update local mods based on room's reported status for the local user (omitting the base call implementation).
// this makes the server authoritative, and avoids the local user potentially setting mods that the server is not aware of (ie. if the match was started during the selection being changed).
var ruleset = Ruleset.Value.CreateInstance();
- Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods).ToList();
+ Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods.Select(m => m.ToMod(ruleset))).ToList();
}
[Resolved(canBeNull: true)]
@@ -398,38 +397,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private void updateCurrentItem()
{
Debug.Assert(client.Room != null);
-
- var expectedSelectedItem = Room.Playlist.SingleOrDefault(i => i.ID == client.Room.Settings.PlaylistItemId);
-
- if (expectedSelectedItem == null)
- return;
-
- // There's no reason to renew the selected item if its content hasn't changed.
- if (SelectedItem.Value?.Equals(expectedSelectedItem) == true && expectedSelectedItem.Beatmap.Value != null)
- return;
-
- // Clear the selected item while the lookup is performed, so components like the ready button can enter their disabled states.
- SelectedItem.Value = null;
-
- if (expectedSelectedItem.Beatmap.Value == null)
- {
- Task.Run(async () =>
- {
- var beatmap = await client.GetAPIBeatmap(expectedSelectedItem.BeatmapID).ConfigureAwait(false);
-
- Schedule(() =>
- {
- expectedSelectedItem.Beatmap.Value = beatmap;
-
- if (Room.Playlist.SingleOrDefault(i => i.ID == client.Room?.Settings.PlaylistItemId)?.Equals(expectedSelectedItem) == true)
- applyCurrentItem();
- });
- });
- }
- else
- applyCurrentItem();
-
- void applyCurrentItem() => SelectedItem.Value = expectedSelectedItem;
+ SelectedItem.Value = Room.Playlist.SingleOrDefault(i => i.ID == client.Room.Settings.PlaylistItemId);
}
private void handleRoomLost() => Schedule(() =>
diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs
index 63957caee3..7b64784316 100644
--- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs
+++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
+using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
@@ -37,6 +38,9 @@ namespace osu.Game.Screens.OnlinePlay
[Resolved(CanBeNull = true)]
protected IBindable SelectedItem { get; private set; }
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
protected override UserActivity InitialActivity => new UserActivity.InLobby(room);
protected readonly Bindable> FreeMods = new Bindable>(Array.Empty());
@@ -78,10 +82,15 @@ namespace osu.Game.Screens.OnlinePlay
{
base.LoadComplete();
- // At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
- // Similarly, freeMods is currently empty but should only contain the allowed mods.
- Mods.Value = SelectedItem?.Value?.RequiredMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty();
- FreeMods.Value = SelectedItem?.Value?.AllowedMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty();
+ var rulesetInstance = SelectedItem?.Value?.RulesetID == null ? null : rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance();
+
+ if (rulesetInstance != null)
+ {
+ // At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
+ // Similarly, freeMods is currently empty but should only contain the allowed mods.
+ Mods.Value = SelectedItem.Value.RequiredMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
+ FreeMods.Value = SelectedItem.Value.AllowedMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
+ }
Mods.BindValueChanged(onModsChanged);
Ruleset.BindValueChanged(onRulesetChanged);
@@ -104,21 +113,13 @@ namespace osu.Game.Screens.OnlinePlay
{
itemSelected = true;
- var item = new PlaylistItem
+ var item = new PlaylistItem(Beatmap.Value.BeatmapInfo)
{
- Beatmap =
- {
- Value = Beatmap.Value.BeatmapInfo
- },
- Ruleset =
- {
- Value = Ruleset.Value
- }
+ RulesetID = Ruleset.Value.OnlineID,
+ RequiredMods = Mods.Value.Select(m => new APIMod(m)).ToArray(),
+ AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray()
};
- item.RequiredMods.AddRange(Mods.Value.Select(m => m.DeepClone()));
- item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.DeepClone()));
-
SelectItem(item);
return true;
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs
index 35d417520e..8403e1e0fe 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs
@@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Screens;
using osu.Game.Extensions;
+using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Scoring;
@@ -33,13 +34,14 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
private void load(IBindable ruleset)
{
// Sanity checks to ensure that PlaylistsPlayer matches the settings for the current PlaylistItem
- if (!Beatmap.Value.BeatmapInfo.MatchesOnlineID(PlaylistItem.Beatmap.Value))
+ if (!Beatmap.Value.BeatmapInfo.MatchesOnlineID(PlaylistItem.Beatmap))
throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap");
- if (!ruleset.Value.MatchesOnlineID(PlaylistItem.Ruleset.Value))
+ if (ruleset.Value.OnlineID != PlaylistItem.RulesetID)
throw new InvalidOperationException("Current Ruleset does not match PlaylistItem's Ruleset");
- if (!PlaylistItem.RequiredMods.All(m => Mods.Value.Any(m.Equals)))
+ var localMods = Mods.Value.Select(m => new APIMod(m)).ToArray();
+ if (!PlaylistItem.RequiredMods.All(m => localMods.Any(m.Equals)))
throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods");
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
index 6c8ab52d22..6674a37c3c 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
@@ -392,7 +392,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
foreach (var item in Playlist)
{
- if (invalidBeatmapIDs.Contains(item.BeatmapID))
+ if (invalidBeatmapIDs.Contains(item.Beatmap.OnlineID))
item.MarkInvalid();
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 542851cb0f..338a9c856f 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -69,132 +69,155 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
Room.MaxAttempts.BindValueChanged(attempts => progressSection.Alpha = Room.MaxAttempts.Value != null ? 1 : 0, true);
}
- protected override Drawable CreateMainContent() => new GridContainer
+ protected override Drawable CreateMainContent() => new Container
{
RelativeSizeAxes = Axes.Both,
- Content = new[]
+ Padding = new MarginPadding { Horizontal = 5, Vertical = 10 },
+ Child = new GridContainer
{
- new Drawable[]
+ RelativeSizeAxes = Axes.Both,
+ ColumnDimensions = new[]
{
- new Container
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 10),
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 10),
+ new Dimension(),
+ },
+ Content = new[]
+ {
+ new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Right = 5 },
- Child = new GridContainer
+ // Playlist items column
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Right = 5 },
+ Child = new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[] { new OverlinedPlaylistHeader(), },
+ new Drawable[]
+ {
+ new DrawableRoomPlaylist
+ {
+ RelativeSizeAxes = Axes.Both,
+ Items = { BindTarget = Room.Playlist },
+ SelectedItem = { BindTarget = SelectedItem },
+ AllowSelection = true,
+ AllowShowingResults = true,
+ RequestResults = item =>
+ {
+ Debug.Assert(RoomId.Value != null);
+ ParentScreen?.Push(new PlaylistsResultsScreen(null, RoomId.Value.Value, item, false));
+ }
+ }
+ },
+ },
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(),
+ }
+ }
+ },
+ // Spacer
+ null,
+ // Middle column (mods and leaderboard)
+ new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
- new Drawable[] { new OverlinedPlaylistHeader(), },
+ new[]
+ {
+ UserModsSection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Alpha = 0,
+ Margin = new MarginPadding { Bottom = 10 },
+ Children = new Drawable[]
+ {
+ new OverlinedHeader("Extra mods"),
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(10, 0),
+ Children = new Drawable[]
+ {
+ new UserModSelectButton
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Width = 90,
+ Text = "Select",
+ Action = ShowUserModSelect,
+ },
+ new ModDisplay
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Current = UserMods,
+ Scale = new Vector2(0.8f),
+ },
+ }
+ }
+ }
+ },
+ },
new Drawable[]
{
- new DrawableRoomPlaylist
+ progressSection = new FillFlowContainer
{
- RelativeSizeAxes = Axes.Both,
- Items = { BindTarget = Room.Playlist },
- SelectedItem = { BindTarget = SelectedItem },
- AllowSelection = true,
- AllowShowingResults = true,
- RequestResults = item =>
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Alpha = 0,
+ Margin = new MarginPadding { Bottom = 10 },
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
{
- Debug.Assert(RoomId.Value != null);
- ParentScreen?.Push(new PlaylistsResultsScreen(null, RoomId.Value.Value, item, false));
+ new OverlinedHeader("Progress"),
+ new RoomLocalUserInfo(),
}
- }
+ },
},
+ new Drawable[]
+ {
+ new OverlinedHeader("Leaderboard")
+ },
+ new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
+ },
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(),
+ }
+ },
+ // Spacer
+ null,
+ // Main right column
+ new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[] { new OverlinedHeader("Chat") },
+ new Drawable[] { new MatchChatDisplay(Room) { RelativeSizeAxes = Axes.Both } }
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
}
- }
- },
- null,
- new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- Content = new[]
- {
- new[]
- {
- UserModsSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Alpha = 0,
- Margin = new MarginPadding { Bottom = 10 },
- Children = new Drawable[]
- {
- new OverlinedHeader("Extra mods"),
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(10, 0),
- Children = new Drawable[]
- {
- new UserModSelectButton
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Width = 90,
- Text = "Select",
- Action = ShowUserModSelect,
- },
- new ModDisplay
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Current = UserMods,
- Scale = new Vector2(0.8f),
- },
- }
- }
- }
- },
- },
- new Drawable[]
- {
- progressSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Alpha = 0,
- Margin = new MarginPadding { Bottom = 10 },
- Direction = FillDirection.Vertical,
- Children = new Drawable[]
- {
- new OverlinedHeader("Progress"),
- new RoomLocalUserInfo(),
- }
- },
- },
- new Drawable[]
- {
- new OverlinedHeader("Leaderboard")
- },
- new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
- new Drawable[] { new OverlinedHeader("Chat"), },
- new Drawable[] { new MatchChatDisplay(Room) { RelativeSizeAxes = Axes.Both } }
},
- RowDimensions = new[]
- {
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.Relative, size: 0.4f, minSize: 120),
- }
},
},
- },
- ColumnDimensions = new[]
- {
- new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400),
- new Dimension(),
- new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600),
}
};
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs
index 0fd76f7e25..86591c1d6d 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs
@@ -3,6 +3,7 @@
using System.Linq;
using osu.Framework.Screens;
+using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.Select;
@@ -30,7 +31,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
break;
case 1:
- populateItemFromCurrent(Playlist.Single());
+ Playlist.Clear();
+ createNewItem();
break;
}
@@ -39,26 +41,15 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
private void createNewItem()
{
- PlaylistItem item = new PlaylistItem
+ PlaylistItem item = new PlaylistItem(Beatmap.Value.BeatmapInfo)
{
- ID = Playlist.Count == 0 ? 0 : Playlist.Max(p => p.ID) + 1
+ ID = Playlist.Count == 0 ? 0 : Playlist.Max(p => p.ID) + 1,
+ RulesetID = Ruleset.Value.OnlineID,
+ RequiredMods = Mods.Value.Select(m => new APIMod(m)).ToArray(),
+ AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray()
};
- populateItemFromCurrent(item);
-
Playlist.Add(item);
}
-
- private void populateItemFromCurrent(PlaylistItem item)
- {
- item.Beatmap.Value = Beatmap.Value.BeatmapInfo;
- item.Ruleset.Value = Ruleset.Value;
-
- item.RequiredMods.Clear();
- item.RequiredMods.AddRange(Mods.Value.Select(m => m.DeepClone()));
-
- item.AllowedMods.Clear();
- item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.DeepClone()));
- }
}
}
diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs
index ffcbb06fb3..807b4989c7 100644
--- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs
+++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Screens.Play.HUD
//CollectionSettings = new CollectionSettings(),
//DiscussionSettings = new DiscussionSettings(),
PlaybackSettings = new PlaybackSettings(),
- VisualSettings = new VisualSettings { Expanded = false }
+ VisualSettings = new VisualSettings { Expanded = { Value = false } }
}
};
}
diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs
index b86deeab89..c4864c0334 100644
--- a/osu.Game/Storyboards/Storyboard.cs
+++ b/osu.Game/Storyboards/Storyboard.cs
@@ -78,7 +78,7 @@ namespace osu.Game.Storyboards
{
get
{
- string backgroundPath = BeatmapInfo.BeatmapSet?.Metadata.BackgroundFile;
+ string backgroundPath = BeatmapInfo.Metadata.BackgroundFile;
if (string.IsNullOrEmpty(backgroundPath))
return false;
diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs
index 331bf04644..24015590e2 100644
--- a/osu.Game/Tests/Visual/EditorTestScene.cs
+++ b/osu.Game/Tests/Visual/EditorTestScene.cs
@@ -136,7 +136,13 @@ namespace osu.Game.Tests.Visual
return new TestWorkingBeatmapCache(this, audioManager, resources, storage, defaultBeatmap, host);
}
- public override WorkingBeatmap CreateNewBlankDifficulty(BeatmapSetInfo beatmapSetInfo, RulesetInfo rulesetInfo)
+ public override WorkingBeatmap CreateNewDifficulty(BeatmapSetInfo targetBeatmapSet, WorkingBeatmap referenceWorkingBeatmap, RulesetInfo rulesetInfo)
+ {
+ // don't actually care about properly creating a difficulty for this context.
+ return TestBeatmap;
+ }
+
+ public override WorkingBeatmap CopyExistingDifficulty(BeatmapSetInfo targetBeatmapSet, WorkingBeatmap referenceWorkingBeatmap)
{
// don't actually care about properly creating a difficulty for this context.
return TestBeatmap;
diff --git a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs
index 204c189591..62d1c9ceca 100644
--- a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Game.Database;
-using osu.Game.Online.Multiplayer;
using osu.Game.Screens.OnlinePlay;
using osu.Game.Tests.Visual.OnlinePlay;
using osu.Game.Tests.Visual.Spectator;
@@ -15,20 +13,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
public interface IMultiplayerTestSceneDependencies : IOnlinePlayTestSceneDependencies
{
///
- /// The cached .
+ /// The cached .
///
- TestMultiplayerClient Client { get; }
+ TestMultiplayerClient MultiplayerClient { get; }
///
/// The cached .
///
new TestMultiplayerRoomManager RoomManager { get; }
- ///
- /// The cached .
- ///
- TestUserLookupCache LookupCache { get; }
-
///
/// The cached .
///
diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
index 7607122ef0..6c40546325 100644
--- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
@@ -17,14 +17,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
public const int PLAYER_1_ID = 55;
public const int PLAYER_2_ID = 56;
- public TestMultiplayerClient Client => OnlinePlayDependencies.Client;
+ public TestMultiplayerClient MultiplayerClient => OnlinePlayDependencies.MultiplayerClient;
public new TestMultiplayerRoomManager RoomManager => OnlinePlayDependencies.RoomManager;
- public TestUserLookupCache LookupCache => OnlinePlayDependencies?.LookupCache;
public TestSpectatorClient SpectatorClient => OnlinePlayDependencies?.SpectatorClient;
protected new MultiplayerTestSceneDependencies OnlinePlayDependencies => (MultiplayerTestSceneDependencies)base.OnlinePlayDependencies;
- public bool RoomJoined => Client.RoomJoined;
+ public bool RoomJoined => MultiplayerClient.RoomJoined;
private readonly bool joinRoom;
@@ -47,10 +46,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "test name" },
Playlist =
{
- new PlaylistItem
+ new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
{
- Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
- Ruleset = { Value = Ruleset.Value }
+ RulesetID = Ruleset.Value.OnlineID
}
}
};
diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs
index ed349a7103..6b4e01b673 100644
--- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Game.Database;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Spectator;
using osu.Game.Screens.OnlinePlay;
@@ -15,19 +14,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
///
public class MultiplayerTestSceneDependencies : OnlinePlayTestSceneDependencies, IMultiplayerTestSceneDependencies
{
- public TestMultiplayerClient Client { get; }
- public TestUserLookupCache LookupCache { get; }
+ public TestMultiplayerClient MultiplayerClient { get; }
public TestSpectatorClient SpectatorClient { get; }
public new TestMultiplayerRoomManager RoomManager => (TestMultiplayerRoomManager)base.RoomManager;
public MultiplayerTestSceneDependencies()
{
- Client = new TestMultiplayerClient(RoomManager);
- LookupCache = new TestUserLookupCache();
+ MultiplayerClient = new TestMultiplayerClient(RoomManager);
SpectatorClient = CreateSpectatorClient();
- CacheAs(Client);
- CacheAs(LookupCache);
+ CacheAs(MultiplayerClient);
CacheAs(SpectatorClient);
}
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
index 15ede6cc26..6dc5159b6f 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -7,14 +7,11 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
-using osu.Game.Beatmaps;
using osu.Game.Online.API;
-using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
using osu.Game.Online.Rooms;
@@ -39,9 +36,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Resolved]
private IAPIProvider api { get; set; } = null!;
- [Resolved]
- private BeatmapManager beatmaps { get; set; } = null!;
-
private readonly TestMultiplayerRoomManager roomManager;
///
@@ -407,23 +401,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
public override Task RemovePlaylistItem(long playlistItemId) => RemoveUserPlaylistItem(api.LocalUser.Value.OnlineID, playlistItemId);
- public override Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default)
- {
- IBeatmapInfo? beatmap = roomManager.ServerSideRooms.SelectMany(r => r.Playlist)
- .FirstOrDefault(p => p.BeatmapID == beatmapId)?.Beatmap.Value
- ?? beatmaps.QueryBeatmap(b => b.OnlineID == beatmapId);
-
- if (beatmap == null)
- throw new InvalidOperationException("Beatmap not found.");
-
- return Task.FromResult(new APIBeatmap
- {
- BeatmapSet = new APIBeatmapSet { OnlineID = beatmap.BeatmapSet?.OnlineID ?? -1 },
- OnlineID = beatmapId,
- Checksum = beatmap.MD5Hash
- });
- }
-
private async Task changeMatchType(MatchType type)
{
Debug.Assert(Room != null);
diff --git a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
index 71acefb158..c94e288e11 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
+using osu.Game.Database;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay;
@@ -31,5 +32,15 @@ namespace osu.Game.Tests.Visual.OnlinePlay
/// The cached .
///
OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; }
+
+ ///
+ /// The cached .
+ ///
+ TestUserLookupCache UserLookupCache { get; }
+
+ ///
+ /// The cached .
+ ///
+ BeatmapLookupCache BeatmapLookupCache { get; }
}
}
diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
index 430aae72f8..b6a347a896 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
@@ -7,6 +7,8 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Database;
+using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay;
@@ -22,6 +24,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay
public IRoomManager RoomManager => OnlinePlayDependencies?.RoomManager;
public OngoingOperationTracker OngoingOperationTracker => OnlinePlayDependencies?.OngoingOperationTracker;
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker => OnlinePlayDependencies?.AvailabilityTracker;
+ public TestUserLookupCache UserLookupCache => OnlinePlayDependencies?.UserLookupCache;
+ public BeatmapLookupCache BeatmapLookupCache => OnlinePlayDependencies?.BeatmapLookupCache;
///
/// All dependencies required for online play components and screens.
@@ -30,9 +34,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
protected override Container Content => content;
- [Resolved]
- private OsuGameBase game { get; set; }
-
private readonly Container content;
private readonly Container drawableDependenciesContainer;
private DelegatedDependencyContainer dependencies;
@@ -46,7 +47,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
});
}
- protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
+ protected sealed override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
dependencies = new DelegatedDependencyContainer(base.CreateChildDependencies(parent));
return dependencies;
@@ -59,19 +60,16 @@ namespace osu.Game.Tests.Visual.OnlinePlay
drawableDependenciesContainer.Clear();
dependencies.OnlinePlayDependencies = CreateOnlinePlayDependencies();
drawableDependenciesContainer.AddRange(OnlinePlayDependencies.DrawableComponents);
+
+ var handler = OnlinePlayDependencies.RequestsHandler;
+
+ // Resolving the BeatmapManager in the test scene will inject the game-wide BeatmapManager, while many test scenes cache their own BeatmapManager instead.
+ // To get around this, the BeatmapManager is looked up from the dependencies provided to the children of the test scene instead.
+ var beatmapManager = dependencies.Get();
+
+ ((DummyAPIAccess)API).HandleRequest = request => handler.HandleRequest(request, API.LocalUser.Value, beatmapManager);
});
- public override void SetUpSteps()
- {
- base.SetUpSteps();
-
- AddStep("setup API", () =>
- {
- var handler = OnlinePlayDependencies.RequestsHandler;
- ((DummyAPIAccess)API).HandleRequest = request => handler.HandleRequest(request, API.LocalUser.Value, game);
- });
- }
-
///
/// Creates the room dependencies. Called every .
///
diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
index 24c4ff79d4..7c8bc2d535 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Game.Database;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay;
@@ -22,6 +23,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay
public OngoingOperationTracker OngoingOperationTracker { get; }
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; }
public TestRoomRequestsHandler RequestsHandler { get; }
+ public TestUserLookupCache UserLookupCache { get; }
+ public BeatmapLookupCache BeatmapLookupCache { get; }
///
/// All cached dependencies which are also components.
@@ -38,6 +41,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay
OngoingOperationTracker = new OngoingOperationTracker();
AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
RoomManager = CreateRoomManager();
+ UserLookupCache = new TestUserLookupCache();
+ BeatmapLookupCache = new BeatmapLookupCache();
dependencies = new DependencyContainer(new CachedModelDependencyContainer(null) { Model = { BindTarget = SelectedRoom } });
@@ -47,6 +52,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay
CacheAs(OngoingOperationTracker);
CacheAs(AvailabilityTracker);
CacheAs(new OverlayColourProvider(OverlayColourScheme.Plum));
+ CacheAs(UserLookupCache);
+ CacheAs(BeatmapLookupCache);
}
public object Get(Type type)
diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
index 4cbc6174c9..6abcb2924c 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
base.JoinRoom(room, password, onSuccess, onError);
}
- public void AddRooms(int count, RulesetInfo ruleset = null, bool withPassword = false)
+ public void AddRooms(int count, RulesetInfo ruleset = null, bool withPassword = false, bool withSpotlightRooms = false)
{
for (int i = 0; i < count; i++)
{
@@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
Name = { Value = $@"Room {currentRoomId}" },
Host = { Value = new APIUser { Username = @"Host" } },
EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) },
- Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal },
+ Category = { Value = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal },
};
if (withPassword)
@@ -43,16 +43,9 @@ namespace osu.Game.Tests.Visual.OnlinePlay
if (ruleset != null)
{
- room.Playlist.Add(new PlaylistItem
+ room.Playlist.Add(new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() })
{
- Ruleset = { Value = ruleset },
- Beatmap =
- {
- Value = new BeatmapInfo
- {
- Metadata = new BeatmapMetadata()
- }
- }
+ RulesetID = ruleset.OnlineID,
});
}
diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
index 5a0a7e71d4..8290af8f78 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
@@ -4,12 +4,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using osu.Game.Beatmaps;
using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
+using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay.Components;
+using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.OnlinePlay
{
@@ -33,9 +37,9 @@ namespace osu.Game.Tests.Visual.OnlinePlay
///
/// The API request to handle.
/// The local user to store in responses where required.
- /// The game base for cases where actual online requests need to be sent.
+ /// The beatmap manager to attempt to retrieve beatmaps from, prior to returning dummy beatmaps.
/// Whether the request was successfully handled.
- public bool HandleRequest(APIRequest request, APIUser localUser, OsuGameBase game)
+ public bool HandleRequest(APIRequest request, APIUser localUser, BeatmapManager beatmapManager)
{
switch (request)
{
@@ -128,6 +132,26 @@ namespace osu.Game.Tests.Visual.OnlinePlay
Statistics = new Dictionary()
});
return true;
+
+ case GetBeatmapsRequest getBeatmapsRequest:
+ var result = new List();
+
+ foreach (int id in getBeatmapsRequest.BeatmapIds)
+ {
+ var baseBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == id);
+
+ if (baseBeatmap == null)
+ {
+ baseBeatmap = new TestBeatmap(new RulesetInfo { OnlineID = 0 }).BeatmapInfo;
+ baseBeatmap.OnlineID = id;
+ baseBeatmap.BeatmapSet!.OnlineID = id;
+ }
+
+ result.Add(OsuTestScene.CreateAPIBeatmap(baseBeatmap));
+ }
+
+ getBeatmapsRequest.TriggerSuccess(new GetBeatmapsResponse { Beatmaps = result });
+ return true;
}
return false;
diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs
index ec02655544..f287a04d71 100644
--- a/osu.Game/Tests/Visual/OsuTestScene.cs
+++ b/osu.Game/Tests/Visual/OsuTestScene.cs
@@ -225,12 +225,24 @@ namespace osu.Game.Tests.Visual
protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset);
///
- /// Returns a sample API Beatmap with BeatmapSet populated.
+ /// Returns a sample API beatmap with a populated beatmap set.
///
/// The ruleset to create the sample model using. osu! ruleset will be used if not specified.
- protected APIBeatmap CreateAPIBeatmap(RulesetInfo ruleset = null)
+ protected APIBeatmap CreateAPIBeatmap(RulesetInfo ruleset = null) => CreateAPIBeatmap(CreateBeatmap(ruleset ?? Ruleset.Value).BeatmapInfo);
+
+ ///
+ /// Constructs a sample API beatmap set containing a beatmap.
+ ///
+ /// The ruleset to create the sample model using. osu! ruleset will be used if not specified.
+ protected APIBeatmapSet CreateAPIBeatmapSet(RulesetInfo ruleset = null) => CreateAPIBeatmapSet(CreateBeatmap(ruleset ?? Ruleset.Value).BeatmapInfo);
+
+ ///
+ /// Constructs a sample API beatmap with a populated beatmap set from a given source beatmap.
+ ///
+ /// The source beatmap.
+ public static APIBeatmap CreateAPIBeatmap(IBeatmapInfo original)
{
- var beatmapSet = CreateAPIBeatmapSet(ruleset ?? Ruleset.Value);
+ var beatmapSet = CreateAPIBeatmapSet(original);
// Avoid circular reference.
var beatmap = beatmapSet.Beatmaps.First();
@@ -243,18 +255,16 @@ namespace osu.Game.Tests.Visual
}
///
- /// Returns a sample API BeatmapSet with beatmaps populated.
+ /// Constructs a sample API beatmap set containing a beatmap from a given source beatmap.
///
- /// The ruleset to create the sample model using. osu! ruleset will be used if not specified.
- protected APIBeatmapSet CreateAPIBeatmapSet(RulesetInfo ruleset = null)
+ /// The source beatmap.
+ public static APIBeatmapSet CreateAPIBeatmapSet(IBeatmapInfo original)
{
- var beatmap = CreateBeatmap(ruleset ?? Ruleset.Value).BeatmapInfo;
-
- Debug.Assert(beatmap.BeatmapSet != null);
+ Debug.Assert(original.BeatmapSet != null);
return new APIBeatmapSet
{
- OnlineID = ((IBeatmapSetInfo)beatmap.BeatmapSet).OnlineID,
+ OnlineID = original.BeatmapSet.OnlineID,
Status = BeatmapOnlineStatus.Ranked,
Covers = new BeatmapSetOnlineCovers
{
@@ -262,29 +272,29 @@ namespace osu.Game.Tests.Visual
Card = "https://assets.ppy.sh/beatmaps/163112/covers/card.jpg",
List = "https://assets.ppy.sh/beatmaps/163112/covers/list.jpg"
},
- Title = beatmap.Metadata.Title,
- TitleUnicode = beatmap.Metadata.TitleUnicode,
- Artist = beatmap.Metadata.Artist,
- ArtistUnicode = beatmap.Metadata.ArtistUnicode,
+ Title = original.Metadata.Title,
+ TitleUnicode = original.Metadata.TitleUnicode,
+ Artist = original.Metadata.Artist,
+ ArtistUnicode = original.Metadata.ArtistUnicode,
Author = new APIUser
{
- Username = beatmap.Metadata.Author.Username,
- Id = beatmap.Metadata.Author.OnlineID
+ Username = original.Metadata.Author.Username,
+ Id = original.Metadata.Author.OnlineID
},
- Source = beatmap.Metadata.Source,
- Tags = beatmap.Metadata.Tags,
+ Source = original.Metadata.Source,
+ Tags = original.Metadata.Tags,
Beatmaps = new[]
{
new APIBeatmap
{
- OnlineID = ((IBeatmapInfo)beatmap).OnlineID,
- OnlineBeatmapSetID = ((IBeatmapSetInfo)beatmap.BeatmapSet).OnlineID,
- Status = beatmap.Status,
- Checksum = beatmap.MD5Hash,
- AuthorID = beatmap.Metadata.Author.OnlineID,
- RulesetID = beatmap.Ruleset.OnlineID,
- StarRating = beatmap.StarRating,
- DifficultyName = beatmap.DifficultyName,
+ OnlineID = original.OnlineID,
+ OnlineBeatmapSetID = original.BeatmapSet.OnlineID,
+ Status = ((BeatmapInfo)original).Status,
+ Checksum = original.MD5Hash,
+ AuthorID = original.Metadata.Author.OnlineID,
+ RulesetID = original.Ruleset.OnlineID,
+ StarRating = original.StarRating,
+ DifficultyName = original.DifficultyName,
}
}
};
diff --git a/osu.Game/Utils/NamingUtils.cs b/osu.Game/Utils/NamingUtils.cs
new file mode 100644
index 0000000000..482e3d0954
--- /dev/null
+++ b/osu.Game/Utils/NamingUtils.cs
@@ -0,0 +1,61 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace osu.Game.Utils
+{
+ public static class NamingUtils
+ {
+ ///
+ /// Given a set of and a target ,
+ /// finds a "best" name closest to that is not in .
+ ///
+ ///
+ ///
+ /// This helper is most useful in scenarios when creating new objects in a set
+ /// (such as adding new difficulties to a beatmap set, or creating a clone of an existing object that needs a unique name).
+ /// If is already present in ,
+ /// this method will append the lowest possible number in brackets that doesn't conflict with
+ /// to and return that.
+ /// See osu.Game.Tests.Utils.NamingUtilsTest for concrete examples of behaviour.
+ ///
+ ///
+ /// and are compared in a case-insensitive manner,
+ /// so this method is safe to use for naming files in a platform-invariant manner.
+ ///
+ ///
+ public static string GetNextBestName(IEnumerable existingNames, string desiredName)
+ {
+ string pattern = $@"^(?i){Regex.Escape(desiredName)}(?-i)( \((?[1-9][0-9]*)\))?$";
+ var regex = new Regex(pattern, RegexOptions.Compiled);
+ var takenNumbers = new HashSet();
+
+ foreach (string name in existingNames)
+ {
+ var match = regex.Match(name);
+ if (!match.Success)
+ continue;
+
+ string copyNumberString = match.Groups[@"copyNumber"].Value;
+
+ if (string.IsNullOrEmpty(copyNumberString))
+ {
+ takenNumbers.Add(0);
+ continue;
+ }
+
+ takenNumbers.Add(int.Parse(copyNumberString));
+ }
+
+ int bestNumber = 0;
+ while (takenNumbers.Contains(bestNumber))
+ bestNumber += 1;
+
+ return bestNumber == 0
+ ? desiredName
+ : $"{desiredName} ({bestNumber})";
+ }
+ }
+}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index a9c0226951..4c6f81defa 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -18,27 +18,27 @@
-
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 5e0b264834..99b9de3fe2 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -60,10 +60,10 @@
-
+
-
+
$(NoWarn);NU1605
@@ -79,15 +79,15 @@
-
-
-
+
+
+
-
-
+
+
-
-
+
+