aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs
blob: 6966038b6148d890ccba789b071d1438f259f8fe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using DiscordRPC;
using Ryujinx.Common;
using Ryujinx.UI.Common.Configuration;
using System.Text;

namespace Ryujinx.UI.Common
{
    public static class DiscordIntegrationModule
    {
        private const string Description = "A simple, experimental Nintendo Switch emulator.";
        private const string ApplicationId = "1216775165866807456";

        private const int ApplicationByteLimit = 128;
        private const string Ellipsis = "…";

        private static DiscordRpcClient _discordClient;
        private static RichPresence _discordPresenceMain;

        public static void Initialize()
        {
            _discordPresenceMain = new RichPresence
            {
                Assets = new Assets
                {
                    LargeImageKey = "ryujinx",
                    LargeImageText = Description,
                },
                Details = "Main Menu",
                State = "Idling",
                Timestamps = Timestamps.Now,
                Buttons =
                [
                    new Button
                    {
                        Label = "Website",
                        Url = "https://ryujinx.org/",
                    },
                ],
            };

            ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
        }

        private static void Update(object sender, ReactiveEventArgs<bool> evnt)
        {
            if (evnt.OldValue != evnt.NewValue)
            {
                // If the integration was active, disable it and unload everything
                if (evnt.OldValue)
                {
                    _discordClient?.Dispose();

                    _discordClient = null;
                }

                // If we need to activate it and the client isn't active, initialize it
                if (evnt.NewValue && _discordClient == null)
                {
                    _discordClient = new DiscordRpcClient(ApplicationId);

                    _discordClient.Initialize();
                    _discordClient.SetPresence(_discordPresenceMain);
                }
            }
        }

        public static void SwitchToPlayingState(string titleId, string applicationName)
        {
            _discordClient?.SetPresence(new RichPresence
            {
                Assets = new Assets
                {
                    LargeImageKey = "game",
                    LargeImageText = TruncateToByteLength(applicationName, ApplicationByteLimit),
                    SmallImageKey = "ryujinx",
                    SmallImageText = Description,
                },
                Details = TruncateToByteLength($"Playing {applicationName}", ApplicationByteLimit),
                State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(),
                Timestamps = Timestamps.Now,
                Buttons =
                [
                    new Button
                    {
                        Label = "Website",
                        Url = "https://ryujinx.org/",
                    },
                ],
            });
        }

        public static void SwitchToMainMenu()
        {
            _discordClient?.SetPresence(_discordPresenceMain);
        }

        private static string TruncateToByteLength(string input, int byteLimit)
        {
            if (Encoding.UTF8.GetByteCount(input) <= byteLimit)
            {
                return input;
            }

            // Find the length to trim the string to guarantee we have space for the trailing ellipsis.
            int trimLimit = byteLimit - Encoding.UTF8.GetByteCount(Ellipsis);

            // Make sure the string is long enough to perform the basic trim.
            // Amount of bytes != Length of the string
            if (input.Length > trimLimit)
            {
                // Basic trim to best case scenario of 1 byte characters.
                input = input[..trimLimit];
            }

            while (Encoding.UTF8.GetByteCount(input) > trimLimit)
            {
                // Remove one character from the end of the string at a time.
                input = input[..^1];
            }

            return input.TrimEnd() + Ellipsis;
        }

        public static void Exit()
        {
            _discordClient?.Dispose();
        }
    }
}