mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 17:43:05 +08:00
Merge branch 'master' into mod-settings-difficulty-cache
This commit is contained in:
commit
102320f8ae
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.813.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.813.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.818.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -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<bool> disableWinKey;
|
||||
private Bindable<bool> localUserPlaying;
|
||||
private IBindable<bool> 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<bool>(OsuSetting.GameplayDisableWinKey);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of sections with the highest strains, which the peak strain reductions will apply to.
|
||||
|
@ -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)
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// Calculates the colour coefficient of taiko difficulty.
|
||||
/// </summary>
|
||||
public class Colour : StrainSkill
|
||||
public class Colour : StrainDecaySkill
|
||||
{
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 0.4;
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// Calculates the rhythm coefficient of taiko difficulty.
|
||||
/// </summary>
|
||||
public class Rhythm : StrainSkill
|
||||
public class Rhythm : StrainDecaySkill
|
||||
{
|
||||
protected override double SkillMultiplier => 10;
|
||||
protected override double StrainDecayBase => 0;
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// <remarks>
|
||||
/// The reference play style chosen uses two hands, with full alternating (the hand changes after every hit).
|
||||
/// </remarks>
|
||||
public class Stamina : StrainSkill
|
||||
public class Stamina : StrainDecaySkill
|
||||
{
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 0.4;
|
||||
|
@ -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<SkinManager>().GetAllUserSkins().Count, Is.EqualTo(1));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Count, Is.EqualTo(1));
|
||||
|
||||
// the first should be overwritten by the second import.
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().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<SkinManager>().GetAllUserSkins().Count, Is.EqualTo(2));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Count, Is.EqualTo(2));
|
||||
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().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<SkinManager>().GetAllUserSkins().Count, Is.EqualTo(2));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Count, Is.EqualTo(2));
|
||||
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -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<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().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<HUDVisibilityMode>(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<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
|
||||
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
||||
AddAssert("config unchanged", () => originalConfigValue == config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
AddAssert("config unchanged", () => localConfig.GetBindable<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode).IsDefault);
|
||||
|
||||
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
|
||||
AddAssert("config unchanged", () => originalConfigValue == config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
AddAssert("config unchanged", () => localConfig.GetBindable<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode).IsDefault);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangeHUDVisibilityOnHiddenKeyCounter()
|
||||
{
|
||||
bool keyCounterVisibleValue = false;
|
||||
|
||||
createNew();
|
||||
AddStep("save keycounter visible value", () => keyCounterVisibleValue = config.Get<bool>(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<HUDVisibilityMode>(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<SkinnableTargetContainer>().Single().ComponentsLoaded);
|
||||
|
||||
AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||
}
|
||||
|
||||
private void createNew(Action<HUDOverlay> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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<HUDVisibilityMode>(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<PlayerLoader>().Count() == 2);
|
||||
AddAssert("all player loader settings hidden", () => this.ChildrenOfType<PlayerLoader>().All(l => !l.ChildrenOfType<FillFlowContainer<PlayerSettingsGroup>>().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<Player>().All(p =>
|
||||
!p.ChildrenOfType<PlayerSettingsOverlay>().Any() &&
|
||||
!p.ChildrenOfType<HoldForMenuButton>().Any() &&
|
||||
p.ChildrenOfType<SongProgressBar>().SingleOrDefault()?.ShowHandle == false));
|
||||
|
||||
AddStep("restore config hud visibility", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTeamDisplay()
|
||||
{
|
||||
|
@ -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<LoungeSubScreen>().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<LoungeSubScreen>().Single().UpdateFilter());
|
||||
AddUntilStep("wait for room", () => this.ChildrenOfType<DrawableRoom>().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<LoungeSubScreen>().Single().UpdateFilter());
|
||||
AddUntilStep("wait for room", () => this.ChildrenOfType<DrawableRoom>().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<LoungeSubScreen>().Single().UpdateFilter());
|
||||
AddUntilStep("wait for room", () => this.ChildrenOfType<DrawableRoom>().Any());
|
||||
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("join room", () => InputManager.Key(Key.Enter));
|
||||
|
||||
|
@ -1,157 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using 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<Room> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -141,6 +141,12 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
public IBindableList<Room> 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<Room> onSuccess = null, Action<string> onError = null)
|
||||
{
|
||||
if (CreateRequested == null)
|
||||
|
@ -1,65 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddUntilStep("wait for load", () => panel.ChildrenOfType<GlobalKeyBindingsSection>().Any());
|
||||
AddStep("Scroll to top", () => panel.ChildrenOfType<SettingsPanel.SettingsSectionsContainer>().First().ScrollToTop());
|
||||
AddWaitStep("wait for scroll", 5);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
145
osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs
Normal file
145
osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.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
|
||||
{
|
||||
/// <summary>
|
||||
/// A pill that displays the star rating of a beatmap.
|
||||
/// </summary>
|
||||
public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue<StarDifficulty>
|
||||
{
|
||||
private readonly Box background;
|
||||
private readonly SpriteIcon starIcon;
|
||||
private readonly OsuSpriteText starsText;
|
||||
|
||||
private readonly BindableWithCurrent<StarDifficulty> current = new BindableWithCurrent<StarDifficulty>();
|
||||
|
||||
public Bindable<StarDifficulty> Current
|
||||
{
|
||||
get => current.Current;
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OverlayColourProvider colourProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="StarRatingDisplay"/> using an already computed <see cref="StarDifficulty"/>.
|
||||
/// </summary>
|
||||
/// <param name="starDifficulty">The already computed <see cref="StarDifficulty"/> to display.</param>
|
||||
/// <param name="size">The size of the star rating display.</param>
|
||||
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,
|
||||
}
|
||||
}
|
@ -36,6 +36,11 @@ namespace osu.Game.Database
|
||||
/// </summary>
|
||||
public IQueryable<T> ConsumableItems => AddIncludesForConsumption(ContextFactory.Get().Set<T>());
|
||||
|
||||
/// <summary>
|
||||
/// Access barebones items with no includes.
|
||||
/// </summary>
|
||||
public IQueryable<T> Items => ContextFactory.Get().Set<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Add a <typeparamref name="T"/> to the database.
|
||||
/// </summary>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -36,6 +36,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public Color4 BackgroundColour
|
||||
{
|
||||
get => backgroundColour ?? Color4.White;
|
||||
set
|
||||
{
|
||||
backgroundColour = value;
|
||||
|
@ -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
|
||||
|
@ -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<bool> localUserPlaying;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGame game, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager)
|
||||
private void load(ILocalUserPlayInfo localUserInfo, FrameworkConfigManager frameworkConfigManager, OsuConfigManager osuConfigManager)
|
||||
{
|
||||
frameworkConfineMode = frameworkConfigManager.GetBindable<ConfineMouseMode>(FrameworkSetting.ConfineMouseMode);
|
||||
frameworkWindowMode = frameworkConfigManager.GetBindable<WindowMode>(FrameworkSetting.WindowMode);
|
||||
frameworkWindowMode.BindValueChanged(_ => updateConfineMode());
|
||||
|
||||
osuConfineMode = osuConfigManager.GetBindable<OsuConfineMouseMode>(OsuSetting.ConfineMouseMode);
|
||||
localUserPlaying = game.LocalUserPlaying.GetBoundCopy();
|
||||
localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy();
|
||||
|
||||
osuConfineMode.ValueChanged += _ => updateConfineMode();
|
||||
localUserPlaying.BindValueChanged(_ => updateConfineMode(), true);
|
||||
|
@ -47,39 +47,13 @@ namespace osu.Game.Online
|
||||
pollIfNecessary();
|
||||
}
|
||||
|
||||
private bool pollIfNecessary()
|
||||
/// <summary>
|
||||
/// Immediately performs a <see cref="Poll"/>.
|
||||
/// </summary>
|
||||
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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -90,13 +64,11 @@ namespace osu.Game.Online
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immediately performs a <see cref="Poll"/>.
|
||||
/// </summary>
|
||||
public void PollImmediately()
|
||||
private void doPoll()
|
||||
{
|
||||
lastTimePolled = Time.Current - TimeBetweenPolls.Value;
|
||||
scheduleNextPoll();
|
||||
scheduledPoll = null;
|
||||
pollingActive = true;
|
||||
Poll().ContinueWith(_ => pollComplete());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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();
|
||||
|
@ -134,7 +134,7 @@ namespace osu.Game.Online.Rooms
|
||||
/// The position of this <see cref="Room"/> in the list. This is not read from or written to the API.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public readonly Bindable<int> Position = new Bindable<int>(-1);
|
||||
public readonly Bindable<long> Position = new Bindable<long>(-1); // Todo: This does not need to exist.
|
||||
|
||||
public Room()
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ namespace osu.Game
|
||||
/// The full osu! experience. Builds on top of <see cref="OsuGameBase"/> to add menus and binding logic
|
||||
/// for initial components that are generally retrieved via DI.
|
||||
/// </summary>
|
||||
public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>
|
||||
public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>, ILocalUserPlayInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 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<bool> ILocalUserPlayInfo.IsPlaying => LocalUserPlaying;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,17 +21,26 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
|
||||
private SettingsDropdown<string> 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> { 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<string>
|
||||
|
@ -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<float> bindable)
|
||||
|
@ -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<T> restoreDefaultButton;
|
||||
|
||||
protected SettingsItem()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@ -110,7 +108,6 @@ namespace osu.Game.Overlays.Settings
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
restoreDefaultButton = new RestoreDefaultValueButton<T>(),
|
||||
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<T>)}");
|
||||
@ -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<T>
|
||||
{
|
||||
Current = controlWithCurrent.Current,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisabled()
|
||||
|
@ -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
|
||||
|
@ -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<SettingsSection> loadableSections = new List<SettingsSection>();
|
||||
|
||||
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<SidebarButton> 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<Drawable>
|
||||
{
|
||||
// masking breaks the pan-out transform with nested sub-settings panels.
|
||||
|
@ -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<bool> localUserPlaying = new Bindable<bool>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGame game)
|
||||
private void load(ILocalUserPlayInfo localUserInfo)
|
||||
{
|
||||
localUserPlaying.BindTo(game.LocalUserPlaying);
|
||||
localUserPlaying.BindTo(localUserInfo.IsPlaying);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
54
osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs
Normal file
54
osu.Game/Rulesets/Difficulty/Skills/StrainDecaySkill.cs
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to processes strain values of <see cref="DifficultyHitObject"/>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.
|
||||
/// </summary>
|
||||
public abstract class StrainDecaySkill : StrainSkill
|
||||
{
|
||||
/// <summary>
|
||||
/// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
|
||||
/// </summary>
|
||||
protected abstract double SkillMultiplier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
protected abstract double StrainDecayBase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The current strain level.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the strain value of a <see cref="DifficultyHitObject"/>. This value is affected by previously processed objects.
|
||||
/// </summary>
|
||||
protected abstract double StrainValueOf(DifficultyHitObject current);
|
||||
|
||||
private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
|
||||
}
|
||||
}
|
@ -15,27 +15,11 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
/// </summary>
|
||||
public abstract class StrainSkill : Skill
|
||||
{
|
||||
/// <summary>
|
||||
/// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
|
||||
/// </summary>
|
||||
protected abstract double SkillMultiplier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
protected abstract double StrainDecayBase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The weight by which each strain value decays.
|
||||
/// </summary>
|
||||
protected virtual double DecayWeight => 0.9;
|
||||
|
||||
/// <summary>
|
||||
/// The current strain level.
|
||||
/// </summary>
|
||||
protected double CurrentStrain { get; private set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The length of each strain section.
|
||||
/// </summary>
|
||||
@ -52,6 +36,11 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the strain value at <see cref="DifficultyHitObject"/>. This value is calculated with or without respect to previous objects.
|
||||
/// </summary>
|
||||
protected abstract double StrainValueAt(DifficultyHitObject current);
|
||||
|
||||
/// <summary>
|
||||
/// Process a <see cref="DifficultyHitObject"/> and update current strain values accordingly.
|
||||
/// </summary>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -88,9 +74,9 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
/// <param name="time">The beginning of the new section in milliseconds.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -98,7 +84,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
/// </summary>
|
||||
/// <param name="time">The time to retrieve the peak strain at.</param>
|
||||
/// <returns>The peak strain.</returns>
|
||||
protected virtual double GetPeakStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime);
|
||||
protected abstract double CalculateInitialStrain(double time);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a live enumerable of the peak strains for each <see cref="SectionLength"/> section of the beatmap,
|
||||
@ -124,12 +110,5 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the strain value of a <see cref="DifficultyHitObject"/>. This value is affected by previously processed objects.
|
||||
/// </summary>
|
||||
protected abstract double StrainValueOf(DifficultyHitObject current);
|
||||
|
||||
private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -128,6 +128,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
ExtendedLimits.UnbindFrom(otherDifficultyBindable.ExtendedLimits);
|
||||
}
|
||||
|
||||
public new DifficultyBindable GetBoundCopy() => new DifficultyBindable { BindTarget = this };
|
||||
protected override Bindable<float?> CreateInstance() => new DifficultyBindable();
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ namespace osu.Game.Screens.Edit
|
||||
protected override int DefaultMaxValue => VALID_DIVISORS.Last();
|
||||
protected override int DefaultPrecision => 1;
|
||||
|
||||
protected override Bindable<int> CreateInstance() => new BindableBeatDivisor();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the appropriate colour for a beat divisor.
|
||||
/// </summary>
|
||||
|
@ -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]
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -14,8 +15,10 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
/// </summary>
|
||||
public class ListingPollingComponent : RoomPollingComponent
|
||||
{
|
||||
[Resolved]
|
||||
private Bindable<FilterCriteria> currentFilter { get; set; }
|
||||
public IBindable<bool> InitialRoomsReceived => initialRoomsReceived;
|
||||
private readonly Bindable<bool> initialRoomsReceived = new Bindable<bool>();
|
||||
|
||||
public readonly Bindable<FilterCriteria> Filter = new Bindable<FilterCriteria>();
|
||||
|
||||
[Resolved]
|
||||
private Bindable<Room> 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<bool>();
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
@ -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<Room> rooms = new BindableList<Room>();
|
||||
|
||||
public IBindable<bool> InitialRoomsReceived => initialRoomsReceived;
|
||||
private readonly Bindable<bool> initialRoomsReceived = new Bindable<bool>();
|
||||
|
||||
public IBindableList<Room> Rooms => rooms;
|
||||
|
||||
protected IBindable<Room> 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<long> ignoredRooms = new HashSet<long>();
|
||||
|
||||
private void onRoomsReceived(List<Room> 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a local <see cref="Room"/> with a remote copy.
|
||||
/// </summary>
|
||||
/// <param name="local">The local <see cref="Room"/> to update.</param>
|
||||
/// <param name="remote">The remote <see cref="Room"/> to update with.</param>
|
||||
private void update(Room local, Room remote)
|
||||
{
|
||||
foreach (var pi in remote.Playlist)
|
||||
pi.MapObjects(beatmaps, rulesets);
|
||||
|
||||
local.CopyFrom(remote);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="Room"/> to the list of available rooms.
|
||||
/// </summary>
|
||||
/// <param name="room">The <see cref="Room"/> to add.</param>
|
||||
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<RoomPollingComponent> CreatePollingComponents();
|
||||
private void notifyRoomsUpdated() => Scheduler.AddOnce(() => RoomsUpdated?.Invoke());
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,18 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when any <see cref="Room"/>s have been received from the API.
|
||||
/// <para>
|
||||
/// Any <see cref="Room"/>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.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public Action<List<Room>> RoomsReceived;
|
||||
|
||||
[Resolved]
|
||||
protected IAPIProvider API { get; private set; }
|
||||
|
||||
protected void NotifyRoomsReceived(List<Room> rooms) => RoomsReceived?.Invoke(rooms);
|
||||
[Resolved]
|
||||
protected IRoomManager RoomManager { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using 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<Room>(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);
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -18,16 +18,29 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
/// </summary>
|
||||
event Action RoomsUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Whether an initial listing of rooms has been received.
|
||||
/// </summary>
|
||||
IBindable<bool> InitialRoomsReceived { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All the active <see cref="Room"/>s.
|
||||
/// </summary>
|
||||
IBindableList<Room> Rooms { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="Room"/> to this <see cref="IRoomManager"/>.
|
||||
/// If already existing, the local room will be updated with the given one.
|
||||
/// </summary>
|
||||
/// <param name="room">The incoming <see cref="Room"/>.</param>
|
||||
void AddOrUpdateRoom(Room room);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a <see cref="Room"/> from this <see cref="IRoomManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="room">The <see cref="Room"/> to remove.</param>
|
||||
void RemoveRoom(Room room);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all <see cref="Room"/>s from this <see cref="IRoomManager"/>.
|
||||
/// </summary>
|
||||
void ClearRooms();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Room"/>.
|
||||
/// </summary>
|
||||
|
@ -30,8 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
public IReadOnlyList<DrawableRoom> Rooms => roomFlow.FlowingChildren.Cast<DrawableRoom>().ToArray();
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private Bindable<FilterCriteria> filter { get; set; }
|
||||
public readonly Bindable<FilterCriteria> Filter = new Bindable<FilterCriteria>();
|
||||
|
||||
[Resolved]
|
||||
private Bindable<Room> 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();
|
||||
}
|
||||
|
@ -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<bool> initialRoomsReceived = new Bindable<bool>();
|
||||
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
|
||||
|
||||
private LoadingLayer loadingLayer;
|
||||
protected ListingPollingComponent ListingPollingComponent { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
private Bindable<Room> 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<FilterCriteria> filter { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||
|
||||
[CanBeNull]
|
||||
private IDisposable joiningRoomOperation { get; set; }
|
||||
|
||||
[CanBeNull]
|
||||
private LeasedBindable<Room> selectionLease;
|
||||
|
||||
private readonly Bindable<FilterCriteria> filter = new Bindable<FilterCriteria>(new FilterCriteria());
|
||||
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
|
||||
private readonly IBindable<bool> isIdle = new BindableBool();
|
||||
private LoadingLayer loadingLayer;
|
||||
private RoomsContainer roomsContainer;
|
||||
private SearchTextBox searchTextBox;
|
||||
private Dropdown<RoomStatusFilter> statusDropdown;
|
||||
|
||||
[CanBeNull]
|
||||
private LeasedBindable<Room> selectionLease;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([CanBeNull] IdleTracker idleTracker)
|
||||
{
|
||||
filter ??= new Bindable<FilterCriteria>(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();
|
||||
|
||||
/// <summary>
|
||||
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -1,12 +1,16 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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<bool> isConnected = new Bindable<bool>();
|
||||
|
||||
[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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Room> currentRoom { get; set; }
|
||||
|
||||
private MultiplayerMatchSettingsOverlay settingsOverlay;
|
||||
private Bindable<Room> currentRoom { get; set; } // Todo: This should not exist.
|
||||
|
||||
private readonly IBindable<bool> isConnected = new Bindable<bool>();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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<double> TimeBetweenListingPolls = new Bindable<double>();
|
||||
public readonly Bindable<double> TimeBetweenSelectionPolls = new Bindable<double>();
|
||||
private readonly IBindable<bool> isConnected = new Bindable<bool>();
|
||||
private readonly Bindable<bool> allowPolling = new Bindable<bool>();
|
||||
|
||||
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<Room> onSuccess = null, Action<string> 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<Room> onSuccess = null, Action<string> 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<RoomPollingComponent> 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<bool> AllowPolling = new Bindable<bool>();
|
||||
|
||||
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<bool> AllowPolling = new Bindable<bool>();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// <param name="score">The score containing the player's replay.</param>
|
||||
/// <param name="spectatorPlayerClock">The clock controlling the gameplay running state.</param>
|
||||
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()
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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<bool> isIdle = new BindableBool();
|
||||
|
||||
[Cached(Type = typeof(IRoomManager))]
|
||||
protected RoomManager RoomManager { get; private set; }
|
||||
|
||||
[Cached]
|
||||
private readonly Bindable<Room> selectedRoom = new Bindable<Room>();
|
||||
|
||||
[Cached]
|
||||
private readonly Bindable<FilterCriteria> currentFilter = new Bindable<FilterCriteria>(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<UserActivity>)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();
|
||||
|
||||
|
@ -1,53 +1,14 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.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();
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -1,21 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
public class PlaylistsRoomManager : RoomManager
|
||||
{
|
||||
public readonly Bindable<double> TimeBetweenListingPolls = new Bindable<double>();
|
||||
public readonly Bindable<double> TimeBetweenSelectionPolls = new Bindable<double>();
|
||||
|
||||
protected override IEnumerable<RoomPollingComponent> CreatePollingComponents() => new RoomPollingComponent[]
|
||||
{
|
||||
new ListingPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls } },
|
||||
new SelectionPollingComponent { TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls } }
|
||||
};
|
||||
}
|
||||
}
|
@ -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<PlaylistItem> playlist { get; set; }
|
||||
|
||||
private readonly IBindable<bool> 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()
|
||||
|
@ -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
|
||||
|
17
osu.Game/Screens/Play/ILocalUserPlayInfo.cs
Normal file
17
osu.Game/Screens/Play/ILocalUserPlayInfo.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
[Cached]
|
||||
public interface ILocalUserPlayInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the local user is currently playing.
|
||||
/// </summary>
|
||||
IBindable<bool> IsPlaying { get; }
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The delay upon completion of the beatmap before displaying the results screen.
|
||||
@ -1052,5 +1052,7 @@ namespace osu.Game.Screens.Play
|
||||
#endregion
|
||||
|
||||
IBindable<bool> ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled;
|
||||
|
||||
IBindable<bool> ILocalUserPlayInfo.IsPlaying => LocalUserPlaying;
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,11 @@ namespace osu.Game.Screens.Play
|
||||
/// </summary>
|
||||
public bool AllowRestart { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player should be able to interact with this player instance.
|
||||
/// </summary>
|
||||
public bool AllowUserInteraction { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player should be allowed to skip intros/outros, advancing to the start of gameplay or the end of a storyboard.
|
||||
/// </summary>
|
||||
|
@ -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;
|
||||
/// <summary>
|
||||
/// A fill flow containing the player settings groups, exposed for the ability to hide it from inheritors of the player loader.
|
||||
/// </summary>
|
||||
protected FillFlowContainer<PlayerSettingsGroup> 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<PlayerSettingsGroup>
|
||||
PlayerSettings = new FillFlowContainer<PlayerSettingsGroup>
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
|
@ -119,7 +119,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
if (drawableRuleset != null)
|
||||
{
|
||||
AllowSeeking.BindTo(drawableRuleset.HasReplayLoaded);
|
||||
if (player?.Configuration.AllowUserInteraction == true)
|
||||
((IBindable<bool>)AllowSeeking).BindTo(drawableRuleset.HasReplayLoaded);
|
||||
|
||||
referenceClock = drawableRuleset.FrameStableClock;
|
||||
Objects = drawableRuleset.Objects;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -1,129 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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
|
||||
{
|
||||
/// <summary>
|
||||
/// A pill that displays the star rating of a <see cref="BeatmapInfo"/>.
|
||||
/// </summary>
|
||||
public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue<StarDifficulty>
|
||||
{
|
||||
private Box background;
|
||||
private FillFlowContainer content;
|
||||
private OsuTextFlowContainer textFlow;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
private readonly BindableWithCurrent<StarDifficulty> current = new BindableWithCurrent<StarDifficulty>();
|
||||
|
||||
public Bindable<StarDifficulty> Current
|
||||
{
|
||||
get => current.Current;
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="StarRatingDisplay"/> using an already computed <see cref="StarDifficulty"/>.
|
||||
/// </summary>
|
||||
/// <param name="starDifficulty">The already computed <see cref="StarDifficulty"/> to display the star difficulty of.</param>
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -105,12 +105,18 @@ namespace osu.Game.Skinning
|
||||
/// Returns a list of all usable <see cref="SkinInfo"/>s that have been loaded by the user.
|
||||
/// </summary>
|
||||
/// <returns>A newly allocated list of available <see cref="SkinInfo"/>.</returns>
|
||||
public List<SkinInfo> GetAllUserSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
|
||||
public List<SkinInfo> 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 };
|
||||
|
@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
protected override Task<MultiplayerRoom> 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;
|
||||
|
||||
|
@ -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<FilterCriteria> Filter = new Bindable<FilterCriteria>(new FilterCriteria());
|
||||
|
||||
public new readonly List<Room> Rooms = new List<Room>();
|
||||
public IReadOnlyList<Room> ServerSideRooms => serverSideRooms;
|
||||
private readonly List<Room> serverSideRooms = new List<Room>();
|
||||
|
||||
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<Room>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -28,17 +28,25 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
|
||||
IBindableList<Room> IRoomManager.Rooms => Rooms;
|
||||
|
||||
private int currentRoomId;
|
||||
|
||||
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> 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<Room> onSuccess = null, Action<string> 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
/// </summary>
|
||||
IRoomManager RoomManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cached <see cref="FilterCriteria"/>.
|
||||
/// </summary>
|
||||
Bindable<FilterCriteria> Filter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cached <see cref="OngoingOperationTracker"/>.
|
||||
/// </summary>
|
||||
|
@ -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<Room> SelectedRoom => OnlinePlayDependencies?.SelectedRoom;
|
||||
public IRoomManager RoomManager => OnlinePlayDependencies?.RoomManager;
|
||||
public Bindable<FilterCriteria> Filter => OnlinePlayDependencies?.Filter;
|
||||
public OngoingOperationTracker OngoingOperationTracker => OnlinePlayDependencies?.OngoingOperationTracker;
|
||||
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker => OnlinePlayDependencies?.AvailabilityTracker;
|
||||
|
||||
|
@ -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<Room> SelectedRoom { get; }
|
||||
public IRoomManager RoomManager { get; }
|
||||
public Bindable<FilterCriteria> Filter { get; }
|
||||
public OngoingOperationTracker OngoingOperationTracker { get; }
|
||||
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; }
|
||||
|
||||
@ -36,7 +34,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
{
|
||||
SelectedRoom = new Bindable<Room>();
|
||||
RoomManager = CreateRoomManager();
|
||||
Filter = new Bindable<FilterCriteria>(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));
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.3.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.813.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.818.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.813.0" />
|
||||
<PackageReference Include="Sentry" Version="3.8.3" />
|
||||
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
||||
|
@ -70,7 +70,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.813.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.818.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.813.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
@ -93,7 +93,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.813.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.818.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user