Initial commit

This commit is contained in:
Thastertyn committed 2024-06-07 00:47:07 +02:00
commit 262601edb8
2025 files changed
+218754

No files matched your search

+219
View File
@@ -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);
}
}
}
}
}
+13
View File
@@ -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:
+55
View File
@@ -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;
}
}
}
+13
View File
@@ -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:
+200
View File
@@ -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
}
}
+13
View File
@@ -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: