From b11ae1c5714b43d33ebbec569faf6569d0304c25 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 20 Mar 2024 06:40:18 +0300 Subject: [PATCH] Organise code, still hook up to `RoomChanged` to update room privacy mode, and use `SkipIdenticalPresence` + scheduling to avoid potential rate-limits --- osu.Desktop/DiscordRichPresence.cs | 85 +++++++++++++++++++----------- 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index d8013aabfe7d..8e4af5c5b17a 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -2,16 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using System.Text; using DiscordRPC; using DiscordRPC.Message; using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Development; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Logging; +using osu.Framework.Threading; using osu.Game; using osu.Game.Configuration; using osu.Game.Extensions; @@ -53,8 +53,6 @@ internal partial class DiscordRichPresence : Component private readonly Bindable privacyMode = new Bindable(); - private int usersCurrentlyInLobby; - private readonly RichPresence presence = new RichPresence { Assets = new Assets { LargeImageKey = "osu_logo_lazer" }, @@ -72,7 +70,9 @@ private void load(OsuConfigManager config) client = new DiscordRpcClient(client_id) { - SkipIdenticalPresence = false // handles better on discord IPC loss, see updateStatus call in onReady. + // SkipIdenticalPresence allows us to fire SetPresence at any point and leave it to the underlying implementation + // to check whether a difference has actually occurred before sending a command to Discord (with a minor caveat that's handled in onReady). + SkipIdenticalPresence = true }; client.OnReady += onReady; @@ -95,10 +95,11 @@ private void load(OsuConfigManager config) activity.BindTo(u.NewValue.Activity); }, true); - ruleset.BindValueChanged(_ => updateStatus()); - status.BindValueChanged(_ => updateStatus()); - activity.BindValueChanged(_ => updateStatus()); - privacyMode.BindValueChanged(_ => updateStatus()); + ruleset.BindValueChanged(_ => updatePresence()); + status.BindValueChanged(_ => updatePresence()); + activity.BindValueChanged(_ => updatePresence()); + privacyMode.BindValueChanged(_ => updatePresence()); + multiplayerClient.RoomUpdated += onRoomUpdated; client.Initialize(); } @@ -106,24 +107,44 @@ private void load(OsuConfigManager config) private void onReady(object _, ReadyMessage __) { Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); - Schedule(updateStatus); + + // when RPC is lost and reconnected, we have to clear presence state for updatePresence to work (see DiscordRpcClient.SkipIdenticalPresence). + if (client.CurrentPresence != null) + client.SetPresence(null); + + updatePresence(); } - private void updateStatus() - { - Debug.Assert(ThreadSafety.IsUpdateThread); + private void onRoomUpdated() => updatePresence(); - if (!client.IsInitialized) - return; + private ScheduledDelegate? presenceUpdateDelegate; - if (status.Value == UserStatus.Offline || privacyMode.Value == DiscordRichPresenceMode.Off) + private void updatePresence() + { + presenceUpdateDelegate?.Cancel(); + presenceUpdateDelegate = Scheduler.AddDelayed(() => { - client.ClearPresence(); - return; - } + if (!client.IsInitialized) + return; + + if (status.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 || status.Value == UserStatus.DoNotDisturb; + updatePresenceStatus(hideIdentifiableInformation); + updatePresenceParty(hideIdentifiableInformation); + updatePresenceAssets(); + client.SetPresence(presence); + }, 200); + } + + private void updatePresenceStatus(bool hideIdentifiableInformation) + { if (activity.Value != null) { presence.State = truncate(activity.Value.GetStatus(hideIdentifiableInformation)); @@ -150,14 +171,14 @@ private void updateStatus() presence.State = "Idle"; presence.Details = string.Empty; } + } + private void updatePresenceParty(bool hideIdentifiableInformation) + { if (!hideIdentifiableInformation && multiplayerClient.Room != null) { MultiplayerRoom room = multiplayerClient.Room; - if (room.Users.Count == usersCurrentlyInLobby) - return; - presence.Party = new Party { Privacy = string.IsNullOrEmpty(room.Settings.Password) ? Party.PrivacySetting.Public : Party.PrivacySetting.Private, @@ -175,17 +196,16 @@ private void updateStatus() }; presence.Secrets.JoinSecret = JsonConvert.SerializeObject(roomSecret); - usersCurrentlyInLobby = room.Users.Count; } else { presence.Party = null; presence.Secrets.JoinSecret = null; - usersCurrentlyInLobby = 0; } + } - Logger.Log($"Updating Discord RPC presence with activity status: {presence.State}, details: {presence.Details}", LoggingTarget.Network, LogLevel.Debug); - + private void updatePresenceAssets() + { // update user information if (privacyMode.Value == DiscordRichPresenceMode.Limited) presence.Assets.LargeImageText = string.Empty; @@ -200,17 +220,15 @@ private void updateStatus() // update ruleset presence.Assets.SmallImageKey = ruleset.Value.IsLegacyRuleset() ? $"mode_{ruleset.Value.OnlineID}" : "mode_custom"; presence.Assets.SmallImageText = ruleset.Value.Name; - - client.SetPresence(presence); } - private void onJoin(object sender, JoinMessage args) + private void onJoin(object sender, JoinMessage args) => Scheduler.AddOnce(() => { game.Window?.Raise(); if (!api.IsLoggedIn) { - Schedule(() => login?.Show()); + login?.Show(); return; } @@ -231,7 +249,7 @@ private void onJoin(object sender, JoinMessage args) }); request.Failure += _ => Logger.Log($"Could not join multiplayer room, room could not be found (room ID: {roomId}).", LoggingTarget.Network, LogLevel.Important); api.Queue(request); - } + }); private static readonly int ellipsis_length = Encoding.UTF8.GetByteCount(new[] { '…' }); @@ -294,6 +312,9 @@ private static bool tryParseRoomSecret(string secretJson, out long roomId, out s protected override void Dispose(bool isDisposing) { + if (multiplayerClient.IsNotNull()) + multiplayerClient.RoomUpdated -= onRoomUpdated; + client.Dispose(); base.Dispose(isDisposing); }