diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 6c7e7d393f..7dd9250ab6 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -51,12 +51,9 @@ namespace osu.Desktop [Resolved] private LocalUserStatisticsProvider statisticsProvider { get; set; } = null!; - [Resolved] - private OsuConfigManager config { get; set; } = null!; - - private readonly IBindable status = new Bindable(); - private readonly IBindable activity = new Bindable(); - private readonly Bindable privacyMode = new Bindable(); + private IBindable privacyMode = null!; + private IBindable userStatus = null!; + private IBindable userActivity = null!; private readonly RichPresence presence = new RichPresence { @@ -71,8 +68,12 @@ namespace osu.Desktop private IBindable? user; [BackgroundDependencyLoader] - private void load() + private void load(OsuConfigManager config, SessionStatics session) { + privacyMode = config.GetBindable(OsuSetting.DiscordRichPresence); + userStatus = config.GetBindable(OsuSetting.UserOnlineStatus); + userActivity = session.GetBindable(Static.UserOnlineActivity); + client = new DiscordRpcClient(client_id) { // SkipIdenticalPresence allows us to fire SetPresence at any point and leave it to the underlying implementation @@ -105,15 +106,11 @@ namespace osu.Desktop { base.LoadComplete(); - config.BindWith(OsuSetting.DiscordRichPresence, privacyMode); - user = api.LocalUser.GetBoundCopy(); - status.BindTo(api.Status); - activity.BindTo(api.Activity); ruleset.BindValueChanged(_ => schedulePresenceUpdate()); - status.BindValueChanged(_ => schedulePresenceUpdate()); - activity.BindValueChanged(_ => schedulePresenceUpdate()); + userStatus.BindValueChanged(_ => schedulePresenceUpdate()); + userActivity.BindValueChanged(_ => schedulePresenceUpdate()); privacyMode.BindValueChanged(_ => schedulePresenceUpdate()); multiplayerClient.RoomUpdated += onRoomUpdated; @@ -145,13 +142,13 @@ namespace osu.Desktop if (!client.IsInitialized) return; - if (!api.IsLoggedIn || 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); @@ -164,12 +161,12 @@ namespace osu.Desktop 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[] { diff --git a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs index 1e9b0317fb..428554f761 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs @@ -8,7 +8,7 @@ using osu.Framework.Allocation; 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; @@ -23,17 +23,23 @@ namespace osu.Game.Tests.Visual.Online [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(Static.UserOnlineActivity, new UserActivity.InLobby(new Room()))); AddStep("Run command", () => Add(new NowPlayingCommand(new Channel()))); @@ -43,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestEditActivity() { - AddStep("Set activity", () => api.Activity.Value = new UserActivity.EditingBeatmap(new BeatmapInfo())); + AddStep("Set activity", () => session.SetValue(Static.UserOnlineActivity, new UserActivity.EditingBeatmap(new BeatmapInfo()))); AddStep("Run command", () => Add(new NowPlayingCommand(new Channel()))); @@ -53,7 +59,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestPlayActivity() { - AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo)); + AddStep("Set activity", () => session.SetValue(Static.UserOnlineActivity, new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo))); AddStep("Run command", () => Add(new NowPlayingCommand(new Channel()))); @@ -64,7 +70,7 @@ namespace osu.Game.Tests.Visual.Online [TestCase(false)] public void TestLinkPresence(bool hasOnlineId) { - AddStep("Set activity", () => api.Activity.Value = new UserActivity.InLobby(new Room())); + AddStep("Set activity", () => session.SetValue(Static.UserOnlineActivity, new UserActivity.InLobby(new Room()))); AddStep("Set beatmap", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null) { @@ -82,7 +88,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestModPresence() { - AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo)); + AddStep("Set activity", () => session.SetValue(Static.UserOnlineActivity, new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo))); AddStep("Add Hidden mod", () => SelectedMods.Value = new[] { Ruleset.Value.CreateInstance().CreateMod() }); diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index c55a597c32..bdfb0217ad 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -10,6 +10,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Scoring; +using osu.Game.Users; namespace osu.Game.Configuration { @@ -30,6 +31,7 @@ namespace osu.Game.Configuration SetDefault(Static.TouchInputActive, RuntimeInfo.IsMobile); SetDefault(Static.LastLocalUserScore, null); SetDefault(Static.LastAppliedOffsetScore, null); + SetDefault(Static.UserOnlineActivity, null); } /// @@ -92,5 +94,7 @@ namespace osu.Game.Configuration /// This is reset when a new challenge is up. /// DailyChallengeIntroPlayed, + + UserOnlineActivity, } } diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index dcb8a193bc..f7fbacf76c 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -60,8 +60,6 @@ namespace osu.Game.Online.API public IBindable LocalUser => localUser; public IBindableList Friends => friends; - public IBindable Status => configStatus; - public IBindable Activity => activity; public INotificationsClient NotificationsClient { get; } @@ -71,8 +69,6 @@ namespace osu.Game.Online.API private BindableList friends { get; } = new BindableList(); - private Bindable activity { get; } = new Bindable(); - protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password)); private readonly Bindable configStatus = new Bindable(); diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index 3fef2b59cf..48c08afb8c 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -12,7 +12,6 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Chat; using osu.Game.Online.Notifications.WebSocket; using osu.Game.Tests; -using osu.Game.Users; namespace osu.Game.Online.API { @@ -28,10 +27,6 @@ namespace osu.Game.Online.API public BindableList Friends { get; } = new BindableList(); - public Bindable Status { get; } = new Bindable(UserStatus.Online); - - public Bindable Activity { get; } = new Bindable(); - public DummyNotificationsClient NotificationsClient { get; } = new DummyNotificationsClient(); INotificationsClient IAPIProvider.NotificationsClient => NotificationsClient; @@ -197,8 +192,6 @@ namespace osu.Game.Online.API IBindable IAPIProvider.LocalUser => LocalUser; IBindableList IAPIProvider.Friends => Friends; - IBindable IAPIProvider.Status => Status; - IBindable IAPIProvider.Activity => Activity; /// /// Skip 2FA requirement for next login. diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index 9ac7343885..3b6763d736 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -8,7 +8,6 @@ using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Chat; using osu.Game.Online.Notifications.WebSocket; -using osu.Game.Users; namespace osu.Game.Online.API { @@ -24,16 +23,6 @@ namespace osu.Game.Online.API /// IBindableList Friends { get; } - /// - /// The status for the current user that's broadcast to other players. - /// - IBindable Status { get; } - - /// - /// The activity for the current user that's broadcast to other players. - /// - IBindable Activity { get; } - /// /// The language supplied by this provider to API requests. /// diff --git a/osu.Game/Online/Chat/NowPlayingCommand.cs b/osu.Game/Online/Chat/NowPlayingCommand.cs index 0e6f6f0bf6..db44017a1b 100644 --- a/osu.Game/Online/Chat/NowPlayingCommand.cs +++ b/osu.Game/Online/Chat/NowPlayingCommand.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -33,6 +34,7 @@ namespace osu.Game.Online.Chat private IBindable currentRuleset { get; set; } = null!; private readonly Channel? target; + private IBindable userActivity = null!; /// /// Creates a new to post the currently-playing beatmap to a parenting . @@ -43,6 +45,12 @@ namespace osu.Game.Online.Chat this.target = target; } + [BackgroundDependencyLoader] + private void load(SessionStatics session) + { + userActivity = session.GetBindable(Static.UserOnlineActivity); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -52,7 +60,7 @@ namespace osu.Game.Online.Chat int beatmapOnlineID; string beatmapDisplayTitle; - switch (api.Activity.Value) + switch (userActivity.Value) { case UserActivity.InGame game: verb = "playing"; @@ -92,14 +100,14 @@ namespace osu.Game.Online.Chat string getRulesetPart() { - if (api.Activity.Value is not UserActivity.InGame) return string.Empty; + if (userActivity.Value is not UserActivity.InGame) return string.Empty; return $"<{currentRuleset.Value.Name}>"; } string getModPart() { - if (api.Activity.Value is not UserActivity.InGame) return string.Empty; + if (userActivity.Value is not UserActivity.InGame) return string.Empty; if (selectedMods.Value.Count == 0) { diff --git a/osu.Game/Online/Metadata/OnlineMetadataClient.cs b/osu.Game/Online/Metadata/OnlineMetadataClient.cs index 101307636a..01d7a564fa 100644 --- a/osu.Game/Online/Metadata/OnlineMetadataClient.cs +++ b/osu.Game/Online/Metadata/OnlineMetadataClient.cs @@ -34,6 +34,9 @@ namespace osu.Game.Online.Metadata private readonly string endpoint; + [Resolved] + private IAPIProvider api { get; set; } = null!; + private IHubClientConnector? connector; private Bindable lastQueueId = null!; private IBindable localUser = null!; @@ -48,7 +51,7 @@ namespace osu.Game.Online.Metadata } [BackgroundDependencyLoader] - private void load(IAPIProvider api, OsuConfigManager config) + private void load(OsuConfigManager config, SessionStatics session) { // Importantly, we are intentionally not using MessagePack here to correctly support derived class serialization. // More information on the limitations / reasoning can be found in osu-server-spectator's initialisation code. @@ -72,11 +75,10 @@ namespace osu.Game.Online.Metadata IsConnected.BindValueChanged(isConnectedChanged, true); } - lastQueueId = config.GetBindable(OsuSetting.LastProcessedMetadataId); - localUser = api.LocalUser.GetBoundCopy(); - userStatus = api.Status.GetBoundCopy(); - userActivity = api.Activity.GetBoundCopy()!; + lastQueueId = config.GetBindable(OsuSetting.LastProcessedMetadataId); + userStatus = config.GetBindable(OsuSetting.UserOnlineStatus); + userActivity = session.GetBindable(Static.UserOnlineActivity); } protected override void LoadComplete() @@ -240,7 +242,14 @@ namespace osu.Game.Online.Metadata throw new OperationCanceledException(); // must be scheduled before any remote calls to avoid mis-ordering. - Schedule(() => userStates.Clear()); + Schedule(() => + { + bool hadLocalUserState = userStates.TryGetValue(api.LocalUser.Value.OnlineID, out var presence); + userStates.Clear(); + if (hadLocalUserState) + userStates[api.LocalUser.Value.OnlineID] = presence; + }); + Debug.Assert(connection != null); await connection.InvokeAsync(nameof(IMetadataServer.EndWatchingUserPresence)).ConfigureAwait(false); Logger.Log($@"{nameof(OnlineMetadataClient)} stopped watching user presence", LoggingTarget.Network); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 859991496d..40d13ae0b7 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -211,6 +211,8 @@ namespace osu.Game private Bindable uiScale; + private Bindable configUserActivity; + private Bindable configSkin; private readonly string[] args; @@ -391,6 +393,8 @@ namespace osu.Game Ruleset.ValueChanged += r => configRuleset.Value = r.NewValue.ShortName; + configUserActivity = SessionStatics.GetBindable(Static.UserOnlineActivity); + configSkin = LocalConfig.GetBindable(OsuSetting.Skin); // Transfer skin from config to realm instance once on startup. @@ -1588,14 +1592,14 @@ namespace osu.Game { backButtonVisibility.UnbindFrom(currentOsuScreen.BackButtonVisibility); OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode); - API.Activity.UnbindFrom(currentOsuScreen.Activity); + configUserActivity.UnbindFrom(currentOsuScreen.Activity); } if (newScreen is IOsuScreen newOsuScreen) { backButtonVisibility.BindTo(newOsuScreen.BackButtonVisibility); OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); - API.Activity.BindTo(newOsuScreen.Activity); + configUserActivity.BindTo(newOsuScreen.Activity); GlobalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = newOsuScreen.HideMenuCursorOnNonMouseInput; diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 9e474ed0c6..69bde877c7 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -74,7 +74,7 @@ namespace osu.Game.Screens /// /// The current for this screen. /// - IBindable Activity { get; } + Bindable Activity { get; } /// /// The amount of parallax to be applied while this screen is displayed. diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index ab66241a77..f5325b3928 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens /// protected readonly Bindable Activity = new Bindable(); - IBindable IOsuScreen.Activity => Activity; + Bindable IOsuScreen.Activity => Activity; /// /// Whether to disallow changes to game-wise Beatmap/Ruleset bindables for this screen (and all children).