From 65d664425bdfeca60343863d1ef441a7ade7a389 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 12 Apr 2019 23:13:13 +0200 Subject: [PATCH 01/20] Added DiscordRichPresence to osu.Desktop packages --- osu.Desktop/osu.Desktop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 66db439c82..7a19e73be6 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -22,6 +22,7 @@ + From 533afaa770be793be46f9948b7168e469f33431f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 13 Apr 2019 13:06:53 +0200 Subject: [PATCH 02/20] Added DiscordRichPresenceClient --- osu.Desktop/DiscordRichPresenceClient.cs | 106 +++++++++++++++++++++++ osu.Desktop/OsuGameDesktop.cs | 2 + 2 files changed, 108 insertions(+) create mode 100644 osu.Desktop/DiscordRichPresenceClient.cs diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs new file mode 100644 index 0000000000..03b2884ab6 --- /dev/null +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -0,0 +1,106 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using DiscordRPC; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Logging; +using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Users; +using User = osu.Game.Users.User; + +namespace osu.Desktop +{ + internal class DiscordRichPresenceClient : Component + { + private const string client_id = "559391129716391967"; + + private Bindable user; + + private readonly DiscordRpcClient client = new DiscordRpcClient(client_id); + + [BackgroundDependencyLoader] + private void load(IAPIProvider provider) + { + user = provider.LocalUser.GetBoundCopy(); + + user.ValueChanged += usr => + { + usr.OldValue.Status.ValueChanged -= updateStatus; + usr.NewValue.Status.ValueChanged += updateStatus; + }; + + user.Value.Status.ValueChanged += updateStatus; + + client.OnReady += (_, __) => Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); + client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client : {e.Message}", LoggingTarget.Network, LogLevel.Debug); + client.OnConnectionFailed += (_, e) => Logger.Log("Discord RPC Client failed to initialize : is discord running ?", LoggingTarget.Network, LogLevel.Debug); + client.OnPresenceUpdate += (_, __) => Logger.Log("Updated Discord Rich Presence", LoggingTarget.Network, LogLevel.Debug); + + client.Initialize(); + } + + private void updateStatus(ValueChangedEvent e) + { + var presence = defaultPresence(e.NewValue.Message); + + switch (e.NewValue) + { + case UserStatusSoloGame game: + presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; + setPresenceGamemode(game.Beatmap.Ruleset, presence); + break; + + case UserStatusEditing editing: + presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title}" + (editing.Beatmap.Version != null ? $"[{editing.Beatmap.Version}]" : ""); + break; + } + + client.SetPresence(presence); + } + + private void setPresenceGamemode(RulesetInfo ruleset, RichPresence presence) + { + switch (ruleset.ID) + { + case 0: + presence.Assets.SmallImageKey = "osu"; + break; + + case 1: + presence.Assets.SmallImageKey = "taiko"; + break; + + case 2: + presence.Assets.SmallImageKey = "fruits"; + break; + + case 3: + presence.Assets.SmallImageKey = "mania"; + break; + } + + presence.Assets.SmallImageText = ruleset.ShortName; + } + + private RichPresence defaultPresence(string status) => new RichPresence + { + Details = status, + Assets = new Assets + { + LargeImageKey = "lazer", + LargeImageText = "osu!lazer" + } + }; + + protected override void Update() + { + if (client.IsInitialized) + client?.Invoke(); + + base.Update(); + } + } +} diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index e7e0af7eea..41e2efd011 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -64,6 +64,8 @@ namespace osu.Desktop else Add(new SimpleUpdateManager()); } + + Add(new DiscordRichPresenceClient()); } protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen) From 4996ad4b042c73de5919818c2a047f19b701c75b Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 13 Apr 2019 13:25:52 +0200 Subject: [PATCH 03/20] Show the current gamemode on the rich presence --- osu.Desktop/DiscordRichPresenceClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 03b2884ab6..1e7c1f051a 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -50,7 +50,7 @@ namespace osu.Desktop { case UserStatusSoloGame game: presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; - setPresenceGamemode(game.Beatmap.Ruleset, presence); + setPresenceGamemode(game.Ruleset, presence); break; case UserStatusEditing editing: From 1b1ebb7fd928f0651e7c2777ce0807e47961ea54 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 14 Apr 2019 21:12:10 +0200 Subject: [PATCH 04/20] Show current logged-in user on rich presence --- osu.Desktop/DiscordRichPresenceClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 1e7c1f051a..1f4d91dbdb 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -91,7 +91,7 @@ namespace osu.Desktop Assets = new Assets { LargeImageKey = "lazer", - LargeImageText = "osu!lazer" + LargeImageText = user.Value.Username } }; From b38160177af31069b0c5377a7d1c8eaa92fe945e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 14 Apr 2019 22:18:57 +0200 Subject: [PATCH 05/20] Add some more icons to rich presence + prevent rich presence from displaying empty brackets when current beatmap doesn't have a version (trying to edit osu! main menu theme music ?) --- osu.Desktop/DiscordRichPresenceClient.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 1f4d91dbdb..93c2a8bdcf 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -15,7 +15,7 @@ namespace osu.Desktop { internal class DiscordRichPresenceClient : Component { - private const string client_id = "559391129716391967"; + private const string client_id = "563024054391537674"; private Bindable user; @@ -54,7 +54,9 @@ namespace osu.Desktop break; case UserStatusEditing editing: - presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title}" + (editing.Beatmap.Version != null ? $"[{editing.Beatmap.Version}]" : ""); + presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); + presence.Assets.SmallImageKey = "edit"; + presence.Assets.SmallImageText = "editing"; break; } @@ -80,6 +82,10 @@ namespace osu.Desktop case 3: presence.Assets.SmallImageKey = "mania"; break; + + default: + presence.Assets.SmallImageKey = "unknown"; + break; } presence.Assets.SmallImageText = ruleset.ShortName; From a4166ce1e3991ba23a7bac15bf10ca860dace0f1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 19 Apr 2019 19:50:13 +0200 Subject: [PATCH 06/20] Removed Update() override from DiscordRichPresenceClient because client.Invoke() is unecessary (events callbacks are automatically called) --- osu.Desktop/DiscordRichPresenceClient.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 93c2a8bdcf..9288141e5f 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -100,13 +100,5 @@ namespace osu.Desktop LargeImageText = user.Value.Username } }; - - protected override void Update() - { - if (client.IsInitialized) - client?.Invoke(); - - base.Update(); - } } } From ee2bbf950f682c82e6cb31ae20f871b1958724af Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 14 May 2019 19:13:21 +0200 Subject: [PATCH 07/20] Update DiscordRichPresenceClient presence logic --- osu.Desktop/DiscordRichPresenceClient.cs | 36 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 9288141e5f..9537252216 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -9,6 +9,7 @@ using osu.Framework.Logging; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Users; +using static osu.Game.Users.UserActivity; using User = osu.Game.Users.User; namespace osu.Desktop @@ -28,32 +29,43 @@ namespace osu.Desktop user.ValueChanged += usr => { - usr.OldValue.Status.ValueChanged -= updateStatus; - usr.NewValue.Status.ValueChanged += updateStatus; + usr.NewValue.Activity.ValueChanged += activity => updateStatus(user.Value.Status.Value, activity.NewValue); + usr.NewValue.Status.ValueChanged += status => updateStatus(status.NewValue, user.Value.Activity.Value); }; - user.Value.Status.ValueChanged += updateStatus; + user.TriggerChange(); - client.OnReady += (_, __) => Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); - client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client : {e.Message}", LoggingTarget.Network, LogLevel.Debug); - client.OnConnectionFailed += (_, e) => Logger.Log("Discord RPC Client failed to initialize : is discord running ?", LoggingTarget.Network, LogLevel.Debug); - client.OnPresenceUpdate += (_, __) => Logger.Log("Updated Discord Rich Presence", LoggingTarget.Network, LogLevel.Debug); + enableLogging(); client.Initialize(); } - private void updateStatus(ValueChangedEvent e) + private void enableLogging() { - var presence = defaultPresence(e.NewValue.Message); + client.OnReady += (_, __) => Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); + client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client : {e.Message}", LoggingTarget.Network, LogLevel.Debug); + client.OnConnectionFailed += (_, e) => Logger.Log("Discord RPC Client failed to initialize : is discord running ?", LoggingTarget.Network, LogLevel.Debug); + client.OnPresenceUpdate += (_, __) => Logger.Log("Updated Discord Rich Presence", LoggingTarget.Network, LogLevel.Debug); + } - switch (e.NewValue) + private void updateStatus(UserStatus st, UserActivity a) + { + var presence = defaultPresence(st is UserStatusOnline ? a?.Status : st.Message); + + if (!(st is UserStatusOnline)) //don't update the presence any further if the current user status is DND / Offline & simply return with the default presence { - case UserStatusSoloGame game: + client.SetPresence(presence); + return; + } + + switch (a) + { + case UserActivitySoloGame game: presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; setPresenceGamemode(game.Ruleset, presence); break; - case UserStatusEditing editing: + case UserActivityEditing editing: presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); presence.Assets.SmallImageKey = "edit"; presence.Assets.SmallImageText = "editing"; From d4013ae0d894134c2397a84f2bf0427bfa026ec0 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 14 May 2019 19:37:43 +0200 Subject: [PATCH 08/20] Make DiscordRichPresenceClient dispose client on disposal --- osu.Desktop/DiscordRichPresenceClient.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 9537252216..d49b08823c 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -112,5 +112,11 @@ namespace osu.Desktop LargeImageText = user.Value.Username } }; + + protected override void Dispose(bool isDisposing) + { + client.Dispose(); + base.Dispose(isDisposing); + } } } From 01b75db21a791732b1e2c57951eaae7a8c2523ac Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 16 May 2019 18:36:54 +0200 Subject: [PATCH 09/20] Use ruleset.ShortName instead of hardcoded names. --- osu.Desktop/DiscordRichPresenceClient.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index d49b08823c..38e929c1ae 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -80,19 +80,19 @@ namespace osu.Desktop switch (ruleset.ID) { case 0: - presence.Assets.SmallImageKey = "osu"; + presence.Assets.SmallImageKey = ruleset.ShortName; break; case 1: - presence.Assets.SmallImageKey = "taiko"; + presence.Assets.SmallImageKey = ruleset.ShortName; break; case 2: - presence.Assets.SmallImageKey = "fruits"; + presence.Assets.SmallImageKey = ruleset.ShortName; break; case 3: - presence.Assets.SmallImageKey = "mania"; + presence.Assets.SmallImageKey = ruleset.ShortName; break; default: From 446210b81223a7635496071a1204ae64d95d25aa Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 25 May 2019 11:33:31 +0200 Subject: [PATCH 10/20] Use an if statement instead of a switch --- osu.Desktop/DiscordRichPresenceClient.cs | 26 ++++-------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 38e929c1ae..4a2f882e2b 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -77,28 +77,10 @@ namespace osu.Desktop private void setPresenceGamemode(RulesetInfo ruleset, RichPresence presence) { - switch (ruleset.ID) - { - case 0: - presence.Assets.SmallImageKey = ruleset.ShortName; - break; - - case 1: - presence.Assets.SmallImageKey = ruleset.ShortName; - break; - - case 2: - presence.Assets.SmallImageKey = ruleset.ShortName; - break; - - case 3: - presence.Assets.SmallImageKey = ruleset.ShortName; - break; - - default: - presence.Assets.SmallImageKey = "unknown"; - break; - } + if (ruleset.ID != null && ruleset.ID <= 3) //legacy rulesets use an ID between 0 and 3 + presence.Assets.SmallImageKey = ruleset.ShortName; + else + presence.Assets.SmallImageKey = "unknown"; //not a legay ruleset so let's display the unknown ruleset icon. presence.Assets.SmallImageText = ruleset.ShortName; } From 9faba94978de1db4008a2124c90f909117e4e93a Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 12 Jun 2019 12:22:52 +0200 Subject: [PATCH 11/20] Fix references to UserActivities --- osu.Desktop/DiscordRichPresenceClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 4a2f882e2b..15d5770041 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -60,12 +60,12 @@ namespace osu.Desktop switch (a) { - case UserActivitySoloGame game: + case UserActivity.SoloGame game: presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; setPresenceGamemode(game.Ruleset, presence); break; - case UserActivityEditing editing: + case UserActivity.Editing editing: presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); presence.Assets.SmallImageKey = "edit"; presence.Assets.SmallImageText = "editing"; From 4275f70cf453aeb3799d934fe9bfd67c81040b8f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 12 Jun 2019 13:14:01 +0200 Subject: [PATCH 12/20] Fix CI inspections. --- osu.Desktop/DiscordRichPresenceClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 15d5770041..81ab995b41 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -60,12 +60,12 @@ namespace osu.Desktop switch (a) { - case UserActivity.SoloGame game: + case SoloGame game: presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; setPresenceGamemode(game.Ruleset, presence); break; - case UserActivity.Editing editing: + case Editing editing: presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); presence.Assets.SmallImageKey = "edit"; presence.Assets.SmallImageText = "editing"; From 54e6e4701970d2372008869d55d3f5263df0b34d Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 21 Jun 2019 12:48:13 +0200 Subject: [PATCH 13/20] Display current user activity on rich presence if current activity != null & user online status == online. --- osu.Desktop/DiscordRichPresenceClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index 81ab995b41..af5b42b275 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -50,7 +50,7 @@ namespace osu.Desktop private void updateStatus(UserStatus st, UserActivity a) { - var presence = defaultPresence(st is UserStatusOnline ? a?.Status : st.Message); + var presence = defaultPresence(st is UserStatusOnline ? a?.Status ?? st.Message : st.Message); //display the current user activity if the user status is online & user activity != null, else display the current user online status if (!(st is UserStatusOnline)) //don't update the presence any further if the current user status is DND / Offline & simply return with the default presence { @@ -80,7 +80,7 @@ namespace osu.Desktop if (ruleset.ID != null && ruleset.ID <= 3) //legacy rulesets use an ID between 0 and 3 presence.Assets.SmallImageKey = ruleset.ShortName; else - presence.Assets.SmallImageKey = "unknown"; //not a legay ruleset so let's display the unknown ruleset icon. + presence.Assets.SmallImageKey = "unknown"; //not a legacy ruleset so let's display the unknown ruleset icon. presence.Assets.SmallImageText = ruleset.ShortName; } From 0f5ef78b69c552545e5cc7dfc9b6a91bd2afafb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 12:39:50 +0900 Subject: [PATCH 14/20] Update client id --- osu.Desktop/DiscordRichPresenceClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs index af5b42b275..7a661fe6a2 100644 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ b/osu.Desktop/DiscordRichPresenceClient.cs @@ -16,7 +16,7 @@ namespace osu.Desktop { internal class DiscordRichPresenceClient : Component { - private const string client_id = "563024054391537674"; + private const string client_id = "367827983903490050"; private Bindable user; From 756d847ad8c848ac24800fd4436ef31fbf261d59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:03 +0900 Subject: [PATCH 15/20] Fix user not getting an initial status --- osu.Game/Online/API/APIAccess.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 8bfc28e774..23c931d161 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -153,6 +153,10 @@ namespace osu.Game.Online.API userReq.Success += u => { LocalUser.Value = u; + + // todo: save/pull from settings + LocalUser.Value.Status.Value = new UserStatusOnline(); + failureCount = 0; //we're connected! From 2f5b27e97c11d122be488e2dbb4ff5dee349e4e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:12 +0900 Subject: [PATCH 16/20] Make user bindables readonly --- osu.Game/Users/User.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index ebd9dbecd1..5d0ffd5a67 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -26,9 +26,9 @@ namespace osu.Game.Users [JsonProperty(@"country")] public Country Country; - public Bindable Status = new Bindable(); + public readonly Bindable Status = new Bindable(); - public IBindable Activity = new Bindable(); + public readonly Bindable Activity = new Bindable(); //public Team Team; From f53fd6e4bcfce9b22eb692c223bdf0cb30332e09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:21 +0900 Subject: [PATCH 17/20] Fix status capitalisation --- osu.Game/Users/UserActivity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index 918c547978..8030fc55a2 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -62,7 +62,7 @@ namespace osu.Game.Users public class InLobby : UserActivity { - public override string Status => @"In a Multiplayer Lobby"; + public override string Status => @"In a multiplayer lobby"; } } } From 0a3d339dd91a6d18f23e1bf7097aec50fe8a65a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:32 +0900 Subject: [PATCH 18/20] Load discord RPC asynchronously --- osu.Desktop/OsuGameDesktop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 9f2a4b12a1..f70cc24159 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -61,7 +61,7 @@ namespace osu.Desktop Add(new SimpleUpdateManager()); } - Add(new DiscordRichPresenceClient()); + LoadComponentAsync(new DiscordRichPresence(), Add); } protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen) From b65847b0d76bd3fffb629176e0a33cdb34764c40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:07:53 +0900 Subject: [PATCH 19/20] Refactor / rewrite discord code --- osu.Desktop/DiscordRichPresence.cs | 120 +++++++++++++++++++++++ osu.Desktop/DiscordRichPresenceClient.cs | 104 -------------------- 2 files changed, 120 insertions(+), 104 deletions(-) create mode 100644 osu.Desktop/DiscordRichPresence.cs delete mode 100644 osu.Desktop/DiscordRichPresenceClient.cs diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs new file mode 100644 index 0000000000..d1bd8fd292 --- /dev/null +++ b/osu.Desktop/DiscordRichPresence.cs @@ -0,0 +1,120 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using DiscordRPC; +using DiscordRPC.Message; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Logging; +using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Users; +using LogLevel = osu.Framework.Logging.LogLevel; +using User = osu.Game.Users.User; + +namespace osu.Desktop +{ + internal class DiscordRichPresence : Component + { + private const string client_id = "367827983903490050"; + + private DiscordRpcClient client; + + [Resolved] + private IBindable ruleset { get; set; } + + private Bindable user; + + private readonly IBindable status = new Bindable(); + private readonly IBindable activity = new Bindable(); + + private readonly RichPresence presence = new RichPresence + { + Assets = new Assets { LargeImageKey = "osu_logo_lazer", } + }; + + [BackgroundDependencyLoader] + private void load(IAPIProvider provider) + { + client = new DiscordRpcClient(client_id) + { + SkipIdenticalPresence = false // handles better on discord IPC loss, see updateStatus call in onReady. + }; + + client.OnReady += onReady; + client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network, LogLevel.Error); + client.OnConnectionFailed += (_, e) => Logger.Log($"An connection occurred with Discord RPC Client: {e.Type}", LoggingTarget.Network, LogLevel.Error); + + (user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u => + { + status.UnbindBindings(); + status.BindTo(u.NewValue.Status); + + activity.UnbindBindings(); + activity.BindTo(u.NewValue.Activity); + }, true); + + ruleset.BindValueChanged(_ => updateStatus()); + status.BindValueChanged(_ => updateStatus()); + activity.BindValueChanged(_ => updateStatus()); + + client.Initialize(); + } + + private void onReady(object _, ReadyMessage __) + { + Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); + updateStatus(); + } + + private void updateStatus() + { + if (status.Value is UserStatusOffline) + { + client.ClearPresence(); + return; + } + + if (status.Value is UserStatusOnline && activity.Value != null) + { + presence.State = activity.Value.Status; + presence.Details = getDetails(activity.Value); + } + else + { + presence.State = "Idle"; + presence.Details = string.Empty; + } + + // update user information + presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); + + // update ruleset + presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_unknown"; + presence.Assets.SmallImageText = ruleset.Value.Name; + + client.SetPresence(presence); + } + + private string getDetails(UserActivity activity) + { + switch (activity) + { + case UserActivity.SoloGame solo: + return solo.Beatmap.ToString(); + + case UserActivity.Editing edit: + return edit.Beatmap.ToString(); + } + + return string.Empty; + } + + protected override void Dispose(bool isDisposing) + { + client.Dispose(); + base.Dispose(isDisposing); + } + } +} diff --git a/osu.Desktop/DiscordRichPresenceClient.cs b/osu.Desktop/DiscordRichPresenceClient.cs deleted file mode 100644 index 7a661fe6a2..0000000000 --- a/osu.Desktop/DiscordRichPresenceClient.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using DiscordRPC; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Logging; -using osu.Game.Online.API; -using osu.Game.Rulesets; -using osu.Game.Users; -using static osu.Game.Users.UserActivity; -using User = osu.Game.Users.User; - -namespace osu.Desktop -{ - internal class DiscordRichPresenceClient : Component - { - private const string client_id = "367827983903490050"; - - private Bindable user; - - private readonly DiscordRpcClient client = new DiscordRpcClient(client_id); - - [BackgroundDependencyLoader] - private void load(IAPIProvider provider) - { - user = provider.LocalUser.GetBoundCopy(); - - user.ValueChanged += usr => - { - usr.NewValue.Activity.ValueChanged += activity => updateStatus(user.Value.Status.Value, activity.NewValue); - usr.NewValue.Status.ValueChanged += status => updateStatus(status.NewValue, user.Value.Activity.Value); - }; - - user.TriggerChange(); - - enableLogging(); - - client.Initialize(); - } - - private void enableLogging() - { - client.OnReady += (_, __) => Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug); - client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client : {e.Message}", LoggingTarget.Network, LogLevel.Debug); - client.OnConnectionFailed += (_, e) => Logger.Log("Discord RPC Client failed to initialize : is discord running ?", LoggingTarget.Network, LogLevel.Debug); - client.OnPresenceUpdate += (_, __) => Logger.Log("Updated Discord Rich Presence", LoggingTarget.Network, LogLevel.Debug); - } - - private void updateStatus(UserStatus st, UserActivity a) - { - var presence = defaultPresence(st is UserStatusOnline ? a?.Status ?? st.Message : st.Message); //display the current user activity if the user status is online & user activity != null, else display the current user online status - - if (!(st is UserStatusOnline)) //don't update the presence any further if the current user status is DND / Offline & simply return with the default presence - { - client.SetPresence(presence); - return; - } - - switch (a) - { - case SoloGame game: - presence.State = $"{game.Beatmap.Metadata.Artist} - {game.Beatmap.Metadata.Title} [{game.Beatmap.Version}]"; - setPresenceGamemode(game.Ruleset, presence); - break; - - case Editing editing: - presence.State = $"{editing.Beatmap.Metadata.Artist} - {editing.Beatmap.Metadata.Title} " + (!string.IsNullOrEmpty(editing.Beatmap.Version) ? $"[{editing.Beatmap.Version}]" : ""); - presence.Assets.SmallImageKey = "edit"; - presence.Assets.SmallImageText = "editing"; - break; - } - - client.SetPresence(presence); - } - - private void setPresenceGamemode(RulesetInfo ruleset, RichPresence presence) - { - if (ruleset.ID != null && ruleset.ID <= 3) //legacy rulesets use an ID between 0 and 3 - presence.Assets.SmallImageKey = ruleset.ShortName; - else - presence.Assets.SmallImageKey = "unknown"; //not a legacy ruleset so let's display the unknown ruleset icon. - - presence.Assets.SmallImageText = ruleset.ShortName; - } - - private RichPresence defaultPresence(string status) => new RichPresence - { - Details = status, - Assets = new Assets - { - LargeImageKey = "lazer", - LargeImageText = user.Value.Username - } - }; - - protected override void Dispose(bool isDisposing) - { - client.Dispose(); - base.Dispose(isDisposing); - } - } -} From 0710e5ba13749212b158f008ecc9c2b5987b3ca8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 18 Dec 2019 14:35:18 +0900 Subject: [PATCH 20/20] Rename unknown mode assets (discord dev page broken) --- osu.Desktop/DiscordRichPresence.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index d1bd8fd292..b53ca6161b 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -91,7 +91,7 @@ namespace osu.Desktop presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); // update ruleset - presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_unknown"; + presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom"; presence.Assets.SmallImageText = ruleset.Value.Name; client.SetPresence(presence);