DiscordIntegrationModule.cs
1 using DiscordRPC; 2 using Ryujinx.Common; 3 using Ryujinx.UI.Common.Configuration; 4 using System.Text; 5 6 namespace Ryujinx.UI.Common 7 { 8 public static class DiscordIntegrationModule 9 { 10 private const string Description = "A simple, experimental Nintendo Switch emulator."; 11 private const string ApplicationId = "1216775165866807456"; 12 13 private const int ApplicationByteLimit = 128; 14 private const string Ellipsis = "…"; 15 16 private static DiscordRpcClient _discordClient; 17 private static RichPresence _discordPresenceMain; 18 19 public static void Initialize() 20 { 21 _discordPresenceMain = new RichPresence 22 { 23 Assets = new Assets 24 { 25 LargeImageKey = "ryujinx", 26 LargeImageText = Description, 27 }, 28 Details = "Main Menu", 29 State = "Idling", 30 Timestamps = Timestamps.Now, 31 Buttons = 32 [ 33 new Button 34 { 35 Label = "Website", 36 Url = "https://ryujinx.org/", 37 }, 38 ], 39 }; 40 41 ConfigurationState.Instance.EnableDiscordIntegration.Event += Update; 42 } 43 44 private static void Update(object sender, ReactiveEventArgs<bool> evnt) 45 { 46 if (evnt.OldValue != evnt.NewValue) 47 { 48 // If the integration was active, disable it and unload everything 49 if (evnt.OldValue) 50 { 51 _discordClient?.Dispose(); 52 53 _discordClient = null; 54 } 55 56 // If we need to activate it and the client isn't active, initialize it 57 if (evnt.NewValue && _discordClient == null) 58 { 59 _discordClient = new DiscordRpcClient(ApplicationId); 60 61 _discordClient.Initialize(); 62 _discordClient.SetPresence(_discordPresenceMain); 63 } 64 } 65 } 66 67 public static void SwitchToPlayingState(string titleId, string applicationName) 68 { 69 _discordClient?.SetPresence(new RichPresence 70 { 71 Assets = new Assets 72 { 73 LargeImageKey = "game", 74 LargeImageText = TruncateToByteLength(applicationName, ApplicationByteLimit), 75 SmallImageKey = "ryujinx", 76 SmallImageText = Description, 77 }, 78 Details = TruncateToByteLength($"Playing {applicationName}", ApplicationByteLimit), 79 State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(), 80 Timestamps = Timestamps.Now, 81 Buttons = 82 [ 83 new Button 84 { 85 Label = "Website", 86 Url = "https://ryujinx.org/", 87 }, 88 ], 89 }); 90 } 91 92 public static void SwitchToMainMenu() 93 { 94 _discordClient?.SetPresence(_discordPresenceMain); 95 } 96 97 private static string TruncateToByteLength(string input, int byteLimit) 98 { 99 if (Encoding.UTF8.GetByteCount(input) <= byteLimit) 100 { 101 return input; 102 } 103 104 // Find the length to trim the string to guarantee we have space for the trailing ellipsis. 105 int trimLimit = byteLimit - Encoding.UTF8.GetByteCount(Ellipsis); 106 107 // Make sure the string is long enough to perform the basic trim. 108 // Amount of bytes != Length of the string 109 if (input.Length > trimLimit) 110 { 111 // Basic trim to best case scenario of 1 byte characters. 112 input = input[..trimLimit]; 113 } 114 115 while (Encoding.UTF8.GetByteCount(input) > trimLimit) 116 { 117 // Remove one character from the end of the string at a time. 118 input = input[..^1]; 119 } 120 121 return input.TrimEnd() + Ellipsis; 122 } 123 124 public static void Exit() 125 { 126 _discordClient?.Dispose(); 127 } 128 } 129 }