/ src / Ryujinx.UI.Common / DiscordIntegrationModule.cs
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  }