Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Status and Activity bindables from APIUser #31513

Merged
merged 12 commits into from
Jan 17, 2025
Merged
41 changes: 16 additions & 25 deletions osu.Desktop/DiscordRichPresence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,9 @@ internal partial class DiscordRichPresence : Component
[Resolved]
private LocalUserStatisticsProvider statisticsProvider { get; set; } = null!;

[Resolved]
private OsuConfigManager config { get; set; } = null!;

private readonly IBindable<UserStatus?> status = new Bindable<UserStatus?>();
private readonly IBindable<UserActivity> activity = new Bindable<UserActivity>();
private readonly Bindable<DiscordRichPresenceMode> privacyMode = new Bindable<DiscordRichPresenceMode>();
private IBindable<DiscordRichPresenceMode> privacyMode = null!;
private IBindable<UserStatus> userStatus = null!;
private IBindable<UserActivity?> userActivity = null!;

private readonly RichPresence presence = new RichPresence
{
Expand All @@ -71,8 +68,12 @@ internal partial class DiscordRichPresence : Component
private IBindable<APIUser>? user;

[BackgroundDependencyLoader]
private void load()
private void load(OsuConfigManager config, SessionStatics session)
{
privacyMode = config.GetBindable<DiscordRichPresenceMode>(OsuSetting.DiscordRichPresence);
userStatus = config.GetBindable<UserStatus>(OsuSetting.UserOnlineStatus);
userActivity = session.GetBindable<UserActivity?>(Static.UserOnlineActivity);

client = new DiscordRpcClient(client_id)
{
// SkipIdenticalPresence allows us to fire SetPresence at any point and leave it to the underlying implementation
Expand Down Expand Up @@ -105,21 +106,11 @@ protected override void LoadComplete()
{
base.LoadComplete();

config.BindWith(OsuSetting.DiscordRichPresence, privacyMode);

user = api.LocalUser.GetBoundCopy();
user.BindValueChanged(u =>
{
status.UnbindBindings();
status.BindTo(u.NewValue.Status);

activity.UnbindBindings();
activity.BindTo(u.NewValue.Activity);
}, true);

ruleset.BindValueChanged(_ => schedulePresenceUpdate());
status.BindValueChanged(_ => schedulePresenceUpdate());
activity.BindValueChanged(_ => schedulePresenceUpdate());
userStatus.BindValueChanged(_ => schedulePresenceUpdate());
userActivity.BindValueChanged(_ => schedulePresenceUpdate());
privacyMode.BindValueChanged(_ => schedulePresenceUpdate());

multiplayerClient.RoomUpdated += onRoomUpdated;
Expand Down Expand Up @@ -151,13 +142,13 @@ private void schedulePresenceUpdate()
if (!client.IsInitialized)
return;

if (status.Value == UserStatus.Offline || privacyMode.Value == DiscordRichPresenceMode.Off)
if (!api.IsLoggedIn || userStatus.Value == UserStatus.Offline || privacyMode.Value == DiscordRichPresenceMode.Off)
{
client.ClearPresence();
return;
}

bool hideIdentifiableInformation = privacyMode.Value == DiscordRichPresenceMode.Limited || status.Value == UserStatus.DoNotDisturb;
bool hideIdentifiableInformation = privacyMode.Value == DiscordRichPresenceMode.Limited || userStatus.Value == UserStatus.DoNotDisturb;

updatePresence(hideIdentifiableInformation);
client.SetPresence(presence);
Expand All @@ -170,12 +161,12 @@ private void updatePresence(bool hideIdentifiableInformation)
return;

// user activity
if (activity.Value != null)
if (userActivity.Value != null)
{
presence.State = clampLength(activity.Value.GetStatus(hideIdentifiableInformation));
presence.Details = clampLength(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty);
presence.State = clampLength(userActivity.Value.GetStatus(hideIdentifiableInformation));
presence.Details = clampLength(userActivity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty);

if (activity.Value.GetBeatmapID(hideIdentifiableInformation) is int beatmapId && beatmapId > 0)
if (userActivity.Value.GetBeatmapID(hideIdentifiableInformation) is int beatmapId && beatmapId > 0)
{
presence.Buttons = new[]
{
Expand Down
27 changes: 14 additions & 13 deletions osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ public partial class TestSceneLoginOverlay : OsuManualInputManagerTestScene
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;

private LoginOverlay loginOverlay = null!;

[Resolved]
private OsuConfigManager configManager { get; set; } = null!;
private OsuConfigManager localConfig = null!;

[Cached(typeof(LocalUserStatisticsProvider))]
private readonly TestSceneUserPanel.TestUserStatisticsProvider statisticsProvider = new TestSceneUserPanel.TestUserStatisticsProvider();

[BackgroundDependencyLoader]
private void load()
{
Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage));

Child = loginOverlay = new LoginOverlay
{
Anchor = Anchor.Centre,
Expand All @@ -49,6 +49,7 @@ private void load()
[SetUpSteps]
public void SetUpSteps()
{
AddStep("reset online state", () => localConfig.SetValue(OsuSetting.UserOnlineStatus, UserStatus.Online));
AddStep("show login overlay", () => loginOverlay.Show());
}

Expand Down Expand Up @@ -89,7 +90,7 @@ public void TestLoginSuccess()
AddStep("clear handler", () => dummyAPI.HandleRequest = null);

assertDropdownState(UserAction.Online);
AddStep("change user state", () => dummyAPI.LocalUser.Value.Status.Value = UserStatus.DoNotDisturb);
AddStep("change user state", () => localConfig.SetValue(OsuSetting.UserOnlineStatus, UserStatus.DoNotDisturb));
assertDropdownState(UserAction.DoNotDisturb);
}

Expand Down Expand Up @@ -188,31 +189,31 @@ public void TestClickingOnFlagClosesOverlay()
public void TestUncheckingRememberUsernameClearsIt()
{
AddStep("logout", () => API.Logout());
AddStep("set username", () => configManager.SetValue(OsuSetting.Username, "test_user"));
AddStep("set remember password", () => configManager.SetValue(OsuSetting.SavePassword, true));
AddStep("set username", () => localConfig.SetValue(OsuSetting.Username, "test_user"));
AddStep("set remember password", () => localConfig.SetValue(OsuSetting.SavePassword, true));
AddStep("uncheck remember username", () =>
{
InputManager.MoveMouseTo(loginOverlay.ChildrenOfType<SettingsCheckbox>().First());
InputManager.Click(MouseButton.Left);
});
AddAssert("remember username off", () => configManager.Get<bool>(OsuSetting.SaveUsername), () => Is.False);
AddAssert("remember password off", () => configManager.Get<bool>(OsuSetting.SavePassword), () => Is.False);
AddAssert("username cleared", () => configManager.Get<string>(OsuSetting.Username), () => Is.Empty);
AddAssert("remember username off", () => localConfig.Get<bool>(OsuSetting.SaveUsername), () => Is.False);
AddAssert("remember password off", () => localConfig.Get<bool>(OsuSetting.SavePassword), () => Is.False);
AddAssert("username cleared", () => localConfig.Get<string>(OsuSetting.Username), () => Is.Empty);
}

[Test]
public void TestUncheckingRememberPasswordClearsToken()
{
AddStep("logout", () => API.Logout());
AddStep("set token", () => configManager.SetValue(OsuSetting.Token, "test_token"));
AddStep("set remember password", () => configManager.SetValue(OsuSetting.SavePassword, true));
AddStep("set token", () => localConfig.SetValue(OsuSetting.Token, "test_token"));
AddStep("set remember password", () => localConfig.SetValue(OsuSetting.SavePassword, true));
AddStep("uncheck remember token", () =>
{
InputManager.MoveMouseTo(loginOverlay.ChildrenOfType<SettingsCheckbox>().Last());
InputManager.Click(MouseButton.Left);
});
AddAssert("remember password off", () => configManager.Get<bool>(OsuSetting.SavePassword), () => Is.False);
AddAssert("token cleared", () => configManager.Get<string>(OsuSetting.Token), () => Is.Empty);
AddAssert("remember password off", () => localConfig.Get<bool>(OsuSetting.SavePassword), () => Is.False);
AddAssert("token cleared", () => localConfig.Get<string>(OsuSetting.Token), () => Is.Empty);
}
}
}
20 changes: 13 additions & 7 deletions osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Configuration;
using osu.Game.Online.Chat;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Mods;
Expand All @@ -23,17 +23,23 @@ public partial class TestSceneNowPlayingCommand : OsuTestScene
[Cached(typeof(IChannelPostTarget))]
private PostTarget postTarget { get; set; }

private DummyAPIAccess api => (DummyAPIAccess)API;
private SessionStatics session = null!;

public TestSceneNowPlayingCommand()
{
Add(postTarget = new PostTarget());
}

[BackgroundDependencyLoader]
private void load()
{
Dependencies.Cache(session = new SessionStatics());
}

[Test]
public void TestGenericActivity()
{
AddStep("Set activity", () => api.Activity.Value = new UserActivity.InLobby(new Room()));
AddStep("Set activity", () => session.SetValue<UserActivity>(Static.UserOnlineActivity, new UserActivity.InLobby(new Room())));

AddStep("Run command", () => Add(new NowPlayingCommand(new Channel())));

Expand All @@ -43,7 +49,7 @@ public void TestGenericActivity()
[Test]
public void TestEditActivity()
{
AddStep("Set activity", () => api.Activity.Value = new UserActivity.EditingBeatmap(new BeatmapInfo()));
AddStep("Set activity", () => session.SetValue<UserActivity>(Static.UserOnlineActivity, new UserActivity.EditingBeatmap(new BeatmapInfo())));

AddStep("Run command", () => Add(new NowPlayingCommand(new Channel())));

Expand All @@ -53,7 +59,7 @@ public void TestEditActivity()
[Test]
public void TestPlayActivity()
{
AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo));
AddStep("Set activity", () => session.SetValue<UserActivity>(Static.UserOnlineActivity, new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo)));

AddStep("Run command", () => Add(new NowPlayingCommand(new Channel())));

Expand All @@ -64,7 +70,7 @@ public void TestPlayActivity()
[TestCase(false)]
public void TestLinkPresence(bool hasOnlineId)
{
AddStep("Set activity", () => api.Activity.Value = new UserActivity.InLobby(new Room()));
AddStep("Set activity", () => session.SetValue<UserActivity>(Static.UserOnlineActivity, new UserActivity.InLobby(new Room())));

AddStep("Set beatmap", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null)
{
Expand All @@ -82,7 +88,7 @@ public void TestLinkPresence(bool hasOnlineId)
[Test]
public void TestModPresence()
{
AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo));
AddStep("Set activity", () => session.SetValue<UserActivity>(Static.UserOnlineActivity, new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo)));

AddStep("Add Hidden mod", () => SelectedMods.Value = new[] { Ruleset.Value.CreateInstance().CreateMod<ModHidden>() });

Expand Down
5 changes: 1 addition & 4 deletions osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ private Drawable generateUser(string username, int id, CountryCode countryCode,
CountryCode = countryCode,
CoverUrl = cover,
Colour = color ?? "000000",
Status =
{
Value = UserStatus.Online
},
IsOnline = true
Copy link
Contributor Author

@smoogipoo smoogipoo Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in 20108e3, this is a hack that I expect to go away soon because obviously this has no way to convey user activity & dnd state.

};

return new ClickableAvatar(user, showPanel)
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public void SetUp() => Schedule(() =>
Id = 3103765,
CountryCode = CountryCode.JP,
CoverUrl = @"https://assets.ppy.sh/user-cover-presets/1/df28696b58541a9e67f6755918951d542d93bdf1da41720fcca2fd2c1ea8cf51.jpeg",
Status = { Value = UserStatus.Online }
IsOnline = true
}) { Width = 300 },
boundPanel1 = new UserGridPanel(new APIUser
{
Expand Down
7 changes: 6 additions & 1 deletion osu.Game/Configuration/OsuConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ protected override void InitialiseDefaults()
SetDefault(OsuSetting.LastProcessedMetadataId, -1);

SetDefault(OsuSetting.ComboColourNormalisationAmount, 0.2f, 0f, 1f, 0.01f);
SetDefault<UserStatus?>(OsuSetting.UserOnlineStatus, null);
SetDefault(OsuSetting.UserOnlineStatus, UserStatus.Online);
Copy link
Contributor Author

@smoogipoo smoogipoo Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this will throw a non-fatal runtime error if you have the previous null default value in the config:

UserOnlineStatus =

However, the value is set to Online as soon as the user logs in, and never set back to the default value after that.

This is not easy to handle because we don't have a good way to change values from nullable to non-nullable right now (if ever?), so I left it as is and hope this is okay. Adding a migration doesn't help because those are processed after all these defaults are applied.


SetDefault(OsuSetting.EditorTimelineShowTimingChanges, true);
SetDefault(OsuSetting.EditorTimelineShowBreaks, true);
Expand Down Expand Up @@ -443,7 +443,12 @@ public enum OsuSetting
EditorShowSpeedChanges,
TouchDisableGameplayTaps,
ModSelectTextSearchStartsActive,

/// <summary>
/// The status for the current user to broadcast to other players.
/// </summary>
UserOnlineStatus,

MultiplayerRoomFilter,
HideCountryFlags,
EditorTimelineShowTimingChanges,
Expand Down
4 changes: 4 additions & 0 deletions osu.Game/Configuration/SessionStatics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Scoring;
using osu.Game.Users;

namespace osu.Game.Configuration
{
Expand All @@ -30,6 +31,7 @@ protected override void InitialiseDefaults()
SetDefault(Static.TouchInputActive, RuntimeInfo.IsMobile);
SetDefault<ScoreInfo>(Static.LastLocalUserScore, null);
SetDefault<ScoreInfo>(Static.LastAppliedOffsetScore, null);
SetDefault<UserActivity>(Static.UserOnlineActivity, null);
}

/// <summary>
Expand Down Expand Up @@ -92,5 +94,7 @@ public enum Static
/// This is reset when a new challenge is up.
/// </summary>
DailyChallengeIntroPlayed,

UserOnlineActivity,
}
}
27 changes: 4 additions & 23 deletions osu.Game/Online/API/APIAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public partial class APIAccess : Component, IAPIProvider

public IBindable<APIUser> LocalUser => localUser;
public IBindableList<APIRelation> Friends => friends;
public IBindable<UserActivity> Activity => activity;

public INotificationsClient NotificationsClient { get; }

Expand All @@ -70,15 +69,10 @@ public partial class APIAccess : Component, IAPIProvider

private BindableList<APIRelation> friends { get; } = new BindableList<APIRelation>();

private Bindable<UserActivity> activity { get; } = new Bindable<UserActivity>();

private Bindable<UserStatus?> configStatus { get; } = new Bindable<UserStatus?>();
private Bindable<UserStatus?> localUserStatus { get; } = new Bindable<UserStatus?>();

protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password));

private readonly Bindable<UserStatus> configStatus = new Bindable<UserStatus>();
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();

private readonly Logger log;

public APIAccess(OsuGameBase game, OsuConfigManager config, EndpointConfiguration endpointConfiguration, string versionHash)
Expand Down Expand Up @@ -121,17 +115,6 @@ public APIAccess(OsuGameBase game, OsuConfigManager config, EndpointConfiguratio
state.Value = APIState.Connecting;
}

localUser.BindValueChanged(u =>
{
u.OldValue?.Activity.UnbindFrom(activity);
u.NewValue.Activity.BindTo(activity);

u.OldValue?.Status.UnbindFrom(localUserStatus);
u.NewValue.Status.BindTo(localUserStatus);
}, true);

localUserStatus.BindTo(configStatus);

var thread = new Thread(run)
{
Name = "APIAccess",
Expand Down Expand Up @@ -342,10 +325,7 @@ private void attemptConnect()
{
Debug.Assert(ThreadSafety.IsUpdateThread);

me.Status.Value = configStatus.Value ?? UserStatus.Online;

localUser.Value = me;

state.Value = me.SessionVerified ? APIState.Online : APIState.RequiresSecondFactorAuth;
failureCount = 0;
};
Expand Down Expand Up @@ -381,8 +361,7 @@ private void setPlaceholderLocalUser()

localUser.Value = new APIUser
{
Username = ProvidedUsername,
Status = { Value = configStatus.Value ?? UserStatus.Online }
Username = ProvidedUsername
};
}

Expand Down Expand Up @@ -608,6 +587,8 @@ public void Logout()
password = null;
SecondFactorCode = null;
authentication.Clear();

// Reset the status to be broadcast on the next login, in case multiple players share the same system.
configStatus.Value = UserStatus.Online;

// Scheduled prior to state change such that the state changed event is invoked with the correct user and their friends present
Expand Down
Loading
Loading