2018-02-22 19:47:18 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Text;
|
2017-08-02 19:31:35 +01:00
|
|
|
|
|
|
|
|
|
public class DiscordRpc
|
|
|
|
|
{
|
2017-09-28 16:15:15 +01:00
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2018-04-16 18:25:44 +01:00
|
|
|
|
public delegate void ReadyCallback(ref DiscordUser connectedUser);
|
2017-08-02 19:31:35 +01:00
|
|
|
|
|
2017-09-28 16:15:15 +01:00
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2017-08-02 19:31:35 +01:00
|
|
|
|
public delegate void DisconnectedCallback(int errorCode, string message);
|
|
|
|
|
|
2017-09-28 16:15:15 +01:00
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2017-08-02 19:31:35 +01:00
|
|
|
|
public delegate void ErrorCallback(int errorCode, string message);
|
|
|
|
|
|
2017-09-28 16:15:15 +01:00
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2017-08-02 19:31:35 +01:00
|
|
|
|
public delegate void JoinCallback(string secret);
|
|
|
|
|
|
2017-09-28 16:15:15 +01:00
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2017-08-02 19:31:35 +01:00
|
|
|
|
public delegate void SpectateCallback(string secret);
|
|
|
|
|
|
2017-10-14 11:21:35 +01:00
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2018-04-16 18:25:44 +01:00
|
|
|
|
public delegate void RequestCallback(ref DiscordUser request);
|
2017-10-14 11:12:03 +01:00
|
|
|
|
|
2017-10-14 11:21:35 +01:00
|
|
|
|
public struct EventHandlers
|
2017-08-02 19:31:35 +01:00
|
|
|
|
{
|
|
|
|
|
public ReadyCallback readyCallback;
|
|
|
|
|
public DisconnectedCallback disconnectedCallback;
|
|
|
|
|
public ErrorCallback errorCallback;
|
|
|
|
|
public JoinCallback joinCallback;
|
|
|
|
|
public SpectateCallback spectateCallback;
|
2017-10-14 11:21:35 +01:00
|
|
|
|
public RequestCallback requestCallback;
|
|
|
|
|
}
|
2017-08-02 19:31:35 +01:00
|
|
|
|
|
2018-02-22 19:47:18 +00:00
|
|
|
|
[Serializable, StructLayout(LayoutKind.Sequential)]
|
|
|
|
|
public struct RichPresenceStruct
|
2017-08-02 19:31:35 +01:00
|
|
|
|
{
|
2018-02-22 19:47:18 +00:00
|
|
|
|
public IntPtr state; /* max 128 bytes */
|
|
|
|
|
public IntPtr details; /* max 128 bytes */
|
2017-08-02 19:31:35 +01:00
|
|
|
|
public long startTimestamp;
|
|
|
|
|
public long endTimestamp;
|
2018-02-22 19:47:18 +00:00
|
|
|
|
public IntPtr largeImageKey; /* max 32 bytes */
|
|
|
|
|
public IntPtr largeImageText; /* max 128 bytes */
|
|
|
|
|
public IntPtr smallImageKey; /* max 32 bytes */
|
|
|
|
|
public IntPtr smallImageText; /* max 128 bytes */
|
|
|
|
|
public IntPtr partyId; /* max 128 bytes */
|
2017-08-02 19:31:35 +01:00
|
|
|
|
public int partySize;
|
|
|
|
|
public int partyMax;
|
2018-02-22 19:47:18 +00:00
|
|
|
|
public IntPtr matchSecret; /* max 128 bytes */
|
|
|
|
|
public IntPtr joinSecret; /* max 128 bytes */
|
|
|
|
|
public IntPtr spectateSecret; /* max 128 bytes */
|
2017-08-02 19:31:35 +01:00
|
|
|
|
public bool instance;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 19:47:18 +00:00
|
|
|
|
[Serializable]
|
2018-04-16 18:25:44 +01:00
|
|
|
|
public struct DiscordUser
|
2017-10-14 11:21:35 +01:00
|
|
|
|
{
|
|
|
|
|
public string userId;
|
|
|
|
|
public string username;
|
2017-11-30 19:24:12 +00:00
|
|
|
|
public string discriminator;
|
2017-10-17 21:33:12 +01:00
|
|
|
|
public string avatar;
|
2017-10-14 11:21:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum Reply
|
|
|
|
|
{
|
|
|
|
|
No = 0,
|
|
|
|
|
Yes = 1,
|
|
|
|
|
Ignore = 2
|
|
|
|
|
}
|
2017-10-14 11:12:03 +01:00
|
|
|
|
|
2017-09-28 16:15:15 +01:00
|
|
|
|
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
|
2017-08-30 23:17:47 +01:00
|
|
|
|
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
|
2017-08-02 19:31:35 +01:00
|
|
|
|
|
2017-09-28 16:15:15 +01:00
|
|
|
|
[DllImport("discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
|
2017-08-02 19:31:35 +01:00
|
|
|
|
public static extern void Shutdown();
|
|
|
|
|
|
2017-09-28 16:15:15 +01:00
|
|
|
|
[DllImport("discord-rpc", EntryPoint = "Discord_RunCallbacks", CallingConvention = CallingConvention.Cdecl)]
|
2017-08-02 19:31:35 +01:00
|
|
|
|
public static extern void RunCallbacks();
|
|
|
|
|
|
2017-09-28 16:15:15 +01:00
|
|
|
|
[DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
|
2018-02-22 19:47:18 +00:00
|
|
|
|
private static extern void UpdatePresenceNative(ref RichPresenceStruct presence);
|
2017-10-14 11:12:03 +01:00
|
|
|
|
|
2018-01-05 23:17:23 +00:00
|
|
|
|
[DllImport("discord-rpc", EntryPoint = "Discord_ClearPresence", CallingConvention = CallingConvention.Cdecl)]
|
|
|
|
|
public static extern void ClearPresence();
|
|
|
|
|
|
2017-10-14 11:21:35 +01:00
|
|
|
|
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
|
|
|
|
|
public static extern void Respond(string userId, Reply reply);
|
2017-08-02 19:31:35 +01:00
|
|
|
|
|
2018-03-23 17:25:28 +00:00
|
|
|
|
[DllImport("discord-rpc", EntryPoint = "Discord_UpdateHandlers", CallingConvention = CallingConvention.Cdecl)]
|
|
|
|
|
public static extern void UpdateHandlers(ref EventHandlers handlers);
|
|
|
|
|
|
2018-02-22 19:47:18 +00:00
|
|
|
|
public static void UpdatePresence(RichPresence presence)
|
|
|
|
|
{
|
|
|
|
|
var presencestruct = presence.GetStruct();
|
|
|
|
|
UpdatePresenceNative(ref presencestruct);
|
|
|
|
|
presence.FreeMem();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class RichPresence
|
|
|
|
|
{
|
|
|
|
|
private RichPresenceStruct _presence;
|
|
|
|
|
private readonly List<IntPtr> _buffers = new List<IntPtr>(10);
|
|
|
|
|
|
|
|
|
|
public string state; /* max 128 bytes */
|
|
|
|
|
public string details; /* max 128 bytes */
|
|
|
|
|
public long startTimestamp;
|
|
|
|
|
public long endTimestamp;
|
|
|
|
|
public string largeImageKey; /* max 32 bytes */
|
|
|
|
|
public string largeImageText; /* max 128 bytes */
|
|
|
|
|
public string smallImageKey; /* max 32 bytes */
|
|
|
|
|
public string smallImageText; /* max 128 bytes */
|
|
|
|
|
public string partyId; /* max 128 bytes */
|
|
|
|
|
public int partySize;
|
|
|
|
|
public int partyMax;
|
|
|
|
|
public string matchSecret; /* max 128 bytes */
|
|
|
|
|
public string joinSecret; /* max 128 bytes */
|
|
|
|
|
public string spectateSecret; /* max 128 bytes */
|
|
|
|
|
public bool instance;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get the <see cref="RichPresenceStruct"/> reprensentation of this instance
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns><see cref="RichPresenceStruct"/> reprensentation of this instance</returns>
|
|
|
|
|
internal RichPresenceStruct GetStruct()
|
|
|
|
|
{
|
|
|
|
|
if (_buffers.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
FreeMem();
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-16 19:23:28 +01:00
|
|
|
|
_presence.state = StrToPtr(state);
|
|
|
|
|
_presence.details = StrToPtr(details);
|
2018-02-22 19:47:18 +00:00
|
|
|
|
_presence.startTimestamp = startTimestamp;
|
|
|
|
|
_presence.endTimestamp = endTimestamp;
|
2018-08-16 19:23:28 +01:00
|
|
|
|
_presence.largeImageKey = StrToPtr(largeImageKey);
|
|
|
|
|
_presence.largeImageText = StrToPtr(largeImageText);
|
|
|
|
|
_presence.smallImageKey = StrToPtr(smallImageKey);
|
|
|
|
|
_presence.smallImageText = StrToPtr(smallImageText);
|
|
|
|
|
_presence.partyId = StrToPtr(partyId);
|
2018-02-22 19:47:18 +00:00
|
|
|
|
_presence.partySize = partySize;
|
|
|
|
|
_presence.partyMax = partyMax;
|
2018-08-16 19:23:28 +01:00
|
|
|
|
_presence.matchSecret = StrToPtr(matchSecret);
|
|
|
|
|
_presence.joinSecret = StrToPtr(joinSecret);
|
|
|
|
|
_presence.spectateSecret = StrToPtr(spectateSecret);
|
2018-02-22 19:47:18 +00:00
|
|
|
|
_presence.instance = instance;
|
|
|
|
|
|
|
|
|
|
return _presence;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns a pointer to a representation of the given string with a size of maxbytes
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="input">String to convert</param>
|
|
|
|
|
/// <returns>Pointer to the UTF-8 representation of <see cref="input"/></returns>
|
2018-08-16 19:23:28 +01:00
|
|
|
|
private IntPtr StrToPtr(string input)
|
2018-02-22 19:47:18 +00:00
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(input)) return IntPtr.Zero;
|
2018-08-16 19:23:28 +01:00
|
|
|
|
var convbytecnt = Encoding.UTF8.GetByteCount(input);
|
|
|
|
|
var buffer = Marshal.AllocHGlobal(convbytecnt + 1);
|
|
|
|
|
for (int i = 0; i < convbytecnt + 1; i++)
|
|
|
|
|
{
|
|
|
|
|
Marshal.WriteByte(buffer, i , 0);
|
|
|
|
|
}
|
2018-02-22 19:47:18 +00:00
|
|
|
|
_buffers.Add(buffer);
|
2018-08-16 19:23:28 +01:00
|
|
|
|
Marshal.Copy(Encoding.UTF8.GetBytes(input), 0, buffer, convbytecnt);
|
2018-02-22 19:47:18 +00:00
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Convert string to UTF-8 and add null termination
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="toconv">string to convert</param>
|
|
|
|
|
/// <returns>UTF-8 representation of <see cref="toconv"/> with added null termination</returns>
|
|
|
|
|
private static string StrToUtf8NullTerm(string toconv)
|
|
|
|
|
{
|
|
|
|
|
var str = toconv.Trim();
|
|
|
|
|
var bytes = Encoding.Default.GetBytes(str);
|
|
|
|
|
if (bytes.Length > 0 && bytes[bytes.Length - 1] != 0)
|
|
|
|
|
{
|
|
|
|
|
str += "\0\0";
|
|
|
|
|
}
|
|
|
|
|
return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(str));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Free the allocated memory for conversion to <see cref="RichPresenceStruct"/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal void FreeMem()
|
|
|
|
|
{
|
|
|
|
|
for (var i = _buffers.Count - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
Marshal.FreeHGlobal(_buffers[i]);
|
|
|
|
|
_buffers.RemoveAt(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|