Merge branch 'master' into editor-select-timing-on-enter

This commit is contained in:
Bartłomiej Dach 2023-02-11 16:27:22 +01:00
commit 774eae98cc
No known key found for this signature in database
28 changed files with 199 additions and 127 deletions

View File

@ -17,7 +17,7 @@
<EmbeddedResource Include="Resources\**\*.*" /> <EmbeddedResource Include="Resources\**\*.*" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Code Analysis"> <ItemGroup Label="Code Analysis">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3" PrivateAssets="All" /> <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" PrivateAssets="All" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" /> <AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Code Analysis"> <PropertyGroup Label="Code Analysis">

View File

@ -9,9 +9,9 @@
<GenerateProgramFile>false</GenerateProgramFile> <GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />

View File

@ -9,9 +9,9 @@
<GenerateProgramFile>false</GenerateProgramFile> <GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />

View File

@ -9,9 +9,9 @@
<GenerateProgramFile>false</GenerateProgramFile> <GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />

View File

@ -9,9 +9,9 @@
<GenerateProgramFile>false</GenerateProgramFile> <GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" /> <ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />

View File

@ -98,7 +98,7 @@ namespace osu.Desktop
if (status.Value is UserStatusOnline && activity.Value != null) if (status.Value is UserStatusOnline && activity.Value != null)
{ {
presence.State = truncate(activity.Value.Status); presence.State = truncate(activity.Value.GetStatus(privacyMode.Value == DiscordRichPresenceMode.Limited));
presence.Details = truncate(getDetails(activity.Value)); presence.Details = truncate(getDetails(activity.Value));
if (getBeatmap(activity.Value) is IBeatmapInfo beatmap && beatmap.OnlineID > 0) if (getBeatmap(activity.Value) is IBeatmapInfo beatmap && beatmap.OnlineID > 0)
@ -186,6 +186,9 @@ namespace osu.Desktop
case UserActivity.Editing edit: case UserActivity.Editing edit:
return edit.BeatmapInfo.ToString() ?? string.Empty; return edit.BeatmapInfo.ToString() ?? string.Empty;
case UserActivity.Watching watching:
return watching.BeatmapInfo.ToString();
case UserActivity.InLobby lobby: case UserActivity.InLobby lobby:
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value; return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
} }

View File

@ -26,8 +26,8 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" /> <PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" /> <PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="System.IO.Packaging" Version="6.0.0" /> <PackageReference Include="System.IO.Packaging" Version="7.0.0" />
<PackageReference Include="DiscordRichPresence" Version="1.1.1.14" /> <PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />

View File

@ -7,9 +7,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" /> <PackageReference Include="BenchmarkDotNet" Version="0.13.4" />
<PackageReference Include="nunit" Version="3.13.3" /> <PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -1,32 +1,64 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
using osuTK.Input; using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public partial class TestSceneReplayPlayer : RateAdjustedBeatmapTestScene public partial class TestSceneReplayPlayer : RateAdjustedBeatmapTestScene
{ {
protected TestReplayPlayer Player; protected TestReplayPlayer Player = null!;
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("Initialise player", () => Player = CreatePlayer(new OsuRuleset()));
AddStep("Load player", () => LoadScreen(Player));
AddUntilStep("player loaded", () => Player.IsLoaded);
}
[Test] [Test]
public void TestPauseViaSpace() public void TestPauseViaSpace()
{ {
loadPlayerWithBeatmap();
double? lastTime = null;
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
AddStep("Pause playback with space", () => InputManager.Key(Key.Space));
AddAssert("player not exited", () => Player.IsCurrentScreen());
AddUntilStep("Time stopped progressing", () =>
{
double current = Player.GameplayClockContainer.CurrentTime;
bool changed = lastTime != current;
lastTime = current;
return !changed;
});
AddWaitStep("wait some", 10);
AddAssert("Time still stopped", () => lastTime == Player.GameplayClockContainer.CurrentTime);
}
[Test]
public void TestPauseViaSpaceWithSkip()
{
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
{
BeatmapInfo = { AudioLeadIn = 60000 }
});
AddUntilStep("wait for skip overlay", () => Player.ChildrenOfType<SkipOverlay>().First().IsButtonVisible);
AddStep("Skip with space", () => InputManager.Key(Key.Space));
AddAssert("Player not paused", () => !Player.DrawableRuleset.IsPaused.Value);
double? lastTime = null; double? lastTime = null;
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0); AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
@ -52,6 +84,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestPauseViaMiddleMouse() public void TestPauseViaMiddleMouse()
{ {
loadPlayerWithBeatmap();
double? lastTime = null; double? lastTime = null;
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0); AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
@ -77,6 +111,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestSeekBackwards() public void TestSeekBackwards()
{ {
loadPlayerWithBeatmap();
double? lastTime = null; double? lastTime = null;
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0); AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
@ -93,6 +129,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestSeekForwards() public void TestSeekForwards()
{ {
loadPlayerWithBeatmap();
double? lastTime = null; double? lastTime = null;
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0); AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
@ -106,12 +144,26 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("Jumped forwards", () => Player.GameplayClockContainer.CurrentTime - lastTime > 500); AddAssert("Jumped forwards", () => Player.GameplayClockContainer.CurrentTime - lastTime > 500);
} }
protected TestReplayPlayer CreatePlayer(Ruleset ruleset) private void loadPlayerWithBeatmap(IBeatmap? beatmap = null)
{ {
Beatmap.Value = CreateWorkingBeatmap(ruleset.RulesetInfo); AddStep("create player", () =>
{
CreatePlayer(new OsuRuleset(), beatmap);
});
AddStep("Load player", () => LoadScreen(Player));
AddUntilStep("player loaded", () => Player.IsLoaded);
}
protected void CreatePlayer(Ruleset ruleset, IBeatmap? beatmap = null)
{
Beatmap.Value = beatmap != null
? CreateWorkingBeatmap(beatmap)
: CreateWorkingBeatmap(ruleset.RulesetInfo);
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() }; SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
return new TestReplayPlayer(false); Player = new TestReplayPlayer(false);
} }
} }
} }

View File

@ -11,6 +11,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
@ -107,7 +109,8 @@ namespace osu.Game.Tests.Visual.Online
AddStep("set online status", () => status.Value = new UserStatusOnline()); AddStep("set online status", () => status.Value = new UserStatusOnline());
AddStep("idle", () => activity.Value = null); AddStep("idle", () => activity.Value = null);
AddStep("spectating", () => activity.Value = new UserActivity.Spectating()); AddStep("watching", () => activity.Value = new UserActivity.Watching(createScore(@"nats")));
AddStep("spectating", () => activity.Value = new UserActivity.Spectating(createScore(@"mrekk")));
AddStep("solo (osu!)", () => activity.Value = soloGameStatusForRuleset(0)); AddStep("solo (osu!)", () => activity.Value = soloGameStatusForRuleset(0));
AddStep("solo (osu!taiko)", () => activity.Value = soloGameStatusForRuleset(1)); AddStep("solo (osu!taiko)", () => activity.Value = soloGameStatusForRuleset(1));
AddStep("solo (osu!catch)", () => activity.Value = soloGameStatusForRuleset(2)); AddStep("solo (osu!catch)", () => activity.Value = soloGameStatusForRuleset(2));
@ -132,6 +135,14 @@ namespace osu.Game.Tests.Visual.Online
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(null, rulesetStore.GetRuleset(rulesetId)); private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(null, rulesetStore.GetRuleset(rulesetId));
private ScoreInfo createScore(string name) => new ScoreInfo(new TestBeatmap(Ruleset.Value).BeatmapInfo)
{
User = new APIUser
{
Username = name,
}
};
private partial class TestUserListPanel : UserListPanel private partial class TestUserListPanel : UserListPanel
{ {
public TestUserListPanel(APIUser user) public TestUserListPanel(APIUser user)

View File

@ -2,11 +2,11 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="DeepEqual" Version="4.2.1" /> <PackageReference Include="DeepEqual" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" /> <PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" Version="4.18.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -4,9 +4,9 @@
<StartupObject>osu.Game.Tournament.Tests.TournamentTestRunner</StartupObject> <StartupObject>osu.Game.Tournament.Tests.TournamentTestRunner</StartupObject>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -35,8 +35,8 @@ namespace osu.Game.Input.Bindings
// It is used to decide the order of precedence, with the earlier items having higher precedence. // It is used to decide the order of precedence, with the earlier items having higher precedence.
public override IEnumerable<IKeyBinding> DefaultKeyBindings => GlobalKeyBindings public override IEnumerable<IKeyBinding> DefaultKeyBindings => GlobalKeyBindings
.Concat(EditorKeyBindings) .Concat(EditorKeyBindings)
.Concat(ReplayKeyBindings)
.Concat(InGameKeyBindings) .Concat(InGameKeyBindings)
.Concat(ReplayKeyBindings)
.Concat(SongSelectKeyBindings) .Concat(SongSelectKeyBindings)
.Concat(AudioControlKeyBindings) .Concat(AudioControlKeyBindings)
// Overlay bindings may conflict with more local cases like the editor so they are checked last. // Overlay bindings may conflict with more local cases like the editor so they are checked last.

View File

@ -95,7 +95,7 @@ namespace osu.Game.Online.Chat
{ {
connector.ChannelJoined += ch => Schedule(() => joinChannel(ch)); connector.ChannelJoined += ch => Schedule(() => joinChannel(ch));
connector.ChannelParted += ch => Schedule(() => LeaveChannel(getChannel(ch))); connector.ChannelParted += ch => Schedule(() => leaveChannel(getChannel(ch), false));
connector.NewMessages += msgs => Schedule(() => addMessages(msgs)); connector.NewMessages += msgs => Schedule(() => addMessages(msgs));
@ -558,7 +558,9 @@ namespace osu.Game.Online.Chat
/// Leave the specified channel. Can be called from any thread. /// Leave the specified channel. Can be called from any thread.
/// </summary> /// </summary>
/// <param name="channel">The channel to leave.</param> /// <param name="channel">The channel to leave.</param>
public void LeaveChannel(Channel channel) => Schedule(() => public void LeaveChannel(Channel channel) => Schedule(() => leaveChannel(channel, true));
private void leaveChannel(Channel channel, bool sendLeaveRequest)
{ {
if (channel == null) return; if (channel == null) return;
@ -581,10 +583,11 @@ namespace osu.Game.Online.Chat
if (channel.Joined.Value) if (channel.Joined.Value)
{ {
api.Queue(new LeaveChannelRequest(channel)); if (sendLeaveRequest)
api.Queue(new LeaveChannelRequest(channel));
channel.Joined.Value = false; channel.Joined.Value = false;
} }
}); }
/// <summary> /// <summary>
/// Opens the most recently closed channel that has not already been reopened, /// Opens the most recently closed channel that has not already been reopened,

View File

@ -148,9 +148,9 @@ namespace osu.Game.Overlays.SkinEditor
component.Origin = Anchor.Centre; component.Origin = Anchor.Centre;
} }
protected override void Update() protected override void UpdateAfterChildren()
{ {
base.Update(); base.UpdateAfterChildren();
if (component.DrawSize != Vector2.Zero) if (component.DrawSize != Vector2.Zero)
{ {

View File

@ -121,8 +121,8 @@ namespace osu.Game.Screens.Play.HUD
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Child = new UprightAspectMaintainingContainer Child = new UprightAspectMaintainingContainer
{ {
Origin = Anchor.CentreRight, Origin = Anchor.Centre,
Anchor = Anchor.CentreRight, Anchor = Anchor.Centre,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Scaling = ScaleMode.Vertical, Scaling = ScaleMode.Vertical,
ScalingFactor = 0.5f, ScalingFactor = 0.5f,

View File

@ -15,6 +15,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
using osu.Game.Users;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
@ -24,6 +25,8 @@ namespace osu.Game.Screens.Play
private readonly bool replayIsFailedScore; private readonly bool replayIsFailedScore;
protected override UserActivity InitialActivity => new UserActivity.Watching(Score.ScoreInfo);
// Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108)
protected override bool CheckModsAllowFailure() protected override bool CheckModsAllowFailure()
{ {

View File

@ -7,6 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Online.Spectator; using osu.Game.Online.Spectator;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
@ -14,6 +15,8 @@ namespace osu.Game.Screens.Play
{ {
private readonly Score score; private readonly Score score;
protected override UserActivity InitialActivity => new UserActivity.Spectating(Score.ScoreInfo);
public SoloSpectatorPlayer(Score score, PlayerConfiguration configuration = null) public SoloSpectatorPlayer(Score score, PlayerConfiguration configuration = null)
: base(score, configuration) : base(score, configuration)
{ {

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
/// <summary> /// <summary>

View File

@ -1,11 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
@ -13,56 +10,49 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Users.Drawables namespace osu.Game.Users.Drawables
{ {
public partial class ClickableAvatar : Container public partial class ClickableAvatar : OsuClickableContainer
{ {
private const string default_tooltip_text = "view profile"; private const string default_tooltip_text = "view profile";
/// <summary> public override LocalisableString TooltipText
/// Whether to open the user's profile when clicked.
/// </summary>
public bool OpenOnClick
{ {
set => clickableArea.Enabled.Value = clickableArea.Action != null && value; get
{
if (!Enabled.Value)
return string.Empty;
return ShowUsernameTooltip ? (user?.Username ?? string.Empty) : default_tooltip_text;
}
set => throw new NotSupportedException();
} }
/// <summary> /// <summary>
/// By default, the tooltip will show "view profile" as avatars are usually displayed next to a username. /// By default, the tooltip will show "view profile" as avatars are usually displayed next to a username.
/// Setting this to <c>true</c> exposes the username via tooltip for special cases where this is not true. /// Setting this to <c>true</c> exposes the username via tooltip for special cases where this is not true.
/// </summary> /// </summary>
public bool ShowUsernameTooltip public bool ShowUsernameTooltip { get; set; }
{
set => clickableArea.TooltipText = value ? (user?.Username ?? string.Empty) : default_tooltip_text;
}
private readonly APIUser user; private readonly APIUser? user;
[Resolved(CanBeNull = true)] [Resolved]
private OsuGame game { get; set; } private OsuGame? game { get; set; }
private readonly ClickableArea clickableArea;
/// <summary> /// <summary>
/// A clickable avatar for the specified user, with UI sounds included. /// A clickable avatar for the specified user, with UI sounds included.
/// If <see cref="OpenOnClick"/> is <c>true</c>, clicking will open the user's profile.
/// </summary> /// </summary>
/// <param name="user">The user. A null value will get a placeholder avatar.</param> /// <param name="user">The user. A null value will get a placeholder avatar.</param>
public ClickableAvatar(APIUser user = null) public ClickableAvatar(APIUser? user = null)
{ {
this.user = user; this.user = user;
Add(clickableArea = new ClickableArea
{
RelativeSizeAxes = Axes.Both,
});
if (user?.Id != APIUser.SYSTEM_USER_ID) if (user?.Id != APIUser.SYSTEM_USER_ID)
clickableArea.Action = openProfile; Action = openProfile;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
LoadComponentAsync(new DrawableAvatar(user), clickableArea.Add); LoadComponentAsync(new DrawableAvatar(user), Add);
} }
private void openProfile() private void openProfile()
@ -71,23 +61,12 @@ namespace osu.Game.Users.Drawables
game?.ShowUser(user); game?.ShowUser(user);
} }
private partial class ClickableArea : OsuClickableContainer protected override bool OnClick(ClickEvent e)
{ {
private LocalisableString tooltip = default_tooltip_text; if (!Enabled.Value)
return false;
public override LocalisableString TooltipText return base.OnClick(e);
{
get => Enabled.Value ? tooltip : default;
set => tooltip = value;
}
protected override bool OnClick(ClickEvent e)
{
if (!Enabled.Value)
return false;
return base.OnClick(e);
}
} }
} }
} }

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
@ -13,9 +11,9 @@ namespace osu.Game.Users.Drawables
/// <summary> /// <summary>
/// An avatar which can update to a new user when needed. /// An avatar which can update to a new user when needed.
/// </summary> /// </summary>
public partial class UpdateableAvatar : ModelBackedDrawable<APIUser> public partial class UpdateableAvatar : ModelBackedDrawable<APIUser?>
{ {
public APIUser User public APIUser? User
{ {
get => Model; get => Model;
set => Model = value; set => Model = value;
@ -58,7 +56,7 @@ namespace osu.Game.Users.Drawables
/// <param name="isInteractive">If set to true, hover/click sounds will play and clicking the avatar will open the user's profile.</param> /// <param name="isInteractive">If set to true, hover/click sounds will play and clicking the avatar will open the user's profile.</param>
/// <param name="showUsernameTooltip">Whether to show the username rather than "view profile" on the tooltip. (note: this only applies if <paramref name="isInteractive"/> is also true)</param> /// <param name="showUsernameTooltip">Whether to show the username rather than "view profile" on the tooltip. (note: this only applies if <paramref name="isInteractive"/> is also true)</param>
/// <param name="showGuestOnNull">Whether to show a default guest representation on null user (as opposed to nothing).</param> /// <param name="showGuestOnNull">Whether to show a default guest representation on null user (as opposed to nothing).</param>
public UpdateableAvatar(APIUser user = null, bool isInteractive = true, bool showUsernameTooltip = false, bool showGuestOnNull = true) public UpdateableAvatar(APIUser? user = null, bool isInteractive = true, bool showUsernameTooltip = false, bool showGuestOnNull = true)
{ {
this.isInteractive = isInteractive; this.isInteractive = isInteractive;
this.showUsernameTooltip = showUsernameTooltip; this.showUsernameTooltip = showUsernameTooltip;
@ -67,7 +65,7 @@ namespace osu.Game.Users.Drawables
User = user; User = user;
} }
protected override Drawable CreateDrawable(APIUser user) protected override Drawable? CreateDrawable(APIUser? user)
{ {
if (user == null && !showGuestOnNull) if (user == null && !showGuestOnNull)
return null; return null;
@ -76,7 +74,6 @@ namespace osu.Game.Users.Drawables
{ {
return new ClickableAvatar(user) return new ClickableAvatar(user)
{ {
OpenOnClick = true,
ShowUsernameTooltip = showUsernameTooltip, ShowUsernameTooltip = showUsernameTooltip,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}; };

View File

@ -106,7 +106,7 @@ namespace osu.Game.Users
// Set status message based on activity (if we have one) and status is not offline // Set status message based on activity (if we have one) and status is not offline
if (activity != null && !(status is UserStatusOffline)) if (activity != null && !(status is UserStatusOffline))
{ {
statusMessage.Text = activity.Status; statusMessage.Text = activity.GetStatus();
statusIcon.FadeColour(activity.GetAppropriateColour(Colours), 500, Easing.OutQuint); statusIcon.FadeColour(activity.GetAppropriateColour(Colours), 500, Easing.OutQuint);
return; return;
} }

View File

@ -7,24 +7,26 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Scoring;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Users namespace osu.Game.Users
{ {
public abstract class UserActivity public abstract class UserActivity
{ {
public abstract string Status { get; } public abstract string GetStatus(bool hideIdentifiableInformation = false);
public virtual Color4 GetAppropriateColour(OsuColour colours) => colours.GreenDarker; public virtual Color4 GetAppropriateColour(OsuColour colours) => colours.GreenDarker;
public class Modding : UserActivity public class Modding : UserActivity
{ {
public override string Status => "Modding a map"; public override string GetStatus(bool hideIdentifiableInformation = false) => "Modding a map";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark; public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark;
} }
public class ChoosingBeatmap : UserActivity public class ChoosingBeatmap : UserActivity
{ {
public override string Status => "Choosing a beatmap"; public override string GetStatus(bool hideIdentifiableInformation = false) => "Choosing a beatmap";
} }
public abstract class InGame : UserActivity public abstract class InGame : UserActivity
@ -39,7 +41,7 @@ namespace osu.Game.Users
Ruleset = ruleset; Ruleset = ruleset;
} }
public override string Status => Ruleset.CreateInstance().PlayingVerb; public override string GetStatus(bool hideIdentifiableInformation = false) => Ruleset.CreateInstance().PlayingVerb;
} }
public class InMultiplayerGame : InGame public class InMultiplayerGame : InGame
@ -49,7 +51,7 @@ namespace osu.Game.Users
{ {
} }
public override string Status => $@"{base.Status} with others"; public override string GetStatus(bool hideIdentifiableInformation = false) => $@"{base.GetStatus(hideIdentifiableInformation)} with others";
} }
public class SpectatingMultiplayerGame : InGame public class SpectatingMultiplayerGame : InGame
@ -59,7 +61,7 @@ namespace osu.Game.Users
{ {
} }
public override string Status => $"Watching others {base.Status.ToLowerInvariant()}"; public override string GetStatus(bool hideIdentifiableInformation = false) => $"Watching others {base.GetStatus(hideIdentifiableInformation).ToLowerInvariant()}";
} }
public class InPlaylistGame : InGame public class InPlaylistGame : InGame
@ -87,22 +89,43 @@ namespace osu.Game.Users
BeatmapInfo = info; BeatmapInfo = info;
} }
public override string Status => @"Editing a beatmap"; public override string GetStatus(bool hideIdentifiableInformation = false) => @"Editing a beatmap";
} }
public class Spectating : UserActivity public class Watching : UserActivity
{ {
public override string Status => @"Spectating a game"; private readonly ScoreInfo score;
protected string Username => score.User.Username;
public BeatmapInfo BeatmapInfo => score.BeatmapInfo;
public Watching(ScoreInfo score)
{
this.score = score;
}
public override string GetStatus(bool hideIdentifiableInformation = false) => hideIdentifiableInformation ? @"Watching a replay" : $@"Watching {Username}'s replay";
}
public class Spectating : Watching
{
public override string GetStatus(bool hideIdentifiableInformation = false) => hideIdentifiableInformation ? @"Spectating a user" : $@"Spectating {Username}";
public Spectating(ScoreInfo score)
: base(score)
{
}
} }
public class SearchingForLobby : UserActivity public class SearchingForLobby : UserActivity
{ {
public override string Status => @"Looking for a lobby"; public override string GetStatus(bool hideIdentifiableInformation = false) => @"Looking for a lobby";
} }
public class InLobby : UserActivity public class InLobby : UserActivity
{ {
public override string Status => @"In a lobby"; public override string GetStatus(bool hideIdentifiableInformation = false) => @"In a lobby";
public readonly Room Room; public readonly Room Room;

View File

@ -18,29 +18,29 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="AutoMapper" Version="11.0.1" /> <PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="DiffPlex" Version="1.7.1" /> <PackageReference Include="DiffPlex" Version="1.7.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
<PackageReference Include="Humanizer" Version="2.14.1" /> <PackageReference Include="Humanizer" Version="2.14.1" />
<PackageReference Include="MessagePack" Version="2.4.35" /> <PackageReference Include="MessagePack" Version="2.4.59" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.10" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="6.0.10" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="6.0.10" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="7.0.2" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.10" /> <PackageReference Include="Microsoft.Data.Sqlite.Core" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" /> <PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="ppy.LocalisationAnalyser" Version="2022.809.0"> <PackageReference Include="ppy.LocalisationAnalyser" Version="2022.809.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Realm" Version="10.18.0" /> <PackageReference Include="Realm" Version="10.20.0" />
<PackageReference Include="ppy.osu.Framework" Version="2023.131.0" /> <PackageReference Include="ppy.osu.Framework" Version="2023.131.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.202.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2023.202.0" />
<PackageReference Include="Sentry" Version="3.23.1" /> <PackageReference Include="Sentry" Version="3.28.1" />
<PackageReference Include="SharpCompress" Version="0.32.2" /> <PackageReference Include="SharpCompress" Version="0.32.2" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.4" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="TagLibSharp" Version="2.3.0" /> <PackageReference Include="TagLibSharp" Version="2.3.0" />