Initial commit
This commit is contained in:
commit
262601edb8
2025 files changed
+218754
No files matched your search
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace DiscordPresence
|
||||
{
|
||||
public class DiscordRpc
|
||||
{
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void ReadyCallback();
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void DisconnectedCallback(int errorCode, string message);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void ErrorCallback(int errorCode, string message);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void JoinCallback(string secret);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void SpectateCallback(string secret);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void RequestCallback(ref JoinRequest request);
|
||||
|
||||
public struct EventHandlers
|
||||
{
|
||||
public ReadyCallback readyCallback;
|
||||
public DisconnectedCallback disconnectedCallback;
|
||||
public ErrorCallback errorCallback;
|
||||
public JoinCallback joinCallback;
|
||||
public SpectateCallback spectateCallback;
|
||||
public RequestCallback requestCallback;
|
||||
}
|
||||
|
||||
[Serializable, StructLayout(LayoutKind.Sequential)]
|
||||
public struct RichPresenceStruct
|
||||
{
|
||||
public IntPtr state; // max 128 bytes
|
||||
public IntPtr details; // max 128 bytes
|
||||
public long startTimestamp;
|
||||
public long endTimestamp;
|
||||
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
|
||||
public int partySize;
|
||||
public int partyMax;
|
||||
public IntPtr matchSecret; // max 128 bytes
|
||||
public IntPtr joinSecret; // max 128 bytes
|
||||
public IntPtr spectateSecret; // max 128 bytes
|
||||
public bool instance;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct JoinRequest
|
||||
{
|
||||
public string userId;
|
||||
public string username;
|
||||
public string discriminator;
|
||||
public string avatar;
|
||||
}
|
||||
|
||||
public enum Reply
|
||||
{
|
||||
No = 0,
|
||||
Yes = 1,
|
||||
Ignore = 2
|
||||
}
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Shutdown();
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_RunCallbacks", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void RunCallbacks();
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void UpdatePresenceNative(ref RichPresenceStruct presence);
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_ClearPresence", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void ClearPresence();
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Respond(string userId, Reply reply);
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_UpdateHandlers", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void UpdateHandlers(ref EventHandlers handlers);
|
||||
|
||||
public static void UpdatePresence(RichPresence presence)
|
||||
{
|
||||
var presencestruct = presence.GetStruct();
|
||||
UpdatePresenceNative(ref presencestruct);
|
||||
presence.FreeMem();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
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"/> representation of this instance
|
||||
/// </summary>
|
||||
/// <returns><see cref="RichPresenceStruct"/> representation of this instance</returns>
|
||||
internal RichPresenceStruct GetStruct()
|
||||
{
|
||||
if (_buffers.Count > 0)
|
||||
FreeMem();
|
||||
|
||||
_presence.state = StrToPtr(state, 128);
|
||||
_presence.details = StrToPtr(details, 128);
|
||||
_presence.startTimestamp = startTimestamp;
|
||||
_presence.endTimestamp = endTimestamp;
|
||||
_presence.largeImageKey = StrToPtr(largeImageKey, 32);
|
||||
_presence.largeImageText = StrToPtr(largeImageText, 128);
|
||||
_presence.smallImageKey = StrToPtr(smallImageKey, 32);
|
||||
_presence.smallImageText = StrToPtr(smallImageText, 128);
|
||||
_presence.partyId = StrToPtr(partyId, 128);
|
||||
_presence.partySize = partySize;
|
||||
_presence.partyMax = partyMax;
|
||||
_presence.matchSecret = StrToPtr(matchSecret, 128);
|
||||
_presence.joinSecret = StrToPtr(joinSecret, 128);
|
||||
_presence.spectateSecret = StrToPtr(spectateSecret, 128);
|
||||
_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>
|
||||
/// <param name="maxbytes">Max number of bytes to use</param>
|
||||
/// <returns>Pointer to the UTF-8 representation of <see cref="input"/></returns>
|
||||
private IntPtr StrToPtr(string input, int maxbytes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input)) return IntPtr.Zero;
|
||||
var convstr = StrClampBytes(input, maxbytes);
|
||||
var convbytecnt = Encoding.UTF8.GetByteCount(convstr);
|
||||
var buffer = Marshal.AllocHGlobal(convbytecnt);
|
||||
_buffers.Add(buffer);
|
||||
Marshal.Copy(Encoding.UTF8.GetBytes(convstr), 0, buffer, convbytecnt);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert string to UTF-9 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>
|
||||
/// Clamp the string to the given byte length preserving null termination
|
||||
/// </summary>
|
||||
/// <param name="toclamp">string to clamp</param>
|
||||
/// <param name="maxbytes">max bytes the resulting string should have (including null termination)</param>
|
||||
/// <returns>null terminated string with a byte length less or equal to <see cref="maxbytes"/></returns>
|
||||
private static string StrClampBytes(string toclamp, int maxbytes)
|
||||
{
|
||||
var str = StrToUtf8NullTerm(toclamp);
|
||||
var strbytes = Encoding.UTF8.GetBytes(str);
|
||||
|
||||
if (strbytes.Length <= maxbytes)
|
||||
return str;
|
||||
|
||||
var newstrbytes = new byte[] { };
|
||||
Array.Copy(strbytes, 0, newstrbytes, 0, maxbytes - 1);
|
||||
newstrbytes[newstrbytes.Length - 1] = 0;
|
||||
newstrbytes[newstrbytes.Length - 2] = 0;
|
||||
|
||||
return Encoding.UTF8.GetString(newstrbytes);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96be01509b6882147aa617d033ba98b0
|
||||
timeCreated: 1521121665
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
|
||||
namespace DiscordPresence
|
||||
{
|
||||
public static class DiscordTime
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the current time
|
||||
/// </summary>
|
||||
/// <returns>The current time epoch</returns>
|
||||
public static long TimeNow()
|
||||
{
|
||||
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add time to an epoch value
|
||||
/// </summary>
|
||||
/// <param name="time">Epoch value</param>
|
||||
/// <param name="seconds">Seconds to add</param>
|
||||
/// <param name="minutes">Minutes to add</param>
|
||||
/// <param name="hours">Hours to add</param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("AddTime (ref) is deprecated. Use regular add time method instead.")]
|
||||
public static long AddTime(ref long time, int seconds, int minutes = 0, int hours = 0)
|
||||
{
|
||||
time += seconds;
|
||||
time += minutes * 60;
|
||||
time += hours * 3600;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long AddTime(long time = -1, int seconds = 0, int minutes = 0, int hours = 0)
|
||||
{
|
||||
if (time == -1)
|
||||
time = TimeNow();
|
||||
|
||||
time += seconds;
|
||||
time += minutes * 60;
|
||||
time += hours * 3600;
|
||||
return time;
|
||||
}
|
||||
|
||||
public static long SubtractTime(long time = -1, int seconds = 0, int minutes = 0, int hours = 0)
|
||||
{
|
||||
if (time == -1)
|
||||
time = TimeNow();
|
||||
|
||||
time -= seconds;
|
||||
time -= minutes * 60;
|
||||
time -= hours * 3600;
|
||||
return time;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48f782a8ee45c474daaf8362cec0e7a8
|
||||
timeCreated: 1521143721
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace DiscordPresence
|
||||
{
|
||||
[Serializable]
|
||||
public class DiscordJoinEvent : UnityEvent<string> { }
|
||||
|
||||
[Serializable]
|
||||
public class DiscordSpectateEvent : UnityEvent<string> { }
|
||||
|
||||
[Serializable]
|
||||
public class DiscordJoinRequestEvent : UnityEvent<DiscordRpc.JoinRequest> { }
|
||||
|
||||
public class PresenceManager : MonoBehaviour
|
||||
{
|
||||
public DiscordRpc.RichPresence presence = new DiscordRpc.RichPresence();
|
||||
public string applicationId;
|
||||
public string optionalSteamId;
|
||||
public int callbackCalls;
|
||||
//public int clickCounter;
|
||||
public DiscordRpc.JoinRequest joinRequest;
|
||||
public UnityEvent onConnect;
|
||||
public UnityEvent onDisconnect;
|
||||
public UnityEvent hasResponded;
|
||||
public DiscordJoinEvent onJoin;
|
||||
public DiscordJoinEvent onSpectate;
|
||||
public DiscordJoinRequestEvent onJoinRequest;
|
||||
|
||||
DiscordRpc.EventHandlers handlers;
|
||||
|
||||
public static PresenceManager instance;
|
||||
|
||||
/*public void OnClick()
|
||||
{
|
||||
Debug.Log("Discord: on click!");
|
||||
clickCounter++;
|
||||
|
||||
presence.details = string.Format("Button clicked {0} times", clickCounter);
|
||||
|
||||
DiscordRpc.UpdatePresence(presence);
|
||||
}*/
|
||||
|
||||
public void RequestRespondYes()
|
||||
{
|
||||
Debug.Log("Discord: responding yes to Ask to Join request");
|
||||
DiscordRpc.Respond(joinRequest.userId, DiscordRpc.Reply.Yes);
|
||||
hasResponded.Invoke();
|
||||
}
|
||||
|
||||
public void RequestRespondNo()
|
||||
{
|
||||
Debug.Log("Discord: responding no to Ask to Join request");
|
||||
DiscordRpc.Respond(joinRequest.userId, DiscordRpc.Reply.No);
|
||||
hasResponded.Invoke();
|
||||
}
|
||||
|
||||
#region Discord Callbacks
|
||||
public void ReadyCallback()
|
||||
{
|
||||
++callbackCalls;
|
||||
Debug.Log("Discord: ready");
|
||||
onConnect.Invoke();
|
||||
UpdatePresence(null);
|
||||
}
|
||||
|
||||
public void DisconnectedCallback(int errorCode, string message)
|
||||
{
|
||||
++callbackCalls;
|
||||
Debug.Log(string.Format("Discord: disconnect {0}: {1}", errorCode, message));
|
||||
onDisconnect.Invoke();
|
||||
}
|
||||
|
||||
public void ErrorCallback(int errorCode, string message)
|
||||
{
|
||||
++callbackCalls;
|
||||
Debug.Log(string.Format("Discord: error {0}: {1}", errorCode, message));
|
||||
}
|
||||
|
||||
public void JoinCallback(string secret)
|
||||
{
|
||||
++callbackCalls;
|
||||
Debug.Log(string.Format("Discord: join ({0})", secret));
|
||||
onJoin.Invoke(secret);
|
||||
}
|
||||
|
||||
public void SpectateCallback(string secret)
|
||||
{
|
||||
++callbackCalls;
|
||||
Debug.Log(string.Format("Discord: spectate ({0})", secret));
|
||||
onSpectate.Invoke(secret);
|
||||
}
|
||||
|
||||
public void RequestCallback(ref DiscordRpc.JoinRequest request)
|
||||
{
|
||||
++callbackCalls;
|
||||
Debug.Log(string.Format("Discord: join request {0}#{1}: {2}", request.username, request.discriminator, request.userId));
|
||||
joinRequest = request;
|
||||
onJoinRequest.Invoke(request);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Monobehaviour Callbacks
|
||||
// Singleton
|
||||
void Awake()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
else if (instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
DiscordRpc.RunCallbacks();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
Debug.Log("Discord: init");
|
||||
callbackCalls = 0;
|
||||
|
||||
handlers = new DiscordRpc.EventHandlers();
|
||||
handlers.readyCallback = ReadyCallback;
|
||||
handlers.disconnectedCallback += DisconnectedCallback;
|
||||
handlers.errorCallback += ErrorCallback;
|
||||
handlers.joinCallback += JoinCallback;
|
||||
handlers.spectateCallback += SpectateCallback;
|
||||
handlers.requestCallback += RequestCallback;
|
||||
DiscordRpc.Initialize(applicationId, ref handlers, true, optionalSteamId);
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
Debug.Log("Discord: shutdown");
|
||||
DiscordRpc.Shutdown();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Update Presence Method
|
||||
public static void UpdatePresence(string detail, string state = null, long start = -1, long end = -1, string largeKey = null,string largeText = null,
|
||||
string smallKey = null, string smallText = null, string partyId = null, int size = -1, int max = -1, string match = null, string join = null,
|
||||
string spectate = null/*, bool instance*/)
|
||||
{
|
||||
instance.presence.details = detail ?? instance.presence.details;
|
||||
instance.presence.state = state ?? instance.presence.state;
|
||||
instance.presence.startTimestamp = (start == -1) ? instance.presence.startTimestamp : start;
|
||||
instance.presence.endTimestamp = (end == -1) ? instance.presence.endTimestamp : end;
|
||||
instance.presence.largeImageKey = largeKey ?? instance.presence.largeImageKey;
|
||||
instance.presence.largeImageText = largeText ?? instance.presence.largeImageText;
|
||||
instance.presence.smallImageKey = smallKey ?? instance.presence.smallImageKey;
|
||||
instance.presence.smallImageText = smallText ?? instance.presence.smallImageText;
|
||||
instance.presence.partyId = partyId ?? instance.presence.partyId;
|
||||
instance.presence.partySize = (size == -1) ? instance.presence.partySize : size;
|
||||
instance.presence.partyMax = (max == -1) ? instance.presence.partyMax : max;
|
||||
instance.presence.matchSecret = match ?? instance.presence.matchSecret;
|
||||
instance.presence.joinSecret = join ?? instance.presence.joinSecret;
|
||||
instance.presence.spectateSecret = spectate ?? instance.presence.spectateSecret;
|
||||
//instance.presence.presence.instance =
|
||||
DiscordRpc.UpdatePresence(instance.presence);
|
||||
}
|
||||
|
||||
public static void ClearPresence()
|
||||
{
|
||||
instance.presence.details = "";
|
||||
instance.presence.state = "";
|
||||
instance.presence.startTimestamp = 0;
|
||||
instance.presence.endTimestamp = 0;
|
||||
instance.presence.largeImageKey = "";
|
||||
instance.presence.largeImageText = "";
|
||||
instance.presence.smallImageText = "";
|
||||
instance.presence.smallImageKey = "";
|
||||
instance.presence.partyId = "";
|
||||
instance.presence.partySize = 0;
|
||||
instance.presence.partyMax = 0;
|
||||
instance.presence.matchSecret = "";
|
||||
instance.presence.joinSecret = "";
|
||||
instance.presence.spectateSecret = "";
|
||||
//instance.presence.instance =
|
||||
}
|
||||
|
||||
public static void ClearAndUpdate()
|
||||
{
|
||||
ClearPresence();
|
||||
DiscordRpc.UpdatePresence(instance.presence);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb9ae853100b2944384e0a4d3708f6ba
|
||||
timeCreated: 1521125169
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user