Skip to content

Commit 626bf17

Browse files
authored
Merge pull request #1 from yuwui/patch/fix-garbled-text
Fix garbled text on some systems
2 parents ce0bae0 + 4845b35 commit 626bf17

File tree

4 files changed

+239
-117
lines changed

4 files changed

+239
-117
lines changed

DiscordInterface.cs

Lines changed: 197 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,134 @@
1-
using System.Runtime.InteropServices;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Runtime.InteropServices;
4+
using System.Text;
25

36
namespace DiscordInterface
47
{
5-
public class DiscordRPC
8+
// stolen from https://github.com/discord/discord-rpc/blob/master/examples/button-clicker/Assets/DiscordRpc.cs
9+
public class DiscordRpc
610
{
7-
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
8-
public delegate void ReadyCallback();
11+
public static void ReadyCallback(ref DiscordUser connectedUser) { Callbacks.readyCallback(ref connectedUser); }
12+
public delegate void OnReadyInfo(ref DiscordUser connectedUser);
913

10-
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
11-
public delegate void DisconnectedCallback(int errorCode, string message);
14+
public static void DisconnectedCallback(int errorCode, string message) { Callbacks.disconnectedCallback(errorCode, message); }
15+
public delegate void OnDisconnectedInfo(int errorCode, string message);
1216

13-
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
14-
public delegate void ErrorCallback(int errorCode, string message);
17+
public static void ErrorCallback(int errorCode, string message) { Callbacks.errorCallback(errorCode, message); }
18+
public delegate void OnErrorInfo(int errorCode, string message);
1519

16-
public struct DiscordEventHandlers
20+
public static void JoinCallback(string secret) { Callbacks.joinCallback(secret); }
21+
public delegate void OnJoinInfo(string secret);
22+
23+
public static void SpectateCallback(string secret) { Callbacks.spectateCallback(secret); }
24+
public delegate void OnSpectateInfo(string secret);
25+
26+
public static void RequestCallback(ref DiscordUser request) { Callbacks.requestCallback(ref request); }
27+
public delegate void OnRequestInfo(ref DiscordUser request);
28+
29+
static EventHandlers Callbacks { get; set; }
30+
31+
public struct EventHandlers
32+
{
33+
public OnReadyInfo readyCallback;
34+
public OnDisconnectedInfo disconnectedCallback;
35+
public OnErrorInfo errorCallback;
36+
public OnJoinInfo joinCallback;
37+
public OnSpectateInfo spectateCallback;
38+
public OnRequestInfo requestCallback;
39+
}
40+
41+
[Serializable, StructLayout(LayoutKind.Sequential)]
42+
public struct RichPresenceStruct
1743
{
18-
public ReadyCallback readyCallback;
19-
public DisconnectedCallback disconnectedCallback;
20-
public ErrorCallback errorCallback;
44+
public IntPtr state; /* max 128 bytes */
45+
public IntPtr details; /* max 128 bytes */
46+
public long startTimestamp;
47+
public long endTimestamp;
48+
public IntPtr largeImageKey; /* max 32 bytes */
49+
public IntPtr largeImageText; /* max 128 bytes */
50+
public IntPtr smallImageKey; /* max 32 bytes */
51+
public IntPtr smallImageText; /* max 128 bytes */
52+
public IntPtr partyId; /* max 128 bytes */
53+
public int partySize;
54+
public int partyMax;
55+
public int partyPrivacy;
56+
public IntPtr matchSecret; /* max 128 bytes */
57+
public IntPtr joinSecret; /* max 128 bytes */
58+
public IntPtr spectateSecret; /* max 128 bytes */
59+
public bool instance;
2160
}
2261

23-
[System.Serializable]
24-
public struct RichPresence
62+
[Serializable]
63+
public struct DiscordUser
2564
{
65+
public string userId;
66+
public string username;
67+
public string discriminator;
68+
public string avatar;
69+
}
70+
71+
public enum Reply
72+
{
73+
No = 0,
74+
Yes = 1,
75+
Ignore = 2
76+
}
77+
78+
public enum PartyPrivacy
79+
{
80+
Private = 0,
81+
Public = 1
82+
}
83+
84+
public static void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId)
85+
{
86+
Callbacks = handlers;
87+
88+
EventHandlers staticEventHandlers = new EventHandlers();
89+
staticEventHandlers.readyCallback += DiscordRpc.ReadyCallback;
90+
staticEventHandlers.disconnectedCallback += DiscordRpc.DisconnectedCallback;
91+
staticEventHandlers.errorCallback += DiscordRpc.ErrorCallback;
92+
staticEventHandlers.joinCallback += DiscordRpc.JoinCallback;
93+
staticEventHandlers.spectateCallback += DiscordRpc.SpectateCallback;
94+
staticEventHandlers.requestCallback += DiscordRpc.RequestCallback;
95+
96+
InitializeInternal(applicationId, ref staticEventHandlers, autoRegister, optionalSteamId);
97+
}
98+
99+
[DllImport("win32-discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
100+
static extern void InitializeInternal(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
101+
102+
[DllImport("win32-discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
103+
public static extern void Shutdown();
104+
105+
[DllImport("win32-discord-rpc", EntryPoint = "Discord_RunCallbacks", CallingConvention = CallingConvention.Cdecl)]
106+
public static extern void RunCallbacks();
107+
108+
[DllImport("win32-discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
109+
private static extern void UpdatePresenceNative(ref RichPresenceStruct presence);
110+
111+
[DllImport("win32-discord-rpc", EntryPoint = "Discord_ClearPresence", CallingConvention = CallingConvention.Cdecl)]
112+
public static extern void ClearPresence();
113+
114+
[DllImport("win32-discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
115+
public static extern void Respond(string userId, Reply reply);
116+
117+
[DllImport("win32-discord-rpc", EntryPoint = "Discord_UpdateHandlers", CallingConvention = CallingConvention.Cdecl)]
118+
public static extern void UpdateHandlers(ref EventHandlers handlers);
119+
120+
public static void UpdatePresence(RichPresence presence)
121+
{
122+
var presencestruct = presence.GetStruct();
123+
UpdatePresenceNative(ref presencestruct);
124+
presence.FreeMem();
125+
}
126+
127+
public class RichPresence
128+
{
129+
private RichPresenceStruct _presence;
130+
private readonly List<IntPtr> _buffers = new List<IntPtr>(10);
131+
26132
public string state; /* max 128 bytes */
27133
public string details; /* max 128 bytes */
28134
public long startTimestamp;
@@ -34,22 +140,89 @@ public struct RichPresence
34140
public string partyId; /* max 128 bytes */
35141
public int partySize;
36142
public int partyMax;
143+
public PartyPrivacy partyPrivacy;
37144
public string matchSecret; /* max 128 bytes */
38145
public string joinSecret; /* max 128 bytes */
39146
public string spectateSecret; /* max 128 bytes */
40147
public bool instance;
41-
}
42148

43-
[DllImport("win32-discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
44-
public static extern void Initialize(string applicationId, ref DiscordEventHandlers handlers, bool autoRegister, string optionalSteamId);
149+
/// <summary>
150+
/// Get the <see cref="RichPresenceStruct"/> reprensentation of this instance
151+
/// </summary>
152+
/// <returns><see cref="RichPresenceStruct"/> reprensentation of this instance</returns>
153+
internal RichPresenceStruct GetStruct()
154+
{
155+
if (_buffers.Count > 0)
156+
{
157+
FreeMem();
158+
}
45159

46-
[DllImport("win32-discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
47-
public static extern void UpdatePresence(ref RichPresence presence);
160+
_presence.state = StrToPtr(state);
161+
_presence.details = StrToPtr(details);
162+
_presence.startTimestamp = startTimestamp;
163+
_presence.endTimestamp = endTimestamp;
164+
_presence.largeImageKey = StrToPtr(largeImageKey);
165+
_presence.largeImageText = StrToPtr(largeImageText);
166+
_presence.smallImageKey = StrToPtr(smallImageKey);
167+
_presence.smallImageText = StrToPtr(smallImageText);
168+
_presence.partyId = StrToPtr(partyId);
169+
_presence.partySize = partySize;
170+
_presence.partyMax = partyMax;
171+
_presence.partyPrivacy = (int)partyPrivacy;
172+
_presence.matchSecret = StrToPtr(matchSecret);
173+
_presence.joinSecret = StrToPtr(joinSecret);
174+
_presence.spectateSecret = StrToPtr(spectateSecret);
175+
_presence.instance = instance;
48176

49-
[DllImport("win32-discord-rpc", EntryPoint = "Discord_RunCallbacks", CallingConvention = CallingConvention.Cdecl)]
50-
public static extern void RunCallbacks();
177+
return _presence;
178+
}
51179

52-
[DllImport("win32-discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
53-
public static extern void Shutdown();
180+
/// <summary>
181+
/// Returns a pointer to a representation of the given string with a size of maxbytes
182+
/// </summary>
183+
/// <param name="input">String to convert</param>
184+
/// <returns>Pointer to the UTF-8 representation of <see cref="input"/></returns>
185+
private IntPtr StrToPtr(string input)
186+
{
187+
if (string.IsNullOrEmpty(input)) return IntPtr.Zero;
188+
var convbytecnt = Encoding.UTF8.GetByteCount(input);
189+
var buffer = Marshal.AllocHGlobal(convbytecnt + 1);
190+
for (int i = 0; i < convbytecnt + 1; i++)
191+
{
192+
Marshal.WriteByte(buffer, i, 0);
193+
}
194+
_buffers.Add(buffer);
195+
Marshal.Copy(Encoding.UTF8.GetBytes(input), 0, buffer, convbytecnt);
196+
return buffer;
197+
}
198+
199+
/// <summary>
200+
/// Convert string to UTF-8 and add null termination
201+
/// </summary>
202+
/// <param name="toconv">string to convert</param>
203+
/// <returns>UTF-8 representation of <see cref="toconv"/> with added null termination</returns>
204+
private static string StrToUtf8NullTerm(string toconv)
205+
{
206+
var str = toconv.Trim();
207+
var bytes = Encoding.Default.GetBytes(str);
208+
if (bytes.Length > 0 && bytes[bytes.Length - 1] != 0)
209+
{
210+
str += "\0\0";
211+
}
212+
return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(str));
213+
}
214+
215+
/// <summary>
216+
/// Free the allocated memory for conversion to <see cref="RichPresenceStruct"/>
217+
/// </summary>
218+
internal void FreeMem()
219+
{
220+
for (var i = _buffers.Count - 1; i >= 0; i--)
221+
{
222+
Marshal.FreeHGlobal(_buffers[i]);
223+
_buffers.RemoveAt(i);
224+
}
225+
}
226+
}
54227
}
55-
}
228+
}

Util.cs

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)