1- using System . Runtime . InteropServices ;
1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Runtime . InteropServices ;
4+ using System . Text ;
25
36namespace 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+ }
0 commit comments