diff --git a/osu.Android.props b/osu.Android.props
index ec223f98c2..24d07b4588 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,7 +52,7 @@
-
+
diff --git a/osu.Desktop/Windows/GameplayWinKeyBlocker.cs b/osu.Desktop/Windows/GameplayWinKeyBlocker.cs
index efc3f21149..dbfd170ea1 100644
--- a/osu.Desktop/Windows/GameplayWinKeyBlocker.cs
+++ b/osu.Desktop/Windows/GameplayWinKeyBlocker.cs
@@ -5,23 +5,23 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Platform;
-using osu.Game;
using osu.Game.Configuration;
+using osu.Game.Screens.Play;
namespace osu.Desktop.Windows
{
public class GameplayWinKeyBlocker : Component
{
private Bindable disableWinKey;
- private Bindable localUserPlaying;
+ private IBindable localUserPlaying;
[Resolved]
private GameHost host { get; set; }
[BackgroundDependencyLoader]
- private void load(OsuGame game, OsuConfigManager config)
+ private void load(ILocalUserPlayInfo localUserInfo, OsuConfigManager config)
{
- localUserPlaying = game.LocalUserPlaying.GetBoundCopy();
+ localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy();
localUserPlaying.BindValueChanged(_ => updateBlocking());
disableWinKey = config.GetBindable(OsuSetting.GameplayDisableWinKey);
diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs
index 4372ed938c..cfb3fe40be 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
{
- public class Movement : StrainSkill
+ public class Movement : StrainDecaySkill
{
private const float absolute_player_positioning_error = 16f;
private const float normalized_hitobject_radius = 41.0f;
diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs
index 2ba2ee6b4a..01d930d585 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs
@@ -10,7 +10,7 @@ using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
{
- public class Strain : StrainSkill
+ public class Strain : StrainDecaySkill
{
private const double individual_decay_base = 0.125;
private const double overall_decay_base = 0.30;
@@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
return individualStrain + overallStrain - CurrentStrain;
}
- protected override double GetPeakStrain(double offset)
+ protected override double CalculateInitialStrain(double offset)
=> applyDecay(individualStrain, offset - Previous[0].StartTime, individual_decay_base)
+ applyDecay(overallStrain, offset - Previous[0].StartTime, overall_decay_base);
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
index e47edc37cc..7bcd867a9c 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
@@ -10,7 +10,7 @@ using osu.Framework.Utils;
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{
- public abstract class OsuStrainSkill : StrainSkill
+ public abstract class OsuStrainSkill : StrainDecaySkill
{
///
/// The number of sections with the highest strains, which the peak strain reductions will apply to.
diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs
index d4f1602a46..c89527d8bd 100644
--- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs
+++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs
@@ -64,11 +64,14 @@ namespace osu.Game.Rulesets.Osu.Edit
if (hitObject is DrawableHitCircle circle)
{
- circle.ApproachCircle
- .FadeOutFromOne(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION * 4)
- .Expire();
+ using (circle.BeginAbsoluteSequence(circle.HitStateUpdateTime))
+ {
+ circle.ApproachCircle
+ .FadeOutFromOne(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION * 4)
+ .Expire();
- circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint);
+ circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint);
+ }
}
if (hitObject is IHasMainCirclePiece mainPieceContainer)
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs
index 769d021362..0c17ca66b9 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Colour.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
///
/// Calculates the colour coefficient of taiko difficulty.
///
- public class Colour : StrainSkill
+ public class Colour : StrainDecaySkill
{
protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 0.4;
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs
index a32f6ebe0d..973e55f4b4 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Rhythm.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
///
/// Calculates the rhythm coefficient of taiko difficulty.
///
- public class Rhythm : StrainSkill
+ public class Rhythm : StrainDecaySkill
{
protected override double SkillMultiplier => 10;
protected override double StrainDecayBase => 0;
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs
index 4cceadb23f..54cf233d69 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Stamina.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
///
/// The reference play style chosen uses two hands, with full alternating (the hand changes after every hit).
///
- public class Stamina : StrainSkill
+ public class Stamina : StrainDecaySkill
{
protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 0.4;
diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
index 8124bd4199..bab8dfc983 100644
--- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
+++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
@@ -50,10 +50,10 @@ namespace osu.Game.Tests.Skins.IO
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk"));
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(1));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(1));
// the first should be overwritten by the second import.
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
}
finally
{
@@ -76,10 +76,10 @@ namespace osu.Game.Tests.Skins.IO
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk"));
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(2));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
}
finally
{
@@ -101,10 +101,10 @@ namespace osu.Game.Tests.Skins.IO
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2.1", "skinner"), "skin2.osk"));
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(2));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
}
finally
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
index 3017428039..290ba3317b 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
@@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneHUDOverlay : OsuManualInputManagerTestScene
{
+ private OsuConfigManager localConfig;
+
private HUDOverlay hudOverlay;
[Cached]
@@ -31,8 +33,14 @@ namespace osu.Game.Tests.Visual.Gameplay
private Drawable hideTarget => hudOverlay.KeyCounter;
private FillFlowContainer keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().First();
- [Resolved]
- private OsuConfigManager config { get; set; }
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage));
+ }
+
+ [SetUp]
+ public void SetUp() => Schedule(() => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always));
[Test]
public void TestComboCounterIncrementing()
@@ -85,11 +93,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
createNew();
- HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringGameplay;
-
- AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode));
-
- AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
+ AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
@@ -98,37 +102,28 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("stop trigering", () => InputManager.ReleaseKey(Key.ControlLeft));
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
-
- AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
}
[Test]
public void TestExternalHideDoesntAffectConfig()
{
- HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringGameplay;
-
createNew();
- AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode));
-
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
- AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.HUDVisibilityMode));
+ AddAssert("config unchanged", () => localConfig.GetBindable(OsuSetting.HUDVisibilityMode).IsDefault);
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
- AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.HUDVisibilityMode));
+ AddAssert("config unchanged", () => localConfig.GetBindable(OsuSetting.HUDVisibilityMode).IsDefault);
}
[Test]
public void TestChangeHUDVisibilityOnHiddenKeyCounter()
{
- bool keyCounterVisibleValue = false;
-
createNew();
- AddStep("save keycounter visible value", () => keyCounterVisibleValue = config.Get(OsuSetting.KeyOverlay));
- AddStep("set keycounter visible false", () =>
+ AddStep("hide key overlay", () =>
{
- config.SetValue(OsuSetting.KeyOverlay, false);
+ localConfig.SetValue(OsuSetting.KeyOverlay, false);
hudOverlay.KeyCounter.AlwaysVisible.Value = false;
});
@@ -139,24 +134,16 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
AddUntilStep("hidetarget is visible", () => hideTarget.IsPresent);
AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent);
-
- AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue));
}
[Test]
public void TestHiddenHUDDoesntBlockSkinnableComponentsLoad()
{
- HUDVisibilityMode originalConfigValue = default;
-
- AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode));
-
- AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
+ AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
createNew();
AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded);
AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType().Single().ComponentsLoaded);
-
- AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
}
private void createNew(Action action = null)
@@ -175,5 +162,11 @@ namespace osu.Game.Tests.Visual.Gameplay
Child = hudOverlay;
});
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ localConfig?.Dispose();
+ base.Dispose(isDisposing);
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index bcbdcd2a4f..7e822f898e 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
var room = RoomManager.Rooms[1];
RoomManager.RemoveRoom(room);
- RoomManager.AddRoom(room);
+ RoomManager.AddOrUpdateRoom(room);
});
AddAssert("no selection", () => checkRoomSelected(null));
@@ -115,11 +115,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4);
- AddStep("filter one room", () => container.Filter(new FilterCriteria { SearchString = "1" }));
+ AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = "1" });
AddUntilStep("1 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 1);
- AddStep("remove filter", () => container.Filter(null));
+ AddStep("remove filter", () => container.Filter.Value = null);
AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4);
}
@@ -131,13 +131,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add rooms", () => RoomManager.AddRooms(3, new CatchRuleset().RulesetInfo));
// Todo: What even is this case...?
- AddStep("set empty filter criteria", () => container.Filter(null));
+ AddStep("set empty filter criteria", () => container.Filter.Value = null);
AddUntilStep("5 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 5);
- AddStep("filter osu! rooms", () => container.Filter(new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo }));
+ AddStep("filter osu! rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo });
AddUntilStep("2 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 2);
- AddStep("filter catch rooms", () => container.Filter(new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo }));
+ AddStep("filter catch rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo });
AddUntilStep("3 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 3);
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
index 65b1d6d53a..18e4a6c575 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
@@ -6,13 +6,17 @@ 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.Configuration;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
using osu.Game.Screens.Play;
+using osu.Game.Screens.Play.HUD;
+using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Tests.Beatmaps.IO;
using osu.Game.Users;
@@ -23,6 +27,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Resolved]
private OsuGameBase game { get; set; }
+ [Resolved]
+ private OsuConfigManager config { get; set; }
+
[Resolved]
private BeatmapManager beatmapManager { get; set; }
@@ -80,6 +87,32 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddWaitStep("wait a bit", 20);
}
+ [Test]
+ public void TestSpectatorPlayerInteractiveElementsHidden()
+ {
+ HUDVisibilityMode originalConfigValue = default;
+
+ AddStep("get original config hud visibility", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode));
+ AddStep("set config hud visibility to always", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always));
+
+ start(new[] { PLAYER_1_ID, PLAYER_2_ID });
+ loadSpectateScreen(false);
+
+ AddUntilStep("wait for player loaders", () => this.ChildrenOfType().Count() == 2);
+ AddAssert("all player loader settings hidden", () => this.ChildrenOfType().All(l => !l.ChildrenOfType>().Any()));
+
+ AddUntilStep("wait for players to load", () => spectatorScreen.AllPlayersLoaded);
+
+ // components wrapped in skinnable target containers load asynchronously, potentially taking more than one frame to load.
+ // therefore use until step rather than direct assert to account for that.
+ AddUntilStep("all interactive elements removed", () => this.ChildrenOfType().All(p =>
+ !p.ChildrenOfType().Any() &&
+ !p.ChildrenOfType().Any() &&
+ p.ChildrenOfType().SingleOrDefault()?.ShowHandle == false));
+
+ AddStep("restore config hud visibility", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
+ }
+
[Test]
public void TestTeamDisplay()
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
index 08b3fb98a8..e618b28f40 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
@@ -44,6 +44,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
private TestMultiplayer multiplayerScreen;
private TestMultiplayerClient client;
+ private TestRequestHandlingMultiplayerRoomManager roomManager => multiplayerScreen.RoomManager;
+
[Cached(typeof(UserLookupCache))]
private UserLookupCache lookupCache = new TestUserLookupCache();
@@ -68,7 +70,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("load dependencies", () =>
{
- client = new TestMultiplayerClient(multiplayerScreen.RoomManager);
+ client = new TestMultiplayerClient(roomManager);
// The screen gets suspended so it stops receiving updates.
Child = client;
@@ -132,39 +134,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestExitMidJoin()
{
- Room room = null;
-
AddStep("create room", () =>
{
- room = new Room
- {
- Name = { Value = "Test Room" },
- Playlist =
- {
- new PlaylistItem
- {
- Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
- }
- }
- };
- });
-
- AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria());
- AddStep("select room", () => InputManager.Key(Key.Down));
- AddStep("join room and immediately exit", () =>
- {
- multiplayerScreen.ChildrenOfType().Single().Open(room);
- Schedule(() => Stack.CurrentScreen.Exit());
- });
- }
-
- [Test]
- public void TestJoinRoomWithoutPassword()
- {
- AddStep("create room", () =>
- {
- multiplayerScreen.RoomManager.AddRoom(new Room
+ roomManager.AddServerSideRoom(new Room
{
Name = { Value = "Test Room" },
Playlist =
@@ -178,7 +150,39 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
});
- AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria());
+ AddStep("refresh rooms", () => this.ChildrenOfType().Single().UpdateFilter());
+ AddUntilStep("wait for room", () => this.ChildrenOfType().Any());
+
+ AddStep("select room", () => InputManager.Key(Key.Down));
+ AddStep("join room and immediately exit select", () =>
+ {
+ InputManager.Key(Key.Enter);
+ Schedule(() => Stack.CurrentScreen.Exit());
+ });
+ }
+
+ [Test]
+ public void TestJoinRoomWithoutPassword()
+ {
+ AddStep("create room", () =>
+ {
+ roomManager.AddServerSideRoom(new Room
+ {
+ Name = { Value = "Test Room" },
+ Playlist =
+ {
+ new PlaylistItem
+ {
+ Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
+ Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ }
+ }
+ });
+ });
+
+ AddStep("refresh rooms", () => this.ChildrenOfType().Single().UpdateFilter());
+ AddUntilStep("wait for room", () => this.ChildrenOfType().Any());
+
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("join room", () => InputManager.Key(Key.Enter));
@@ -211,7 +215,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create room", () =>
{
- multiplayerScreen.RoomManager.AddRoom(new Room
+ roomManager.AddServerSideRoom(new Room
{
Name = { Value = "Test Room" },
Password = { Value = "password" },
@@ -226,7 +230,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
});
- AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria());
+ AddStep("refresh rooms", () => this.ChildrenOfType().Single().UpdateFilter());
+ AddUntilStep("wait for room", () => this.ChildrenOfType().Any());
+
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("join room", () => InputManager.Key(Key.Enter));
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs
deleted file mode 100644
index b17427a30b..0000000000
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs
+++ /dev/null
@@ -1,157 +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 NUnit.Framework;
-using osu.Framework.Testing;
-using osu.Game.Online.Rooms;
-using osu.Game.Screens.OnlinePlay.Components;
-using osu.Game.Tests.Beatmaps;
-using osu.Game.Tests.Visual.OnlinePlay;
-
-namespace osu.Game.Tests.Visual.Multiplayer
-{
- [HeadlessTest]
- public class TestSceneMultiplayerRoomManager : MultiplayerTestScene
- {
- protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
-
- public TestSceneMultiplayerRoomManager()
- : base(false)
- {
- }
-
- [Test]
- public void TestPollsInitially()
- {
- AddStep("create room manager with a few rooms", () =>
- {
- RoomManager.CreateRoom(createRoom(r => r.Name.Value = "1"));
- RoomManager.PartRoom();
- RoomManager.CreateRoom(createRoom(r => r.Name.Value = "2"));
- RoomManager.PartRoom();
- RoomManager.ClearRooms();
- });
-
- AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2);
- AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value);
- }
-
- [Test]
- public void TestRoomsClearedOnDisconnection()
- {
- AddStep("create room manager with a few rooms", () =>
- {
- RoomManager.CreateRoom(createRoom());
- RoomManager.PartRoom();
- RoomManager.CreateRoom(createRoom());
- RoomManager.PartRoom();
- });
-
- AddStep("disconnect", () => Client.Disconnect());
-
- AddAssert("rooms cleared", () => ((RoomManager)RoomManager).Rooms.Count == 0);
- AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value);
- }
-
- [Test]
- public void TestRoomsPolledOnReconnect()
- {
- AddStep("create room manager with a few rooms", () =>
- {
- RoomManager.CreateRoom(createRoom());
- RoomManager.PartRoom();
- RoomManager.CreateRoom(createRoom());
- RoomManager.PartRoom();
- });
-
- AddStep("disconnect", () => Client.Disconnect());
- AddStep("connect", () => Client.Connect());
-
- AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2);
- AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value);
- }
-
- [Test]
- public void TestRoomsNotPolledWhenJoined()
- {
- AddStep("create room manager with a room", () =>
- {
- RoomManager.CreateRoom(createRoom());
- RoomManager.ClearRooms();
- });
-
- AddAssert("manager not polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 0);
- AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value);
- }
-
- [Test]
- public void TestMultiplayerRoomJoinedWhenCreated()
- {
- AddStep("create room manager with a room", () =>
- {
- RoomManager.CreateRoom(createRoom());
- });
-
- AddUntilStep("multiplayer room joined", () => Client.Room != null);
- }
-
- [Test]
- public void TestMultiplayerRoomPartedWhenAPIRoomParted()
- {
- AddStep("create room manager with a room", () =>
- {
- RoomManager.CreateRoom(createRoom());
- RoomManager.PartRoom();
- });
-
- AddAssert("multiplayer room parted", () => Client.Room == null);
- }
-
- [Test]
- public void TestMultiplayerRoomJoinedWhenAPIRoomJoined()
- {
- AddStep("create room manager with a room", () =>
- {
- var r = createRoom();
- RoomManager.CreateRoom(r);
- RoomManager.PartRoom();
- RoomManager.JoinRoom(r);
- });
-
- AddUntilStep("multiplayer room joined", () => Client.Room != null);
- }
-
- private Room createRoom(Action initFunc = null)
- {
- var room = new Room
- {
- Name =
- {
- Value = "test room"
- },
- Playlist =
- {
- new PlaylistItem
- {
- Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
- Ruleset = { Value = Ruleset.Value }
- }
- }
- };
-
- initFunc?.Invoke(room);
- return room;
- }
-
- private class TestDependencies : MultiplayerTestSceneDependencies
- {
- public TestDependencies()
- {
- // Need to set these values as early as possible.
- RoomManager.TimeBetweenListingPolls.Value = 1;
- RoomManager.TimeBetweenSelectionPolls.Value = 1;
- }
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
index cdc655500d..98882b659c 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
@@ -141,6 +141,12 @@ namespace osu.Game.Tests.Visual.Playlists
public IBindableList Rooms => null;
+ public void AddOrUpdateRoom(Room room) => throw new NotImplementedException();
+
+ public void RemoveRoom(Room room) => throw new NotImplementedException();
+
+ public void ClearRooms() => throw new NotImplementedException();
+
public void CreateRoom(Room room, Action onSuccess = null, Action onError = null)
{
if (CreateRequested == null)
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs
deleted file mode 100644
index 566452249f..0000000000
--- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs
+++ /dev/null
@@ -1,65 +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.Linq;
-using NUnit.Framework;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Utils;
-using osu.Game.Beatmaps;
-using osu.Game.Screens.Ranking.Expanded;
-using osuTK;
-
-namespace osu.Game.Tests.Visual.Ranking
-{
- public class TestSceneStarRatingDisplay : OsuTestScene
- {
- [Test]
- public void TestDisplay()
- {
- AddStep("load displays", () => Child = new FillFlowContainer
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- ChildrenEnumerable = new[]
- {
- 1.23,
- 2.34,
- 3.45,
- 4.56,
- 5.67,
- 6.78,
- 10.11,
- }.Select(starRating => new StarRatingDisplay(new StarDifficulty(starRating, 0))
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre
- })
- });
- }
-
- [Test]
- public void TestChangingStarRatingDisplay()
- {
- StarRatingDisplay starRating = null;
-
- AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Scale = new Vector2(3f),
- });
-
- AddRepeatStep("set random value", () =>
- {
- starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
- }, 10);
-
- AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
- {
- if (starRating != null)
- starRating.Current.Value = new StarDifficulty(d, 1);
- });
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
index fa2c9ecdea..57ba051214 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
@@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Settings
[SetUpSteps]
public void SetUpSteps()
{
+ AddUntilStep("wait for load", () => panel.ChildrenOfType().Any());
AddStep("Scroll to top", () => panel.ChildrenOfType().First().ScrollToTop());
AddWaitStep("wait for scroll", 5);
}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
index 115d2fec7d..0af77b3b5a 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
@@ -4,6 +4,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Settings
@@ -11,27 +12,39 @@ namespace osu.Game.Tests.Visual.Settings
[TestFixture]
public class TestSceneSettingsPanel : OsuTestScene
{
- private readonly SettingsPanel settings;
- private readonly DialogOverlay dialogOverlay;
+ private SettingsPanel settings;
+ private DialogOverlay dialogOverlay;
- public TestSceneSettingsPanel()
+ [SetUpSteps]
+ public void SetUpSteps()
{
- settings = new SettingsOverlay
+ AddStep("create settings", () =>
{
- State = { Value = Visibility.Visible }
- };
- Add(dialogOverlay = new DialogOverlay
- {
- Depth = -1
+ settings?.Expire();
+
+ Add(settings = new SettingsOverlay
+ {
+ State = { Value = Visibility.Visible }
+ });
});
}
+ [Test]
+ public void ToggleVisibility()
+ {
+ AddWaitStep("wait some", 5);
+ AddToggleStep("toggle editor visibility", visible => settings.ToggleVisibility());
+ }
+
[BackgroundDependencyLoader]
private void load()
{
- Dependencies.Cache(dialogOverlay);
+ Add(dialogOverlay = new DialogOverlay
+ {
+ Depth = -1
+ });
- Add(settings);
+ Dependencies.Cache(dialogOverlay);
}
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs
new file mode 100644
index 0000000000..052251d5a8
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs
@@ -0,0 +1,97 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneStarRatingDisplay : OsuTestScene
+ {
+ [TestCase(StarRatingDisplaySize.Regular)]
+ [TestCase(StarRatingDisplaySize.Small)]
+ public void TestDisplay(StarRatingDisplaySize size)
+ {
+ AddStep("load displays", () =>
+ {
+ Child = new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ Spacing = new Vector2(2f),
+ Direction = FillDirection.Horizontal,
+ ChildrenEnumerable = Enumerable.Range(0, 15).Select(i => new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ Spacing = new Vector2(2f),
+ Direction = FillDirection.Vertical,
+ ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i * (i >= 11 ? 25f : 1f) + j * 0.1f, 0), size)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }),
+ })
+ };
+ });
+ }
+
+ [Test]
+ public void TestSpectrum()
+ {
+ StarRatingDisplay starRating = null;
+
+ BindableDouble starRatingNumeric;
+
+ AddStep("load display", () =>
+ {
+ Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scale = new Vector2(3f),
+ };
+ });
+
+ AddStep("transform over spectrum", () =>
+ {
+ starRatingNumeric = new BindableDouble();
+ starRatingNumeric.BindValueChanged(val => starRating.Current.Value = new StarDifficulty(val.NewValue, 1));
+ this.TransformBindableTo(starRatingNumeric, 10, 10000, Easing.OutQuint);
+ });
+ }
+
+ [Test]
+ public void TestChangingStarRatingDisplay()
+ {
+ StarRatingDisplay starRating = null;
+
+ AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scale = new Vector2(3f),
+ });
+
+ AddRepeatStep("set random value", () =>
+ {
+ starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
+ }, 10);
+
+ AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
+ {
+ if (starRating != null)
+ starRating.Current.Value = new StarDifficulty(d, 1);
+ });
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs
new file mode 100644
index 0000000000..25cde5fb82
--- /dev/null
+++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs
@@ -0,0 +1,145 @@
+// 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.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Beatmaps.Drawables
+{
+ ///
+ /// A pill that displays the star rating of a beatmap.
+ ///
+ public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue
+ {
+ private readonly Box background;
+ private readonly SpriteIcon starIcon;
+ private readonly OsuSpriteText starsText;
+
+ private readonly BindableWithCurrent current = new BindableWithCurrent();
+
+ public Bindable Current
+ {
+ get => current.Current;
+ set => current.Current = value;
+ }
+
+ [Resolved]
+ private OsuColour colours { get; set; }
+
+ [Resolved(canBeNull: true)]
+ private OverlayColourProvider colourProvider { get; set; }
+
+ ///
+ /// Creates a new using an already computed .
+ ///
+ /// The already computed to display.
+ /// The size of the star rating display.
+ public StarRatingDisplay(StarDifficulty starDifficulty, StarRatingDisplaySize size = StarRatingDisplaySize.Regular)
+ {
+ Current.Value = starDifficulty;
+
+ AutoSizeAxes = Axes.Both;
+
+ MarginPadding margin = default;
+
+ switch (size)
+ {
+ case StarRatingDisplaySize.Small:
+ margin = new MarginPadding { Horizontal = 7f };
+ break;
+
+ case StarRatingDisplaySize.Range:
+ margin = new MarginPadding { Horizontal = 8f };
+ break;
+
+ case StarRatingDisplaySize.Regular:
+ margin = new MarginPadding { Horizontal = 8f, Vertical = 2f };
+ break;
+ }
+
+ InternalChild = new CircularContainer
+ {
+ Masking = true,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new GridContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ Margin = margin,
+ ColumnDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.Absolute, 3f),
+ new Dimension(GridSizeMode.AutoSize, minSize: 25f),
+ },
+ RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
+ Content = new[]
+ {
+ new[]
+ {
+ starIcon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Icon = FontAwesome.Solid.Star,
+ Size = new Vector2(8f),
+ },
+ Empty(),
+ starsText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Margin = new MarginPadding { Bottom = 1.5f },
+ // todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f
+ // see https://github.com/ppy/osu-framework/issues/3271.
+ Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold),
+ Shadow = false,
+ }
+ }
+ }
+ },
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Current.BindValueChanged(c =>
+ {
+ starsText.Text = c.NewValue.Stars.ToString("0.00");
+
+ background.Colour = colours.ForStarDifficulty(c.NewValue.Stars);
+
+ starIcon.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47");
+ starsText.Colour = c.NewValue.Stars >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f);
+ }, true);
+ }
+ }
+
+ public enum StarRatingDisplaySize
+ {
+ Small,
+ Range,
+ Regular,
+ }
+}
diff --git a/osu.Game/Database/MutableDatabaseBackedStore.cs b/osu.Game/Database/MutableDatabaseBackedStore.cs
index c9d0c4bc41..b0feb7bb78 100644
--- a/osu.Game/Database/MutableDatabaseBackedStore.cs
+++ b/osu.Game/Database/MutableDatabaseBackedStore.cs
@@ -36,6 +36,11 @@ namespace osu.Game.Database
///
public IQueryable ConsumableItems => AddIncludesForConsumption(ContextFactory.Get().Set());
+ ///
+ /// Access barebones items with no includes.
+ ///
+ public IQueryable Items => ContextFactory.Get().Set();
+
///
/// Add a to the database.
///
diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
index f77a3109c9..06a40e7245 100644
--- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
@@ -22,7 +22,10 @@ namespace osu.Game.Graphics.UserInterface
public void TakeFocus()
{
- if (allowImmediateFocus) GetContainingInputManager().ChangeFocus(this);
+ if (!allowImmediateFocus)
+ return;
+
+ Scheduler.Add(() => GetContainingInputManager().ChangeFocus(this), false);
}
public bool HoldFocus
diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs
index 8d686e8c2f..cbf3e6b3aa 100644
--- a/osu.Game/Graphics/UserInterface/Nub.cs
+++ b/osu.Game/Graphics/UserInterface/Nub.cs
@@ -6,6 +6,7 @@ using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
@@ -57,18 +58,13 @@ namespace osu.Game.Graphics.UserInterface
EdgeEffect = new EdgeEffectParameters
{
- Colour = GlowColour,
+ Colour = GlowColour.Opacity(0),
Type = EdgeEffectType.Glow,
Radius = 10,
Roundness = 8,
};
}
- protected override void LoadComplete()
- {
- FadeEdgeEffectTo(0);
- }
-
private bool glowing;
public bool Glowing
diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs
index cd9ca9f87f..82a3e73b84 100644
--- a/osu.Game/Graphics/UserInterface/OsuButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuButton.cs
@@ -36,6 +36,7 @@ namespace osu.Game.Graphics.UserInterface
public Color4 BackgroundColour
{
+ get => backgroundColour ?? Color4.White;
set
{
backgroundColour = value;
diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
index 61dd5fb2d9..42f628a75a 100644
--- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
@@ -69,6 +69,7 @@ namespace osu.Game.Graphics.UserInterface
BackgroundColour = Color4.Black.Opacity(0.5f);
MaskingContainer.CornerRadius = corner_radius;
+ Alpha = 0;
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
ItemsContainer.Padding = new MarginPadding(5);
@@ -94,9 +95,11 @@ namespace osu.Game.Graphics.UserInterface
protected override void AnimateClose()
{
- this.FadeOut(300, Easing.OutQuint);
if (wasOpened)
+ {
+ this.FadeOut(300, Easing.OutQuint);
sampleClose?.Play();
+ }
}
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs
index 75d9c8debb..d2bf953dbc 100644
--- a/osu.Game/Input/ConfineMouseTracker.cs
+++ b/osu.Game/Input/ConfineMouseTracker.cs
@@ -7,6 +7,7 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Game.Configuration;
+using osu.Game.Screens.Play;
namespace osu.Game.Input
{
@@ -24,14 +25,14 @@ namespace osu.Game.Input
private IBindable localUserPlaying;
[BackgroundDependencyLoader]
- private void load(OsuGame game, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager)
+ private void load(ILocalUserPlayInfo localUserInfo, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager)
{
frameworkConfineMode = frameworkConfigManager.GetBindable(FrameworkSetting.ConfineMouseMode);
frameworkWindowMode = frameworkConfigManager.GetBindable(FrameworkSetting.WindowMode);
frameworkWindowMode.BindValueChanged(_ => updateConfineMode());
osuConfineMode = osuConfigManager.GetBindable(OsuSetting.ConfineMouseMode);
- localUserPlaying = game.LocalUserPlaying.GetBoundCopy();
+ localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy();
osuConfineMode.ValueChanged += _ => updateConfineMode();
localUserPlaying.BindValueChanged(_ => updateConfineMode(), true);
diff --git a/osu.Game/Online/PollingComponent.cs b/osu.Game/Online/PollingComponent.cs
index 806c0047e7..243be8da44 100644
--- a/osu.Game/Online/PollingComponent.cs
+++ b/osu.Game/Online/PollingComponent.cs
@@ -47,39 +47,13 @@ namespace osu.Game.Online
pollIfNecessary();
}
- private bool pollIfNecessary()
+ ///
+ /// Immediately performs a .
+ ///
+ public void PollImmediately()
{
- // we must be loaded so we have access to clock.
- if (!IsLoaded) return false;
-
- // there's already a poll process running.
- if (pollingActive) return false;
-
- // don't try polling if the time between polls hasn't been set.
- if (TimeBetweenPolls.Value == 0) return false;
-
- if (!lastTimePolled.HasValue)
- {
- doPoll();
- return true;
- }
-
- if (Time.Current - lastTimePolled.Value > TimeBetweenPolls.Value)
- {
- doPoll();
- return true;
- }
-
- // not enough time has passed since the last poll. we do want to schedule a poll to happen, though.
+ lastTimePolled = Time.Current - TimeBetweenPolls.Value;
scheduleNextPoll();
- return false;
- }
-
- private void doPoll()
- {
- scheduledPoll = null;
- pollingActive = true;
- Poll().ContinueWith(_ => pollComplete());
}
///
@@ -90,13 +64,11 @@ namespace osu.Game.Online
return Task.CompletedTask;
}
- ///
- /// Immediately performs a .
- ///
- public void PollImmediately()
+ private void doPoll()
{
- lastTimePolled = Time.Current - TimeBetweenPolls.Value;
- scheduleNextPoll();
+ scheduledPoll = null;
+ pollingActive = true;
+ Poll().ContinueWith(_ => pollComplete());
}
///
@@ -111,6 +83,33 @@ namespace osu.Game.Online
pollIfNecessary();
}
+ private void pollIfNecessary()
+ {
+ // we must be loaded so we have access to clock.
+ if (!IsLoaded) return;
+
+ // there's already a poll process running.
+ if (pollingActive) return;
+
+ // don't try polling if the time between polls hasn't been set.
+ if (TimeBetweenPolls.Value == 0) return;
+
+ if (!lastTimePolled.HasValue)
+ {
+ doPoll();
+ return;
+ }
+
+ if (Time.Current - lastTimePolled.Value > TimeBetweenPolls.Value)
+ {
+ doPoll();
+ return;
+ }
+
+ // not enough time has passed since the last poll. we do want to schedule a poll to happen, though.
+ scheduleNextPoll();
+ }
+
private void scheduleNextPoll()
{
scheduledPoll?.Cancel();
diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs
index 4bd5b1a788..d964060f10 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -134,7 +134,7 @@ namespace osu.Game.Online.Rooms
/// The position of this in the list. This is not read from or written to the API.
///
[JsonIgnore]
- public readonly Bindable Position = new Bindable(-1);
+ public readonly Bindable Position = new Bindable(-1); // Todo: This does not need to exist.
public Room()
{
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index fb682e0909..0db63df69b 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -62,7 +62,7 @@ namespace osu.Game
/// The full osu! experience. Builds on top of to add menus and binding logic
/// for initial components that are generally retrieved via DI.
///
- public class OsuGame : OsuGameBase, IKeyBindingHandler
+ public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo
{
///
/// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications).
@@ -1085,5 +1085,7 @@ namespace osu.Game
if (newScreen == null)
Exit();
}
+
+ IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying;
}
}
diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs
index fd3ee16fe6..87a294cc10 100644
--- a/osu.Game/Overlays/RestoreDefaultValueButton.cs
+++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs
@@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.UserInterface;
@@ -38,12 +39,12 @@ namespace osu.Game.Overlays
current.ValueChanged += _ => UpdateState();
current.DefaultChanged += _ => UpdateState();
current.DisabledChanged += _ => UpdateState();
- UpdateState();
+
+ if (IsLoaded)
+ UpdateState();
}
}
- private Color4 buttonColour;
-
private bool hovering;
public RestoreDefaultValueButton()
@@ -58,12 +59,11 @@ namespace osu.Game.Overlays
private void load(OsuColour colour)
{
BackgroundColour = colour.Yellow;
- buttonColour = colour.Yellow;
Content.Width = 0.33f;
Content.CornerRadius = 3;
Content.EdgeEffect = new EdgeEffectParameters
{
- Colour = buttonColour.Opacity(0.1f),
+ Colour = BackgroundColour.Opacity(0.1f),
Type = EdgeEffectType.Glow,
Radius = 2,
};
@@ -81,7 +81,10 @@ namespace osu.Game.Overlays
protected override void LoadComplete()
{
base.LoadComplete();
- UpdateState();
+
+ // avoid unnecessary transforms on first display.
+ Alpha = currentAlpha;
+ Background.Colour = currentColour;
}
public LocalisableString TooltipText => "revert to default";
@@ -101,14 +104,16 @@ namespace osu.Game.Overlays
public void UpdateState() => Scheduler.AddOnce(updateState);
+ private float currentAlpha => current.IsDefault ? 0f : hovering && !current.Disabled ? 1f : 0.65f;
+ private ColourInfo currentColour => current.Disabled ? Color4.Gray : BackgroundColour;
+
private void updateState()
{
if (current == null)
return;
- this.FadeTo(current.IsDefault ? 0f :
- hovering && !current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint);
- this.FadeColour(current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint);
+ this.FadeTo(currentAlpha, 200, Easing.OutQuint);
+ Background.FadeColour(currentColour, 200, Easing.OutQuint);
}
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs
index 501f1b86b8..d697b45424 100644
--- a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs
@@ -21,17 +21,26 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
private SettingsDropdown dropdown;
- protected override void Dispose(bool isDisposing)
+ [BackgroundDependencyLoader]
+ private void load()
{
- base.Dispose(isDisposing);
-
- if (audio != null)
+ Children = new Drawable[]
{
- audio.OnNewDevice -= onDeviceChanged;
- audio.OnLostDevice -= onDeviceChanged;
- }
+ dropdown = new AudioDeviceSettingsDropdown
+ {
+ Keywords = new[] { "speaker", "headphone", "output" }
+ }
+ };
+
+ updateItems();
+
+ audio.OnNewDevice += onDeviceChanged;
+ audio.OnLostDevice += onDeviceChanged;
+ dropdown.Current = audio.AudioDevice;
}
+ private void onDeviceChanged(string name) => updateItems();
+
private void updateItems()
{
var deviceItems = new List { string.Empty };
@@ -50,26 +59,15 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
dropdown.Items = deviceItems.Distinct().ToList();
}
- private void onDeviceChanged(string name) => updateItems();
-
- protected override void LoadComplete()
+ protected override void Dispose(bool isDisposing)
{
- base.LoadComplete();
+ base.Dispose(isDisposing);
- Children = new Drawable[]
+ if (audio != null)
{
- dropdown = new AudioDeviceSettingsDropdown
- {
- Keywords = new[] { "speaker", "headphone", "output" }
- }
- };
-
- updateItems();
-
- dropdown.Current = audio.AudioDevice;
-
- audio.OnNewDevice += onDeviceChanged;
- audio.OnLostDevice += onDeviceChanged;
+ audio.OnNewDevice -= onDeviceChanged;
+ audio.OnLostDevice -= onDeviceChanged;
+ }
}
private class AudioDeviceSettingsDropdown : SettingsDropdown
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
index 124b3b804c..adf1453d1a 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
@@ -98,8 +98,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
- AutoSizeDuration = transition_duration,
- AutoSizeEasing = Easing.OutQuint,
Masking = true,
Children = new[]
{
@@ -176,13 +174,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
scalingMode.BindValueChanged(mode =>
{
scalingSettings.ClearTransforms();
- scalingSettings.AutoSizeAxes = mode.NewValue != ScalingMode.Off ? Axes.Y : Axes.None;
+ scalingSettings.AutoSizeDuration = transition_duration;
+ scalingSettings.AutoSizeEasing = Easing.OutQuint;
- if (mode.NewValue == ScalingMode.Off)
- scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
+ updateScalingModeVisibility();
+ });
- scalingSettings.ForEach(s => s.TransferValueOnCommit = mode.NewValue == ScalingMode.Everything);
- }, true);
+ // initial update bypasses transforms
+ updateScalingModeVisibility();
void updateResolutionDropdown()
{
@@ -191,6 +190,15 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
else
resolutionDropdown.Hide();
}
+
+ void updateScalingModeVisibility()
+ {
+ if (scalingMode.Value == ScalingMode.Off)
+ scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
+
+ scalingSettings.AutoSizeAxes = scalingMode.Value != ScalingMode.Off ? Axes.Y : Axes.None;
+ scalingSettings.ForEach(s => s.TransferValueOnCommit = scalingMode.Value == ScalingMode.Everything);
+ }
}
private void bindPreviewEvent(Bindable bindable)
diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs
index ef2027fdab..6621caef4e 100644
--- a/osu.Game/Overlays/Settings/SettingsItem.cs
+++ b/osu.Game/Overlays/Settings/SettingsItem.cs
@@ -93,15 +93,13 @@ namespace osu.Game.Overlays.Settings
public bool MatchingFilter
{
- set => this.FadeTo(value ? 1 : 0);
+ set => Alpha = value ? 1 : 0;
}
public bool FilteringActive { get; set; }
public event Action SettingChanged;
- private readonly RestoreDefaultValueButton restoreDefaultButton;
-
protected SettingsItem()
{
RelativeSizeAxes = Axes.X;
@@ -110,7 +108,6 @@ namespace osu.Game.Overlays.Settings
InternalChildren = new Drawable[]
{
- restoreDefaultButton = new RestoreDefaultValueButton(),
FlowContent = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
@@ -123,7 +120,7 @@ namespace osu.Game.Overlays.Settings
},
};
- // all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is
+ // IMPORTANT: all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is
// never loaded, but requires bindable storage.
if (controlWithCurrent == null)
throw new ArgumentException(@$"Control created via {nameof(CreateControl)} must implement {nameof(IHasCurrentValue)}");
@@ -132,12 +129,17 @@ namespace osu.Game.Overlays.Settings
controlWithCurrent.Current.DisabledChanged += _ => updateDisabled();
}
- protected override void LoadComplete()
+ [BackgroundDependencyLoader]
+ private void load()
{
- base.LoadComplete();
-
+ // intentionally done before LoadComplete to avoid overhead.
if (ShowsDefaultIndicator)
- restoreDefaultButton.Current = controlWithCurrent.Current;
+ {
+ AddInternal(new RestoreDefaultValueButton
+ {
+ Current = controlWithCurrent.Current,
+ });
+ }
}
private void updateDisabled()
diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs
index 30a53b351d..cf6a313a1f 100644
--- a/osu.Game/Overlays/Settings/SidebarButton.cs
+++ b/osu.Game/Overlays/Settings/SidebarButton.cs
@@ -22,6 +22,9 @@ namespace osu.Game.Overlays.Settings
private readonly Box selectionIndicator;
private readonly Container text;
+ // always consider as part of flow, even when not visible (for the sake of the initial animation).
+ public override bool IsPresent => true;
+
private SettingsSection section;
public SettingsSection Section
diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs
index f1c41c4b50..376e36ea4e 100644
--- a/osu.Game/Overlays/SettingsPanel.cs
+++ b/osu.Game/Overlays/SettingsPanel.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
@@ -58,6 +59,12 @@ namespace osu.Game.Overlays
private readonly bool showSidebar;
+ private LoadingLayer loading;
+
+ private readonly List loadableSections = new List();
+
+ private Task sectionsLoadingTask;
+
protected SettingsPanel(bool showSidebar)
{
this.showSidebar = showSidebar;
@@ -86,69 +93,48 @@ namespace osu.Game.Overlays
Colour = OsuColour.Gray(0.05f),
Alpha = 1,
},
- SectionsContainer = new SettingsSectionsContainer
+ loading = new LoadingLayer
{
- Masking = true,
- RelativeSizeAxes = Axes.Both,
- ExpandableHeader = CreateHeader(),
- FixedHeader = searchTextBox = new SeekLimitedSearchTextBox
- {
- RelativeSizeAxes = Axes.X,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Width = 0.95f,
- Margin = new MarginPadding
- {
- Top = 20,
- Bottom = 20
- },
- },
- Footer = CreateFooter()
- },
+ State = { Value = Visibility.Visible }
+ }
}
};
+ Add(SectionsContainer = new SettingsSectionsContainer
+ {
+ Masking = true,
+ RelativeSizeAxes = Axes.Both,
+ ExpandableHeader = CreateHeader(),
+ FixedHeader = searchTextBox = new SeekLimitedSearchTextBox
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Width = 0.95f,
+ Margin = new MarginPadding
+ {
+ Top = 20,
+ Bottom = 20
+ },
+ },
+ Footer = CreateFooter().With(f => f.Alpha = 0)
+ });
+
if (showSidebar)
{
AddInternal(Sidebar = new Sidebar { Width = sidebar_width });
-
- SectionsContainer.SelectedSection.ValueChanged += section =>
- {
- selectedSidebarButton.Selected = false;
- selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue);
- selectedSidebarButton.Selected = true;
- };
}
- searchTextBox.Current.ValueChanged += term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue;
-
CreateSections()?.ForEach(AddSection);
}
protected void AddSection(SettingsSection section)
{
- SectionsContainer.Add(section);
+ if (IsLoaded)
+ // just to keep things simple. can be accommodated for if we ever need it.
+ throw new InvalidOperationException("All sections must be added before the panel is loaded.");
- if (Sidebar != null)
- {
- var button = new SidebarButton
- {
- Section = section,
- Action = () =>
- {
- SectionsContainer.ScrollTo(section);
- Sidebar.State = ExpandedState.Contracted;
- },
- };
-
- Sidebar.Add(button);
-
- if (selectedSidebarButton == null)
- {
- selectedSidebarButton = Sidebar.Children.First();
- selectedSidebarButton.Selected = true;
- }
- }
+ loadableSections.Add(section);
}
protected virtual Drawable CreateHeader() => new Container();
@@ -161,6 +147,12 @@ namespace osu.Game.Overlays
ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint);
+ // delay load enough to ensure it doesn't overlap with the initial animation.
+ // this is done as there is still a brief stutter during load completion which is more visible if the transition is in progress.
+ // the eventual goal would be to remove the need for this by splitting up load into smaller work pieces, or fixing the remaining
+ // load complete overheads.
+ Scheduler.AddDelayed(loadSections, TRANSITION_LENGTH / 3);
+
Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint);
this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint);
@@ -199,6 +191,78 @@ namespace osu.Game.Overlays
Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
}
+ private const double fade_in_duration = 1000;
+
+ private void loadSections()
+ {
+ if (sectionsLoadingTask != null)
+ return;
+
+ sectionsLoadingTask = LoadComponentsAsync(loadableSections, sections =>
+ {
+ SectionsContainer.AddRange(sections);
+ SectionsContainer.Footer.FadeInFromZero(fade_in_duration, Easing.OutQuint);
+ SectionsContainer.SearchContainer.FadeInFromZero(fade_in_duration, Easing.OutQuint);
+
+ loading.Hide();
+
+ searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue, true);
+ searchTextBox.TakeFocus();
+
+ loadSidebarButtons();
+ });
+ }
+
+ private void loadSidebarButtons()
+ {
+ if (Sidebar == null)
+ return;
+
+ LoadComponentsAsync(createSidebarButtons(), buttons =>
+ {
+ float delay = 0;
+
+ foreach (var button in buttons)
+ {
+ Sidebar.Add(button);
+
+ button.FadeOut()
+ .Delay(delay)
+ .FadeInFromZero(fade_in_duration, Easing.OutQuint);
+
+ delay += 40;
+ }
+
+ SectionsContainer.SelectedSection.BindValueChanged(section =>
+ {
+ if (selectedSidebarButton != null)
+ selectedSidebarButton.Selected = false;
+
+ selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue);
+ selectedSidebarButton.Selected = true;
+ }, true);
+ });
+ }
+
+ private IEnumerable createSidebarButtons()
+ {
+ foreach (var section in SectionsContainer)
+ {
+ yield return new SidebarButton
+ {
+ Section = section,
+ Action = () =>
+ {
+ if (!SectionsContainer.IsLoaded)
+ return;
+
+ SectionsContainer.ScrollTo(section);
+ Sidebar.State = ExpandedState.Contracted;
+ },
+ };
+ }
+ }
+
private class NonMaskedContent : Container
{
// masking breaks the pan-out transform with nested sub-settings panels.
diff --git a/osu.Game/Performance/HighPerformanceSession.cs b/osu.Game/Performance/HighPerformanceSession.cs
index 661c1046f1..3ef0e0bf93 100644
--- a/osu.Game/Performance/HighPerformanceSession.cs
+++ b/osu.Game/Performance/HighPerformanceSession.cs
@@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Game.Screens.Play;
namespace osu.Game.Performance
{
@@ -12,9 +13,9 @@ namespace osu.Game.Performance
private readonly IBindable localUserPlaying = new Bindable();
[BackgroundDependencyLoader]
- private void load(OsuGame game)
+ private void load(ILocalUserPlayInfo localUserInfo)
{
- localUserPlaying.BindTo(game.LocalUserPlaying);
+ localUserPlaying.BindTo(localUserInfo.IsPlaying);
}
protected override void LoadComplete()
diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs
new file mode 100644
index 0000000000..73bab31e82
--- /dev/null
+++ b/osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs
@@ -0,0 +1,54 @@
+// 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.Game.Rulesets.Difficulty.Preprocessing;
+using osu.Game.Rulesets.Mods;
+
+namespace osu.Game.Rulesets.Difficulty.Skills
+{
+ ///
+ /// Used to processes strain values of s, keep track of strain levels caused by the processed objects
+ /// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
+ ///
+ public abstract class StrainDecaySkill : StrainSkill
+ {
+ ///
+ /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
+ ///
+ protected abstract double SkillMultiplier { get; }
+
+ ///
+ /// Determines how quickly strain decays for the given skill.
+ /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second.
+ ///
+ protected abstract double StrainDecayBase { get; }
+
+ ///
+ /// The current strain level.
+ ///
+ protected double CurrentStrain { get; private set; } = 1;
+
+ protected StrainDecaySkill(Mod[] mods)
+ : base(mods)
+ {
+ }
+
+ protected override double CalculateInitialStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime);
+
+ protected override double StrainValueAt(DifficultyHitObject current)
+ {
+ CurrentStrain *= strainDecay(current.DeltaTime);
+ CurrentStrain += StrainValueOf(current) * SkillMultiplier;
+
+ return CurrentStrain;
+ }
+
+ ///
+ /// Calculates the strain value of a . This value is affected by previously processed objects.
+ ///
+ protected abstract double StrainValueOf(DifficultyHitObject current);
+
+ private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
+ }
+}
diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs
index d4fcefab9b..0880f1b08e 100644
--- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs
+++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs
@@ -15,27 +15,11 @@ namespace osu.Game.Rulesets.Difficulty.Skills
///
public abstract class StrainSkill : Skill
{
- ///
- /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
- ///
- protected abstract double SkillMultiplier { get; }
-
- ///
- /// Determines how quickly strain decays for the given skill.
- /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second.
- ///
- protected abstract double StrainDecayBase { get; }
-
///
/// The weight by which each strain value decays.
///
protected virtual double DecayWeight => 0.9;
- ///
- /// The current strain level.
- ///
- protected double CurrentStrain { get; private set; } = 1;
-
///
/// The length of each strain section.
///
@@ -52,6 +36,11 @@ namespace osu.Game.Rulesets.Difficulty.Skills
{
}
+ ///
+ /// Returns the strain value at . This value is calculated with or without respect to previous objects.
+ ///
+ protected abstract double StrainValueAt(DifficultyHitObject current);
+
///
/// Process a and update current strain values accordingly.
///
@@ -68,10 +57,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills
currentSectionEnd += SectionLength;
}
- CurrentStrain *= strainDecay(current.DeltaTime);
- CurrentStrain += StrainValueOf(current) * SkillMultiplier;
-
- currentSectionPeak = Math.Max(CurrentStrain, currentSectionPeak);
+ currentSectionPeak = Math.Max(StrainValueAt(current), currentSectionPeak);
}
///
@@ -88,9 +74,9 @@ namespace osu.Game.Rulesets.Difficulty.Skills
/// The beginning of the new section in milliseconds.
private void startNewSectionFrom(double time)
{
- // The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries.
+ // The maximum strain of the new section is not zero by default
// This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level.
- currentSectionPeak = GetPeakStrain(time);
+ currentSectionPeak = CalculateInitialStrain(time);
}
///
@@ -98,7 +84,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills
///
/// The time to retrieve the peak strain at.
/// The peak strain.
- protected virtual double GetPeakStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime);
+ protected abstract double CalculateInitialStrain(double time);
///
/// Returns a live enumerable of the peak strains for each section of the beatmap,
@@ -124,12 +110,5 @@ namespace osu.Game.Rulesets.Difficulty.Skills
return difficulty;
}
-
- ///
- /// Calculates the strain value of a . This value is affected by previously processed objects.
- ///
- protected abstract double StrainValueOf(DifficultyHitObject current);
-
- private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
}
}
diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs
index 186514e868..3978378c3a 100644
--- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs
+++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Mods
{
// Intercept and extract the internal number bindable from DifficultyBindable.
// This will provide bounds and precision specifications for the slider bar.
- difficultyBindable = ((DifficultyBindable)value).GetBoundCopy();
+ difficultyBindable = (DifficultyBindable)value.GetBoundCopy();
sliderDisplayCurrent.BindTo(difficultyBindable.CurrentNumber);
base.Current = difficultyBindable;
diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs
index 664b88eef4..e4304795f2 100644
--- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs
+++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs
@@ -128,6 +128,6 @@ namespace osu.Game.Rulesets.Mods
ExtendedLimits.UnbindFrom(otherDifficultyBindable.ExtendedLimits);
}
- public new DifficultyBindable GetBoundCopy() => new DifficultyBindable { BindTarget = this };
+ protected override Bindable CreateInstance() => new DifficultyBindable();
}
}
diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs
index ff33f0c70d..dfe2992a7c 100644
--- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs
+++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs
@@ -41,6 +41,8 @@ namespace osu.Game.Screens.Edit
protected override int DefaultMaxValue => VALID_DIVISORS.Last();
protected override int DefaultPrecision => 1;
+ protected override Bindable CreateInstance() => new BindableBeatDivisor();
+
///
/// Retrieves the appropriate colour for a beat divisor.
///
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
index 6e57b8e88c..911c9fea51 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs
@@ -166,14 +166,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}
if (IsSelected)
- {
border.Show();
- colour = colour.Lighten(0.3f);
- }
else
- {
border.Hide();
- }
if (Item is IHasDuration duration && duration.Duration > 0)
circle.Colour = ColourInfo.GradientHorizontal(colour, colour.Lighten(0.4f));
@@ -212,14 +207,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
for (int i = 0; i < repeats.RepeatCount; i++)
{
- repeatsContainer.Add(new Circle
+ repeatsContainer.Add(new Tick
{
- Size = new Vector2(circle_size / 3),
- Alpha = 0.2f,
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.Centre,
- RelativePositionAxes = Axes.X,
- X = (float)(i + 1) / (repeats.RepeatCount + 1),
+ X = (float)(i + 1) / (repeats.RepeatCount + 1)
});
}
}
@@ -233,6 +223,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft;
+ private class Tick : Circle
+ {
+ public Tick()
+ {
+ Size = new Vector2(circle_size / 4);
+ Anchor = Anchor.CentreLeft;
+ Origin = Anchor.Centre;
+ RelativePositionAxes = Axes.X;
+ }
+ }
+
public class DragArea : Circle
{
private readonly HitObject hitObject;
@@ -304,20 +305,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private void updateState()
{
- if (hasMouseDown)
- {
- this.ScaleTo(0.7f, 200, Easing.OutQuint);
- }
- else if (IsHovered)
- {
- this.ScaleTo(0.8f, 200, Easing.OutQuint);
- }
- else
- {
- this.ScaleTo(0.6f, 200, Easing.OutQuint);
- }
+ float scale = 0.5f;
- this.FadeTo(IsHovered || hasMouseDown ? 0.8f : 0.2f, 200, Easing.OutQuint);
+ if (hasMouseDown)
+ scale = 0.6f;
+ else if (IsHovered)
+ scale = 0.7f;
+
+ this.ScaleTo(scale, 200, Easing.OutQuint);
+ this.FadeTo(IsHovered || hasMouseDown ? 1f : 0.9f, 200, Easing.OutQuint);
}
[Resolved]
diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
index e50784fcbe..1387b5a671 100644
--- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -14,8 +15,10 @@ namespace osu.Game.Screens.OnlinePlay.Components
///
public class ListingPollingComponent : RoomPollingComponent
{
- [Resolved]
- private Bindable currentFilter { get; set; }
+ public IBindable InitialRoomsReceived => initialRoomsReceived;
+ private readonly Bindable initialRoomsReceived = new Bindable();
+
+ public readonly Bindable Filter = new Bindable();
[Resolved]
private Bindable selectedRoom { get; set; }
@@ -23,9 +26,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
[BackgroundDependencyLoader]
private void load()
{
- currentFilter.BindValueChanged(_ =>
+ Filter.BindValueChanged(_ =>
{
- NotifyRoomsReceived(null);
+ RoomManager.ClearRooms();
+ initialRoomsReceived.Value = false;
+
if (IsLoaded)
PollImmediately();
});
@@ -38,24 +43,26 @@ namespace osu.Game.Screens.OnlinePlay.Components
if (!API.IsLoggedIn)
return base.Poll();
+ if (Filter.Value == null)
+ return base.Poll();
+
var tcs = new TaskCompletionSource();
pollReq?.Cancel();
- pollReq = new GetRoomsRequest(currentFilter.Value.Status, currentFilter.Value.Category);
+ pollReq = new GetRoomsRequest(Filter.Value.Status, Filter.Value.Category);
pollReq.Success += result =>
{
- for (int i = 0; i < result.Count; i++)
+ foreach (var existing in RoomManager.Rooms.ToArray())
{
- if (result[i].RoomID.Value == selectedRoom.Value?.RoomID.Value)
- {
- // The listing request always has less information than the opened room, so don't include it.
- result[i] = selectedRoom.Value;
- break;
- }
+ if (result.All(r => r.RoomID.Value != existing.RoomID.Value))
+ RoomManager.RemoveRoom(existing);
}
- NotifyRoomsReceived(result);
+ foreach (var incoming in result)
+ RoomManager.AddOrUpdateRoom(incoming);
+
+ initialRoomsReceived.Value = true;
tcs.SetResult(true);
};
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
index 422576648c..3b6c1c8be0 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
@@ -8,7 +8,6 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
@@ -17,15 +16,12 @@ using osu.Game.Rulesets;
namespace osu.Game.Screens.OnlinePlay.Components
{
- public abstract class RoomManager : CompositeDrawable, IRoomManager
+ public class RoomManager : Component, IRoomManager
{
public event Action RoomsUpdated;
private readonly BindableList rooms = new BindableList();
- public IBindable InitialRoomsReceived => initialRoomsReceived;
- private readonly Bindable initialRoomsReceived = new Bindable();
-
public IBindableList Rooms => rooms;
protected IBindable JoinedRoom => joinedRoom;
@@ -40,15 +36,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
[Resolved]
private IAPIProvider api { get; set; }
- protected RoomManager()
+ public RoomManager()
{
RelativeSizeAxes = Axes.Both;
-
- InternalChildren = CreatePollingComponents().Select(p =>
- {
- p.RoomsReceived = onRoomsReceived;
- return p;
- }).ToList();
}
protected override void Dispose(bool isDisposing)
@@ -67,10 +57,10 @@ namespace osu.Game.Screens.OnlinePlay.Components
{
joinedRoom.Value = room;
- update(room, result);
- addRoom(room);
+ AddOrUpdateRoom(result);
+ room.CopyFrom(result); // Also copy back to the source model, since this is likely to have been stored elsewhere.
- RoomsUpdated?.Invoke();
+ // The server may not contain all properties (such as password), so invoke success with the given room.
onSuccess?.Invoke(room);
};
@@ -118,84 +108,49 @@ namespace osu.Game.Screens.OnlinePlay.Components
private readonly HashSet ignoredRooms = new HashSet();
- private void onRoomsReceived(List received)
+ public void AddOrUpdateRoom(Room room)
{
- if (received == null)
- {
- ClearRooms();
+ Debug.Assert(room.RoomID.Value != null);
+
+ if (ignoredRooms.Contains(room.RoomID.Value.Value))
return;
- }
- // Remove past matches
- foreach (var r in rooms.ToList())
+ room.Position.Value = -room.RoomID.Value.Value;
+
+ try
{
- if (received.All(e => e.RoomID.Value != r.RoomID.Value))
- rooms.Remove(r);
- }
+ foreach (var pi in room.Playlist)
+ pi.MapObjects(beatmaps, rulesets);
- for (int i = 0; i < received.Count; i++)
+ var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value);
+ if (existing == null)
+ rooms.Add(room);
+ else
+ existing.CopyFrom(room);
+ }
+ catch (Exception ex)
{
- var room = received[i];
+ Logger.Error(ex, $"Failed to update room: {room.Name.Value}.");
- Debug.Assert(room.RoomID.Value != null);
-
- if (ignoredRooms.Contains(room.RoomID.Value.Value))
- continue;
-
- room.Position.Value = i;
-
- try
- {
- update(room, room);
- addRoom(room);
- }
- catch (Exception ex)
- {
- Logger.Error(ex, $"Failed to update room: {room.Name.Value}.");
-
- ignoredRooms.Add(room.RoomID.Value.Value);
- rooms.Remove(room);
- }
+ ignoredRooms.Add(room.RoomID.Value.Value);
+ rooms.Remove(room);
}
- RoomsUpdated?.Invoke();
- initialRoomsReceived.Value = true;
+ notifyRoomsUpdated();
}
- protected void RemoveRoom(Room room) => rooms.Remove(room);
+ public void RemoveRoom(Room room)
+ {
+ rooms.Remove(room);
+ notifyRoomsUpdated();
+ }
- protected void ClearRooms()
+ public void ClearRooms()
{
rooms.Clear();
- initialRoomsReceived.Value = false;
+ notifyRoomsUpdated();
}
- ///
- /// Updates a local with a remote copy.
- ///
- /// The local to update.
- /// The remote to update with.
- private void update(Room local, Room remote)
- {
- foreach (var pi in remote.Playlist)
- pi.MapObjects(beatmaps, rulesets);
-
- local.CopyFrom(remote);
- }
-
- ///
- /// Adds a to the list of available rooms.
- ///
- /// The to add.
- private void addRoom(Room room)
- {
- var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value);
- if (existing == null)
- rooms.Add(room);
- else
- existing.CopyFrom(room);
- }
-
- protected abstract IEnumerable CreatePollingComponents();
+ private void notifyRoomsUpdated() => Scheduler.AddOnce(() => RoomsUpdated?.Invoke());
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs
index b2ea3a05d6..cd224a7347 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs
@@ -1,29 +1,18 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
-using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Online;
using osu.Game.Online.API;
-using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Components
{
public abstract class RoomPollingComponent : PollingComponent
{
- ///
- /// Invoked when any s have been received from the API.
- ///
- /// Any s present locally but not returned by this event are to be removed from display.
- /// If null, the display of local rooms is reset to an initial state.
- ///
- ///
- public Action> RoomsReceived;
-
[Resolved]
protected IAPIProvider API { get; private set; }
- protected void NotifyRoomsReceived(List rooms) => RoomsReceived?.Invoke(rooms);
+ [Resolved]
+ protected IRoomManager RoomManager { get; private set; }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
index dcf3c94b76..88d9469f8c 100644
--- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.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 System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -48,17 +46,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
pollReq.Success += result =>
{
- // existing rooms need to be ordered by their position because the received of NotifyRoomsReceives expects to be able to sort them based on this order.
- var rooms = new List(roomManager.Rooms.OrderBy(r => r.Position.Value));
-
- int index = rooms.FindIndex(r => r.RoomID.Value == result.RoomID.Value);
-
- if (index < 0)
- return;
-
- rooms[index] = result;
-
- NotifyRoomsReceived(rooms);
+ RoomManager.AddOrUpdateRoom(result);
tcs.SetResult(true);
};
diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs
index a27b27b8ad..7b14acf924 100644
--- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs
@@ -10,8 +10,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
-using osu.Game.Screens.Ranking.Expanded;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Components
@@ -64,8 +64,8 @@ namespace osu.Game.Screens.OnlinePlay.Components
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
- minDisplay = new StarRatingDisplay(default),
- maxDisplay = new StarRatingDisplay(default)
+ minDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Range),
+ maxDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Range)
}
}
};
diff --git a/osu.Game/Screens/OnlinePlay/IRoomManager.cs b/osu.Game/Screens/OnlinePlay/IRoomManager.cs
index 34c1393ff1..6e1ffbda74 100644
--- a/osu.Game/Screens/OnlinePlay/IRoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/IRoomManager.cs
@@ -18,16 +18,29 @@ namespace osu.Game.Screens.OnlinePlay
///
event Action RoomsUpdated;
- ///
- /// Whether an initial listing of rooms has been received.
- ///
- IBindable InitialRoomsReceived { get; }
-
///
/// All the active s.
///
IBindableList Rooms { get; }
+ ///
+ /// Adds a to this .
+ /// If already existing, the local room will be updated with the given one.
+ ///
+ /// The incoming .
+ void AddOrUpdateRoom(Room room);
+
+ ///
+ /// Removes a from this .
+ ///
+ /// The to remove.
+ void RemoveRoom(Room room);
+
+ ///
+ /// Removes all s from this .
+ ///
+ void ClearRooms();
+
///
/// Creates a new .
///
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
index 46d9850fde..e243477a8c 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
@@ -30,8 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
public IReadOnlyList Rooms => roomFlow.FlowingChildren.Cast().ToArray();
- [Resolved(CanBeNull = true)]
- private Bindable filter { get; set; }
+ public readonly Bindable Filter = new Bindable();
[Resolved]
private Bindable selectedRoom { get; set; }
@@ -74,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
rooms.BindTo(roomManager.Rooms);
- filter?.BindValueChanged(criteria => Filter(criteria.NewValue));
+ Filter?.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true);
selectedRoom.BindValueChanged(selection =>
{
@@ -85,7 +84,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
private void updateSelection() =>
roomFlow.Children.ForEach(r => r.State = r.Room == selectedRoom.Value ? SelectionState.Selected : SelectionState.NotSelected);
- public void Filter(FilterCriteria criteria)
+ private void applyFilterCriteria(FilterCriteria criteria)
{
roomFlow.Children.ForEach(r =>
{
@@ -126,7 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
roomFlow.Add(new DrawableRoom(room));
}
- Filter(filter?.Value);
+ applyFilterCriteria(Filter?.Value);
updateSelection();
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index e0e5cc415e..8bed3d6049 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -13,14 +13,17 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
+using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Input;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Rulesets;
+using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Users;
@@ -42,10 +45,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
AutoSizeAxes = Axes.Both
};
- private readonly IBindable initialRoomsReceived = new Bindable();
- private readonly IBindable operationInProgress = new Bindable();
-
- private LoadingLayer loadingLayer;
+ protected ListingPollingComponent ListingPollingComponent { get; private set; }
[Resolved]
private Bindable selectedRoom { get; set; }
@@ -56,31 +56,34 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
[Resolved(CanBeNull = true)]
private OngoingOperationTracker ongoingOperationTracker { get; set; }
- [Resolved(CanBeNull = true)]
- private Bindable filter { get; set; }
-
[Resolved]
private IBindable ruleset { get; set; }
[CanBeNull]
private IDisposable joiningRoomOperation { get; set; }
+ [CanBeNull]
+ private LeasedBindable selectionLease;
+
+ private readonly Bindable filter = new Bindable(new FilterCriteria());
+ private readonly IBindable operationInProgress = new Bindable();
+ private readonly IBindable isIdle = new BindableBool();
+ private LoadingLayer loadingLayer;
private RoomsContainer roomsContainer;
private SearchTextBox searchTextBox;
private Dropdown statusDropdown;
- [CanBeNull]
- private LeasedBindable selectionLease;
-
- [BackgroundDependencyLoader]
- private void load()
+ [BackgroundDependencyLoader(true)]
+ private void load([CanBeNull] IdleTracker idleTracker)
{
- filter ??= new Bindable(new FilterCriteria());
+ if (idleTracker != null)
+ isIdle.BindTo(idleTracker.IsIdle);
OsuScrollContainer scrollContainer;
- InternalChildren = new[]
+ InternalChildren = new Drawable[]
{
+ ListingPollingComponent = CreatePollingComponent().With(c => c.Filter.BindTarget = filter),
loadingLayer = new LoadingLayer(true),
new Container
{
@@ -154,7 +157,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
RelativeSizeAxes = Axes.Both,
ScrollbarOverlapsContent = false,
- Child = roomsContainer = new RoomsContainer()
+ Child = roomsContainer = new RoomsContainer
+ {
+ Filter = { BindTarget = filter }
+ }
},
}
},
@@ -180,21 +186,22 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced());
ruleset.BindValueChanged(_ => UpdateFilter());
- initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived);
- initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer());
+ isIdle.BindValueChanged(_ => updatePollingRate(this.IsCurrentScreen()), true);
if (ongoingOperationTracker != null)
{
operationInProgress.BindTo(ongoingOperationTracker.InProgress);
- operationInProgress.BindValueChanged(_ => updateLoadingLayer(), true);
+ operationInProgress.BindValueChanged(_ => updateLoadingLayer());
}
+ ListingPollingComponent.InitialRoomsReceived.BindValueChanged(_ => updateLoadingLayer(), true);
+
updateFilter();
}
#region Filtering
- protected void UpdateFilter() => Scheduler.AddOnce(updateFilter);
+ public void UpdateFilter() => Scheduler.AddOnce(updateFilter);
private ScheduledDelegate scheduledFilterUpdate;
@@ -235,7 +242,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
-
onReturning();
}
@@ -275,11 +281,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private void onReturning()
{
+ updatePollingRate(true);
searchTextBox.HoldFocus = true;
}
private void onLeaving()
{
+ updatePollingRate(false);
searchTextBox.HoldFocus = false;
// ensure any password prompt is dismissed.
@@ -327,6 +335,24 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
this.Push(CreateRoomSubScreen(room));
}
+ private void updateLoadingLayer()
+ {
+ if (operationInProgress.Value || !ListingPollingComponent.InitialRoomsReceived.Value)
+ loadingLayer.Show();
+ else
+ loadingLayer.Hide();
+ }
+
+ private void updatePollingRate(bool isCurrentScreen)
+ {
+ if (!isCurrentScreen)
+ ListingPollingComponent.TimeBetweenPolls.Value = 0;
+ else
+ ListingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000;
+
+ Logger.Log($"Polling adjusted (listing: {ListingPollingComponent.TimeBetweenPolls.Value})");
+ }
+
protected abstract OsuButton CreateNewRoomButton();
///
@@ -337,13 +363,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
protected abstract RoomSubScreen CreateRoomSubScreen(Room room);
- private void updateLoadingLayer()
- {
- if (operationInProgress.Value || !initialRoomsReceived.Value)
- loadingLayer.Show();
- else
- loadingLayer.Hide();
- }
+ protected abstract ListingPollingComponent CreatePollingComponent();
private class LoungeSearchTextBox : SearchTextBox
{
diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs
index 2676453a7e..62a968b508 100644
--- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs
@@ -41,11 +41,13 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
protected override void PopIn()
{
+ base.PopIn();
Settings.MoveToY(0, TRANSITION_DURATION, Easing.OutQuint);
}
protected override void PopOut()
{
+ base.PopOut();
Settings.MoveToY(-1, TRANSITION_DURATION, Easing.InSine);
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
index 45928505bb..58b5b7bbeb 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.OnlinePlay.Components;
@@ -23,35 +22,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
client.ChangeState(MultiplayerUserState.Idle);
}
- protected override void UpdatePollingRate(bool isIdle)
- {
- var multiplayerRoomManager = (MultiplayerRoomManager)RoomManager;
-
- if (!this.IsCurrentScreen())
- {
- multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
- multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
- }
- else
- {
- switch (CurrentSubScreen)
- {
- case LoungeSubScreen _:
- multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000;
- multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000;
- break;
-
- // Don't poll inside the match or anywhere else.
- default:
- multiplayerRoomManager.TimeBetweenListingPolls.Value = 0;
- multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0;
- break;
- }
- }
-
- Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})");
- }
-
protected override string ScreenTitle => "Multiplayer";
protected override RoomManager CreateRoomManager() => new MultiplayerRoomManager();
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
index ad7882abc2..d152fc3913 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
@@ -1,12 +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 System.Threading.Tasks;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Logging;
+using osu.Framework.Screens;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
+using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
@@ -21,6 +25,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[Resolved]
private MultiplayerClient client { get; set; }
+ public override void OnResuming(IScreen last)
+ {
+ base.OnResuming(last);
+
+ // Upon having left a room, we don't know whether we were the only participant, and whether the room is now closed as a result of leaving it.
+ // To work around this, temporarily remove the room and trigger an immediate listing poll.
+ if (last is MultiplayerMatchSubScreen match)
+ {
+ RoomManager.RemoveRoom(match.Room);
+ ListingPollingComponent.PollImmediately();
+ }
+ }
+
protected override FilterCriteria CreateFilterCriteria()
{
var criteria = base.CreateFilterCriteria();
@@ -39,6 +56,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room);
+ protected override ListingPollingComponent CreatePollingComponent() => new MultiplayerListingPollingComponent();
+
protected override void OpenNewRoom(Room room)
{
if (client?.IsConnected.Value != true)
@@ -49,5 +68,32 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
base.OpenNewRoom(room);
}
+
+ private class MultiplayerListingPollingComponent : ListingPollingComponent
+ {
+ [Resolved]
+ private MultiplayerClient client { get; set; }
+
+ private readonly IBindable isConnected = new Bindable();
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ isConnected.BindTo(client.IsConnected);
+ isConnected.BindValueChanged(c => Scheduler.AddOnce(() =>
+ {
+ if (isConnected.Value && IsLoaded)
+ PollImmediately();
+ }), true);
+ }
+
+ protected override Task Poll()
+ {
+ if (!isConnected.Value)
+ return Task.CompletedTask;
+
+ return base.Poll();
+ }
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
index 1943ff668f..6277afa8bb 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
@@ -42,6 +42,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
public override string ShortTitle => "room";
+ public readonly Room Room;
+
[Resolved]
private MultiplayerClient client { get; set; }
@@ -49,9 +51,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private OngoingOperationTracker ongoingOperationTracker { get; set; }
[Resolved]
- private Bindable currentRoom { get; set; }
-
- private MultiplayerMatchSettingsOverlay settingsOverlay;
+ private Bindable currentRoom { get; set; } // Todo: This should not exist.
private readonly IBindable isConnected = new Bindable();
@@ -59,9 +59,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private IDisposable readyClickOperation;
private GridContainer mainContent;
+ private MultiplayerMatchSettingsOverlay settingsOverlay;
public MultiplayerMatchSubScreen(Room room)
{
+ Room = room;
+
Title = room.RoomID.Value == null ? "New room" : room.Name.Value;
Activity.Value = new UserActivity.InLobby(room);
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
index cbba4babe5..2d94b2328d 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
@@ -2,11 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Threading.Tasks;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Extensions.ExceptionExtensions;
using osu.Framework.Logging;
using osu.Game.Online.Multiplayer;
@@ -21,22 +18,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[Resolved]
private MultiplayerClient multiplayerClient { get; set; }
- public readonly Bindable TimeBetweenListingPolls = new Bindable();
- public readonly Bindable TimeBetweenSelectionPolls = new Bindable();
- private readonly IBindable isConnected = new Bindable();
- private readonly Bindable allowPolling = new Bindable();
-
- private ListingPollingComponent listingPollingComponent;
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- isConnected.BindTo(multiplayerClient.IsConnected);
- isConnected.BindValueChanged(_ => Scheduler.AddOnce(updatePolling));
- JoinedRoom.BindValueChanged(_ => Scheduler.AddOnce(updatePolling), true);
- }
-
public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null)
=> base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password.Value, onSuccess, onError), onError);
@@ -64,19 +45,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
if (JoinedRoom.Value == null)
return;
- var joinedRoom = JoinedRoom.Value;
-
base.PartRoom();
-
multiplayerClient.LeaveRoom();
-
- // Todo: This is not the way to do this. Basically when we're the only participant and the room closes, there's no way to know if this is actually the case.
- // This is delayed one frame because upon exiting the match subscreen, multiplayer updates the polling rate and messes with polling.
- Schedule(() =>
- {
- RemoveRoom(joinedRoom);
- listingPollingComponent.PollImmediately();
- });
}
private void joinMultiplayerRoom(Room room, string password, Action onSuccess = null, Action onError = null)
@@ -99,70 +69,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
}
});
}
-
- private void updatePolling()
- {
- if (!isConnected.Value)
- ClearRooms();
-
- // Don't poll when not connected or when a room has been joined.
- allowPolling.Value = isConnected.Value && JoinedRoom.Value == null;
- }
-
- protected override IEnumerable CreatePollingComponents() => new RoomPollingComponent[]
- {
- listingPollingComponent = new MultiplayerListingPollingComponent
- {
- TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls },
- AllowPolling = { BindTarget = allowPolling }
- },
- new MultiplayerSelectionPollingComponent
- {
- TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls },
- AllowPolling = { BindTarget = allowPolling }
- }
- };
-
- private class MultiplayerListingPollingComponent : ListingPollingComponent
- {
- public readonly IBindable AllowPolling = new Bindable();
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- AllowPolling.BindValueChanged(allowPolling =>
- {
- if (!allowPolling.NewValue)
- return;
-
- if (IsLoaded)
- PollImmediately();
- });
- }
-
- protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll();
- }
-
- private class MultiplayerSelectionPollingComponent : SelectionPollingComponent
- {
- public readonly IBindable AllowPolling = new Bindable();
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- AllowPolling.BindValueChanged(allowPolling =>
- {
- if (!allowPolling.NewValue)
- return;
-
- if (IsLoaded)
- PollImmediately();
- });
- }
-
- protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll();
- }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs
index 2c157b0564..ececa1e497 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
/// The score containing the player's replay.
/// The clock controlling the gameplay running state.
public MultiSpectatorPlayer([NotNull] Score score, [NotNull] ISpectatorPlayerClock spectatorPlayerClock)
- : base(score)
+ : base(score, new PlayerConfiguration { AllowUserInteraction = false })
{
this.spectatorPlayerClock = spectatorPlayerClock;
}
@@ -34,6 +34,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
private void load()
{
spectatorPlayerClock.WaitingOnFrames.BindTo(waitingOnFrames);
+
+ HUDOverlay.PlayerSettingsOverlay.Expire();
+ HUDOverlay.HoldToQuit.Expire();
}
protected override void UpdateAfterChildren()
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs
index 5a1d28e9c4..14bd8fa6dc 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayerLoader.cs
@@ -3,6 +3,7 @@
using System;
using JetBrains.Annotations;
+using osu.Framework.Allocation;
using osu.Game.Scoring;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
@@ -19,6 +20,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
{
}
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ PlayerSettings.Expire();
+ }
+
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
}
diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
index fd265e9978..e5962db608 100644
--- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
@@ -14,14 +14,12 @@ using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers;
-using osu.Game.Input;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
-using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
@@ -44,17 +42,12 @@ namespace osu.Game.Screens.OnlinePlay
private LoungeSubScreen loungeSubScreen;
private ScreenStack screenStack;
- private readonly IBindable isIdle = new BindableBool();
-
[Cached(Type = typeof(IRoomManager))]
protected RoomManager RoomManager { get; private set; }
[Cached]
private readonly Bindable selectedRoom = new Bindable();
- [Cached]
- private readonly Bindable currentFilter = new Bindable(new FilterCriteria());
-
[Cached]
private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker();
@@ -67,9 +60,6 @@ namespace osu.Game.Screens.OnlinePlay
[Resolved]
protected IAPIProvider API { get; private set; }
- [Resolved(CanBeNull = true)]
- private IdleTracker idleTracker { get; set; }
-
[Resolved(CanBeNull = true)]
private OsuLogo logo { get; set; }
@@ -147,12 +137,6 @@ namespace osu.Game.Screens.OnlinePlay
apiState.BindTo(API.State);
apiState.BindValueChanged(onlineStateChanged, true);
-
- if (idleTracker != null)
- {
- isIdle.BindTo(idleTracker.IsIdle);
- isIdle.BindValueChanged(idle => UpdatePollingRate(idle.NewValue), true);
- }
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
@@ -162,8 +146,6 @@ namespace osu.Game.Screens.OnlinePlay
return dependencies;
}
- protected abstract void UpdatePollingRate(bool isIdle);
-
private void forcefullyExit()
{
// This is temporary since we don't currently have a way to force screens to be exited
@@ -199,8 +181,6 @@ namespace osu.Game.Screens.OnlinePlay
screenStack.CurrentScreen.OnResuming(last);
base.OnResuming(last);
-
- UpdatePollingRate(isIdle.Value);
}
public override void OnSuspending(IScreen next)
@@ -210,8 +190,6 @@ namespace osu.Game.Screens.OnlinePlay
Debug.Assert(screenStack.CurrentScreen != null);
screenStack.CurrentScreen.OnSuspending(next);
-
- UpdatePollingRate(isIdle.Value);
}
public override bool OnExiting(IScreen next)
@@ -275,15 +253,13 @@ namespace osu.Game.Screens.OnlinePlay
if (newScreen is IOsuScreen newOsuScreen)
((IBindable)Activity).BindTo(newOsuScreen.Activity);
-
- UpdatePollingRate(isIdle.Value);
}
protected IScreen CurrentSubScreen => screenStack.CurrentScreen;
protected abstract string ScreenTitle { get; }
- protected abstract RoomManager CreateRoomManager();
+ protected virtual RoomManager CreateRoomManager() => new RoomManager();
protected abstract LoungeSubScreen CreateLounge();
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs
index 6a78e24ba1..1edeef77df 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs
@@ -1,53 +1,14 @@
// 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.Logging;
-using osu.Framework.Screens;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
-using osu.Game.Screens.OnlinePlay.Match;
namespace osu.Game.Screens.OnlinePlay.Playlists
{
public class Playlists : OnlinePlayScreen
{
- protected override void UpdatePollingRate(bool isIdle)
- {
- var playlistsManager = (PlaylistsRoomManager)RoomManager;
-
- if (!this.IsCurrentScreen())
- {
- playlistsManager.TimeBetweenListingPolls.Value = 0;
- playlistsManager.TimeBetweenSelectionPolls.Value = 0;
- }
- else
- {
- switch (CurrentSubScreen)
- {
- case LoungeSubScreen _:
- playlistsManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000;
- playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000;
- break;
-
- case RoomSubScreen _:
- playlistsManager.TimeBetweenListingPolls.Value = 0;
- playlistsManager.TimeBetweenSelectionPolls.Value = isIdle ? 30000 : 5000;
- break;
-
- default:
- playlistsManager.TimeBetweenListingPolls.Value = 0;
- playlistsManager.TimeBetweenSelectionPolls.Value = 0;
- break;
- }
- }
-
- Logger.Log($"Polling adjusted (listing: {playlistsManager.TimeBetweenListingPolls.Value}, selection: {playlistsManager.TimeBetweenSelectionPolls.Value})");
- }
-
protected override string ScreenTitle => "Playlists";
- protected override RoomManager CreateRoomManager() => new PlaylistsRoomManager();
-
protected override LoungeSubScreen CreateLounge() => new PlaylistsLoungeSubScreen();
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
index eee4d4f407..dced9b8691 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
+using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
@@ -66,6 +67,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room);
+ protected override ListingPollingComponent CreatePollingComponent() => new ListingPollingComponent();
+
private enum PlaylistsCategory
{
Any,
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs
deleted file mode 100644
index c55d1c3e94..0000000000
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomManager.cs
+++ /dev/null
@@ -1,21 +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.Collections.Generic;
-using osu.Framework.Bindables;
-using osu.Game.Screens.OnlinePlay.Components;
-
-namespace osu.Game.Screens.OnlinePlay.Playlists
-{
- public class PlaylistsRoomManager : RoomManager
- {
- public readonly Bindable TimeBetweenListingPolls = new Bindable();
- public readonly Bindable TimeBetweenSelectionPolls = new Bindable();
-
- protected override IEnumerable CreatePollingComponents() => new RoomPollingComponent[]
- {
- new ListingPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls } },
- new SelectionPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls } }
- };
- }
-}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 953c687087..682b055766 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -3,11 +3,14 @@
using System.Diagnostics;
using System.Linq;
+using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Logging;
using osu.Framework.Screens;
+using osu.Game.Input;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
@@ -33,12 +36,13 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
[Resolved(typeof(Room), nameof(Room.Playlist))]
private BindableList playlist { get; set; }
+ private readonly IBindable isIdle = new BindableBool();
+
private MatchSettingsOverlay settingsOverlay;
private MatchLeaderboard leaderboard;
-
private OverlinedHeader participantsHeader;
-
private GridContainer mainContent;
+ private SelectionPollingComponent selectionPollingComponent;
public PlaylistsRoomSubScreen(Room room)
{
@@ -46,11 +50,15 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
Activity.Value = new UserActivity.InLobby(room);
}
- [BackgroundDependencyLoader]
- private void load()
+ [BackgroundDependencyLoader(true)]
+ private void load([CanBeNull] IdleTracker idleTracker)
{
+ if (idleTracker != null)
+ isIdle.BindTo(idleTracker.IsIdle);
+
AddRangeInternal(new Drawable[]
{
+ selectionPollingComponent = new SelectionPollingComponent(),
mainContent = new GridContainer
{
RelativeSizeAxes = Axes.Both,
@@ -260,6 +268,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
{
base.LoadComplete();
+ isIdle.BindValueChanged(_ => updatePollingRate(), true);
+
roomId.BindValueChanged(id =>
{
if (id.NewValue == null)
@@ -275,6 +285,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}, true);
}
+ private void updatePollingRate()
+ {
+ selectionPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 30000 : 5000;
+ Logger.Log($"Polling adjusted (selection: {selectionPollingComponent.TimeBetweenPolls.Value})");
+ }
+
protected override Screen CreateGameplayScreen() => new PlayerLoader(() => new PlaylistsPlayer(SelectedItem.Value)
{
Exited = () => leaderboard.RefreshScores()
diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs
index 4265a83ce1..d77673580a 100644
--- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs
+++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs
@@ -10,12 +10,12 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play.HUD;
-using osu.Game.Screens.Ranking.Expanded;
using osuTK;
namespace osu.Game.Screens.Play
diff --git a/osu.Game/Screens/Play/ILocalUserPlayInfo.cs b/osu.Game/Screens/Play/ILocalUserPlayInfo.cs
new file mode 100644
index 0000000000..9a2259b12f
--- /dev/null
+++ b/osu.Game/Screens/Play/ILocalUserPlayInfo.cs
@@ -0,0 +1,17 @@
+// 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.Bindables;
+
+namespace osu.Game.Screens.Play
+{
+ [Cached]
+ public interface ILocalUserPlayInfo
+ {
+ ///
+ /// Whether the local user is currently playing.
+ ///
+ IBindable IsPlaying { get; }
+ }
+}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 09eaf1c543..5461c6ac6c 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play
{
[Cached]
[Cached(typeof(ISamplePlaybackDisabler))]
- public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler
+ public abstract class Player : ScreenWithBeatmapBackground, ISamplePlaybackDisabler, ILocalUserPlayInfo
{
///
/// The delay upon completion of the beatmap before displaying the results screen.
@@ -1052,5 +1052,7 @@ namespace osu.Game.Screens.Play
#endregion
IBindable ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled;
+
+ IBindable ILocalUserPlayInfo.IsPlaying => LocalUserPlaying;
}
}
diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs
index 18ee73374f..3aa424e5d5 100644
--- a/osu.Game/Screens/Play/PlayerConfiguration.cs
+++ b/osu.Game/Screens/Play/PlayerConfiguration.cs
@@ -20,6 +20,11 @@ namespace osu.Game.Screens.Play
///
public bool AllowRestart { get; set; } = true;
+ ///
+ /// Whether the player should be able to interact with this player instance.
+ ///
+ public bool AllowUserInteraction { get; set; } = true;
+
///
/// Whether the player should be allowed to skip intros/outros, advancing to the start of gameplay or the end of a storyboard.
///
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index 5f6b4ca2b0..969527a758 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -46,9 +46,14 @@ namespace osu.Game.Screens.Play
protected override bool PlayResumeSound => false;
- protected BeatmapMetadataDisplay MetadataInfo;
+ protected BeatmapMetadataDisplay MetadataInfo { get; private set; }
- protected VisualSettings VisualSettings;
+ ///
+ /// A fill flow containing the player settings groups, exposed for the ability to hide it from inheritors of the player loader.
+ ///
+ protected FillFlowContainer PlayerSettings { get; private set; }
+
+ protected VisualSettings VisualSettings { get; private set; }
protected Task LoadTask { get; private set; }
@@ -140,7 +145,7 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
- new FillFlowContainer
+ PlayerSettings = new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs
index f28622f42e..b27a9c5f5d 100644
--- a/osu.Game/Screens/Play/SongProgress.cs
+++ b/osu.Game/Screens/Play/SongProgress.cs
@@ -119,7 +119,8 @@ namespace osu.Game.Screens.Play
if (drawableRuleset != null)
{
- AllowSeeking.BindTo(drawableRuleset.HasReplayLoaded);
+ if (player?.Configuration.AllowUserInteraction == true)
+ ((IBindable)AllowSeeking).BindTo(drawableRuleset.HasReplayLoaded);
referenceClock = drawableRuleset.FrameStableClock;
Objects = drawableRuleset.Objects;
diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs
index f662a479ec..1dae28092a 100644
--- a/osu.Game/Screens/Play/SpectatorPlayer.cs
+++ b/osu.Game/Screens/Play/SpectatorPlayer.cs
@@ -23,7 +23,8 @@ namespace osu.Game.Screens.Play
protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap
- public SpectatorPlayer(Score score)
+ public SpectatorPlayer(Score score, PlayerConfiguration configuration = null)
+ : base(configuration)
{
this.score = score;
}
diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
index e10fe5726d..bcb5e7999f 100644
--- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
+++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs
deleted file mode 100644
index 2b86100be8..0000000000
--- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs
+++ /dev/null
@@ -1,129 +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.Globalization;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.UserInterface;
-using osu.Game.Beatmaps;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Containers;
-using osuTK;
-using osuTK.Graphics;
-
-namespace osu.Game.Screens.Ranking.Expanded
-{
- ///
- /// A pill that displays the star rating of a .
- ///
- public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue
- {
- private Box background;
- private FillFlowContainer content;
- private OsuTextFlowContainer textFlow;
-
- [Resolved]
- private OsuColour colours { get; set; }
-
- private readonly BindableWithCurrent current = new BindableWithCurrent();
-
- public Bindable Current
- {
- get => current.Current;
- set => current.Current = value;
- }
-
- ///
- /// Creates a new using an already computed .
- ///
- /// The already computed to display the star difficulty of.
- public StarRatingDisplay(StarDifficulty starDifficulty)
- {
- Current.Value = starDifficulty;
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache)
- {
- AutoSizeAxes = Axes.Both;
-
- InternalChildren = new Drawable[]
- {
- new CircularContainer
- {
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- Children = new Drawable[]
- {
- background = new Box
- {
- RelativeSizeAxes = Axes.Both,
- },
- }
- },
- content = new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Padding = new MarginPadding { Horizontal = 8, Vertical = 4 },
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(2, 0),
- Children = new Drawable[]
- {
- new SpriteIcon
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Size = new Vector2(7),
- Icon = FontAwesome.Solid.Star,
- },
- textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black))
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- TextAnchor = Anchor.BottomLeft,
- }
- }
- }
- };
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- Current.BindValueChanged(_ => updateDisplay(), true);
- }
-
- private void updateDisplay()
- {
- var starRatingParts = Current.Value.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.');
- string wholePart = starRatingParts[0];
- string fractionPart = starRatingParts[1];
- string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
-
- var stars = Current.Value.Stars;
-
- background.Colour = colours.ForStarDifficulty(stars);
- content.Colour = stars >= 6.5 ? colours.Orange1 : Color4.Black;
-
- textFlow.Clear();
- textFlow.AddText($"{wholePart}", s =>
- {
- s.Font = s.Font.With(size: 14);
- s.UseFullGlyphHeight = false;
- });
-
- textFlow.AddText($"{separator}{fractionPart}", s =>
- {
- s.Font = s.Font.With(size: 7);
- s.UseFullGlyphHeight = false;
- });
- }
- }
-}
diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
index 678e7d5d73..14c32f2348 100644
--- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs
+++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
@@ -29,7 +29,6 @@ using osu.Game.Extensions;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
-using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Select
diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs
index ea55fd28c2..0f805990b9 100644
--- a/osu.Game/Skinning/SkinManager.cs
+++ b/osu.Game/Skinning/SkinManager.cs
@@ -105,12 +105,18 @@ namespace osu.Game.Skinning
/// Returns a list of all usable s that have been loaded by the user.
///
/// A newly allocated list of available .
- public List GetAllUserSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
+ public List GetAllUserSkins(bool includeFiles = false)
+ {
+ if (includeFiles)
+ return ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
+
+ return ModelStore.Items.Where(s => !s.DeletePending).ToList();
+ }
public void SelectRandomSkin()
{
// choose from only user skins, removing the current selection to ensure a new one is chosen.
- var randomChoices = GetAllUsableSkins().Where(s => s.ID != CurrentSkinInfo.Value.ID).ToArray();
+ var randomChoices = ModelStore.Items.Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID).ToArray();
if (randomChoices.Length == 0)
{
@@ -118,7 +124,8 @@ namespace osu.Game.Skinning
return;
}
- CurrentSkinInfo.Value = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length));
+ var chosen = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length));
+ CurrentSkinInfo.Value = ModelStore.ConsumableItems.Single(i => i.ID == chosen.ID);
}
protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name };
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
index f2da66d666..2c0ca0b872 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
protected override Task JoinRoom(long roomId, string? password = null)
{
- var apiRoom = roomManager.Rooms.Single(r => r.RoomID.Value == roomId);
+ var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId);
if (password != apiRoom.Password.Value)
throw new InvalidOperationException("Invalid password.");
@@ -260,7 +260,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
Debug.Assert(Room != null);
- var apiRoom = roomManager.Rooms.Single(r => r.RoomID.Value == Room.RoomID);
+ var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == Room.RoomID);
var set = apiRoom.Playlist.FirstOrDefault(p => p.BeatmapID == beatmapId)?.Beatmap.Value.BeatmapSet
?? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId)?.BeatmapSet;
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs
index 2e56c8a094..c3a944f93c 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs
@@ -5,14 +5,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay.Components;
-using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Multiplayer;
namespace osu.Game.Tests.Visual.Multiplayer
@@ -32,10 +30,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Resolved]
private OsuGameBase game { get; set; }
- [Cached]
- public readonly Bindable Filter = new Bindable(new FilterCriteria());
-
- public new readonly List Rooms = new List();
+ public IReadOnlyList ServerSideRooms => serverSideRooms;
+ private readonly List serverSideRooms = new List();
private int currentRoomId;
private int currentPlaylistItemId;
@@ -60,7 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value);
apiRoom.Password.Value = createRoomRequest.Room.Password.Value;
- AddRoom(apiRoom);
+ AddServerSideRoom(apiRoom);
var responseRoom = new APICreatedRoom();
responseRoom.CopyFrom(createResponseRoom(apiRoom, false));
@@ -70,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
case JoinRoomRequest joinRoomRequest:
{
- var room = Rooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value);
+ var room = ServerSideRooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value);
if (joinRoomRequest.Password != room.Password.Value)
{
@@ -89,14 +85,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
case GetRoomsRequest getRoomsRequest:
var roomsWithoutParticipants = new List();
- foreach (var r in Rooms)
+ foreach (var r in ServerSideRooms)
roomsWithoutParticipants.Add(createResponseRoom(r, false));
getRoomsRequest.TriggerSuccess(roomsWithoutParticipants);
return true;
case GetRoomRequest getRoomRequest:
- getRoomRequest.TriggerSuccess(createResponseRoom(Rooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true));
+ getRoomRequest.TriggerSuccess(createResponseRoom(ServerSideRooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true));
return true;
case GetBeatmapSetRequest getBeatmapSetRequest:
@@ -132,17 +128,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
};
}
- public void AddRoom(Room room)
+ public void AddServerSideRoom(Room room)
{
room.RoomID.Value ??= currentRoomId++;
for (int i = 0; i < room.Playlist.Count; i++)
room.Playlist[i].ID = currentPlaylistItemId++;
- Rooms.Add(room);
+ serverSideRooms.Add(room);
}
- public new void RemoveRoom(Room room) => base.RemoveRoom(room);
-
private Room createResponseRoom(Room room, bool withParticipants)
{
var responseRoom = new Room();
@@ -152,9 +146,5 @@ namespace osu.Game.Tests.Visual.Multiplayer
responseRoom.RecentParticipants.Clear();
return responseRoom;
}
-
- public new void ClearRooms() => base.ClearRooms();
-
- public new void Schedule(Action action) => base.Schedule(action);
}
}
diff --git a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs
index d37a64fa4b..55fbb9f1a6 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/BasicTestRoomManager.cs
@@ -28,17 +28,25 @@ namespace osu.Game.Tests.Visual.OnlinePlay
IBindableList IRoomManager.Rooms => Rooms;
+ private int currentRoomId;
+
public void CreateRoom(Room room, Action onSuccess = null, Action onError = null)
{
room.RoomID.Value ??= Rooms.Select(r => r.RoomID.Value).Where(id => id != null).Select(id => id.Value).DefaultIfEmpty().Max() + 1;
onSuccess?.Invoke(room);
- AddRoom(room);
+ AddOrUpdateRoom(room);
}
- public void AddRoom(Room room)
+ public void AddOrUpdateRoom(Room room)
{
- Rooms.Add(room);
+ var existing = Rooms.FirstOrDefault(r => r.RoomID.Value != null && r.RoomID.Value == room.RoomID.Value);
+
+ if (existing != null)
+ existing.CopyFrom(room);
+ else
+ Rooms.Add(room);
+
RoomsUpdated?.Invoke();
}
@@ -48,6 +56,12 @@ namespace osu.Game.Tests.Visual.OnlinePlay
RoomsUpdated?.Invoke();
}
+ public void ClearRooms()
+ {
+ Rooms.Clear();
+ RoomsUpdated?.Invoke();
+ }
+
public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null)
{
JoinRoomRequested?.Invoke(room, password);
@@ -64,9 +78,9 @@ namespace osu.Game.Tests.Visual.OnlinePlay
{
var room = new Room
{
- RoomID = { Value = i },
- Position = { Value = i },
- Name = { Value = $"Room {i}" },
+ RoomID = { Value = currentRoomId },
+ Position = { Value = currentRoomId },
+ Name = { Value = $"Room {currentRoomId}" },
Host = { Value = new User { Username = "Host" } },
EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) },
Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal },
@@ -89,6 +103,8 @@ namespace osu.Game.Tests.Visual.OnlinePlay
}
CreateRoom(room);
+
+ currentRoomId++;
}
}
}
diff --git a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
index 6e1e831d9b..71acefb158 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
@@ -4,7 +4,6 @@
using osu.Framework.Bindables;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay;
-using osu.Game.Screens.OnlinePlay.Lounge.Components;
namespace osu.Game.Tests.Visual.OnlinePlay
{
@@ -23,11 +22,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
///
IRoomManager RoomManager { get; }
- ///
- /// The cached .
- ///
- Bindable Filter { get; }
-
///
/// The cached .
///
diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
index 997c910dd4..8716646074 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
@@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay;
-using osu.Game.Screens.OnlinePlay.Lounge.Components;
namespace osu.Game.Tests.Visual.OnlinePlay
{
@@ -20,7 +19,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
{
public Bindable SelectedRoom => OnlinePlayDependencies?.SelectedRoom;
public IRoomManager RoomManager => OnlinePlayDependencies?.RoomManager;
- public Bindable Filter => OnlinePlayDependencies?.Filter;
public OngoingOperationTracker OngoingOperationTracker => OnlinePlayDependencies?.OngoingOperationTracker;
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker => OnlinePlayDependencies?.AvailabilityTracker;
diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
index 05ba509a73..e39711b7ce 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
@@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay;
-using osu.Game.Screens.OnlinePlay.Lounge.Components;
namespace osu.Game.Tests.Visual.OnlinePlay
{
@@ -20,7 +19,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
{
public Bindable SelectedRoom { get; }
public IRoomManager RoomManager { get; }
- public Bindable Filter { get; }
public OngoingOperationTracker OngoingOperationTracker { get; }
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; }
@@ -36,7 +34,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
{
SelectedRoom = new Bindable();
RoomManager = CreateRoomManager();
- Filter = new Bindable(new FilterCriteria());
OngoingOperationTracker = new OngoingOperationTracker();
AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
@@ -44,7 +41,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
CacheAs(SelectedRoom);
CacheAs(RoomManager);
- CacheAs(Filter);
CacheAs(OngoingOperationTracker);
CacheAs(AvailabilityTracker);
CacheAs(new OverlayColourProvider(OverlayColourScheme.Plum));
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index d4dba9330f..928620b32e 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -36,7 +36,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 7e514afe74..77f9052e85 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -93,7 +93,7 @@
-
+