Skip to content
This repository has been archived by the owner on Feb 1, 2022. It is now read-only.

Commit

Permalink
update DiscordRpc and callback stuff, some docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Tustin committed Nov 20, 2018
1 parent 86b72de commit 6b48ab5
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 129 deletions.
12 changes: 11 additions & 1 deletion PlayStationDiscord/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ public static class Config
private static string ConfigFile => ApplicationDataDirectory + "/config.json";

private static ConfigModel _settings { get; set; } = default(ConfigModel);


/// <summary>
/// ETag hash for games file on GitHub.
/// </summary>
public static string ETag
{
get
Expand All @@ -26,6 +29,9 @@ public static string ETag
}
}

/// <summary>
/// Creates AppData directory is necessary and sets up config file.
/// </summary>
public static void Init()
{
// Create %appdata% directory if necessary.
Expand All @@ -36,6 +42,7 @@ public static void Init()

if (_settings == default(ConfigModel))
{
// If the config file doesn't exist, create it and fill it with default values.
if (!File.Exists(ConfigFile))
{
using (var stream = File.CreateText(ConfigFile))
Expand All @@ -52,6 +59,9 @@ public static void Init()
}
}

/// <summary>
/// Save the config file.
/// </summary>
public static void Save()
{
using (var stream = File.CreateText(ConfigFile))
Expand Down
47 changes: 21 additions & 26 deletions PlayStationDiscord/DiscordController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,63 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static DiscordRpc;

namespace PlayStationDiscord
{
internal class DiscordController
{
public static DiscordRPC.RichPresence presence;
DiscordRPC.EventHandlers handlers;
public static RichPresence presence;
private static EventHandlers handlers;
private static CancellationTokenSource CallbacksCts = new CancellationTokenSource();

public static string PS4ApplicationId = "457775893746810880";
public static string PS3ApplicationId = "459823182044725269";
// Application IDs for each supported platform.
public static string PS4ApplicationId = "457775893746810880";
public static string PS3ApplicationId = "459823182044725269";
public static string VitaApplicationId = "493957159323828259";

public bool Running { get; set; }

/// <summary>
/// Initializes Discord RPC
/// </summary>

public void Initialize(KeyValuePair<DiscordApplicationId, ConsoleInformation> application)
{
handlers = new DiscordRPC.EventHandlers();
handlers.readyCallback = ReadyCallback;
handlers = new EventHandlers();
handlers.readyCallback += ReadyCallback;
handlers.disconnectedCallback += DisconnectedCallback;
handlers.errorCallback += ErrorCallback;
DiscordRPC.Initialize(application.Value.ClientId, ref handlers, true, default(string));
DiscordRpc.Initialize(application.Value.ClientId, ref handlers, true, default(string));
CallbacksCts = new CancellationTokenSource();
Task.Run(RunCallbacksController);
this.Running = true;
}

public void Stop()
{
DiscordRPC.Shutdown();
DiscordRpc.Shutdown();
CallbacksCts.Cancel();
this.Running = false;
}

public void ReadyCallback()
{
var ff = "aa";
Console.WriteLine("Discord RPC is ready!");
}

public void DisconnectedCallback(int errorCode, string message)
{
var ff = errorCode;
Console.WriteLine($"Error: {errorCode} - {message}");
Logger.Write($"Disconnect callback fired: {errorCode} - {message}");
}

public void ErrorCallback(int errorCode, string message)
{
var aa = errorCode;
Console.WriteLine($"Error: {errorCode} - {message}");
Logger.Write($"Error callback fired: {errorCode} - {message}");
}

private static async Task RunCallbacks()
private static async Task RunCallbacksController()
{
await Task.Run(() =>
while (!CallbacksCts.IsCancellationRequested)
{
while (true)
{
//presence.RunCallbacks();
Thread.Sleep(1000);
}
});
DiscordRpc.RunCallbacks();
Thread.Sleep(1000);
}
}
}
}
229 changes: 167 additions & 62 deletions PlayStationDiscord/DiscordRPC.cs
Original file line number Diff line number Diff line change
@@ -1,93 +1,198 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace PlayStationDiscord
public class DiscordRpc
{
public class DiscordRPC
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ReadyCallback(/*ref DiscordUser connectedUser */);

[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 DiscordUser request);

public struct EventHandlers
{
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
public ReadyCallback readyCallback;
public DisconnectedCallback disconnectedCallback;
public ErrorCallback errorCallback;
public JoinCallback joinCallback;
public SpectateCallback spectateCallback;
public RequestCallback requestCallback;
}

[DllImport("discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
public static extern void Shutdown();
[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;
}

[DllImport("discord-rpc", EntryPoint = "Discord_RunCallbacks", CallingConvention = CallingConvention.Cdecl)]
public static extern void RunCallbacks();
[Serializable]
public struct DiscordUser
{
public string userId;
public string username;
public string discriminator;
public string avatar;
}

[DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
public static extern void UpdatePresence(ref RichPresence presence);
public enum Reply
{
No = 0,
Yes = 1,
Ignore = 2
}

[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
public static extern void Respond(string userId, Reply reply);
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ReadyCallback();
[DllImport("discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
public static extern void Shutdown();

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DisconnectedCallback(int errorCode, string message);
[DllImport("discord-rpc", EntryPoint = "Discord_RunCallbacks", CallingConvention = CallingConvention.Cdecl)]
public static extern void RunCallbacks();

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ErrorCallback(int errorCode, string message);
[DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
private static extern void UpdatePresenceNative(ref RichPresenceStruct presence);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void RunCallbacksDel();
[DllImport("discord-rpc", EntryPoint = "Discord_ClearPresence", CallingConvention = CallingConvention.Cdecl)]
public static extern void ClearPresence();

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void JoinCallback(string secret);
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
public static extern void Respond(string userId, Reply reply);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SpectateCallback(string secret);
[DllImport("discord-rpc", EntryPoint = "Discord_UpdateHandlers", CallingConvention = CallingConvention.Cdecl)]
public static extern void UpdateHandlers(ref EventHandlers handlers);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void RequestCallback(JoinRequest request);
public static void UpdatePresence(RichPresence presence)
{
var presencestruct = presence.GetStruct();
UpdatePresenceNative(ref presencestruct);
presence.FreeMem();
}

[System.Serializable, StructLayout(LayoutKind.Sequential)]
public struct RichPresence
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()
{
public IntPtr state; /* max 128 bytes */
public IntPtr details; /* max 128 bytes */
public long startTimestamp;
public long endTimestamp;
public string largeImageKey; /* max 32 bytes */
public IntPtr 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;
if (_buffers.Count > 0)
{
FreeMem();
}

_presence.state = StrToPtr(state);
_presence.details = StrToPtr(details);
_presence.startTimestamp = startTimestamp;
_presence.endTimestamp = endTimestamp;
_presence.largeImageKey = StrToPtr(largeImageKey);
_presence.largeImageText = StrToPtr(largeImageText);
_presence.smallImageKey = StrToPtr(smallImageKey);
_presence.smallImageText = StrToPtr(smallImageText);
_presence.partyId = StrToPtr(partyId);
_presence.partySize = partySize;
_presence.partyMax = partyMax;
_presence.matchSecret = StrToPtr(matchSecret);
_presence.joinSecret = StrToPtr(joinSecret);
_presence.spectateSecret = StrToPtr(spectateSecret);
_presence.instance = instance;

return _presence;
}

public struct EventHandlers
/// <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>
private IntPtr StrToPtr(string input)
{
public ReadyCallback readyCallback;
public DisconnectedCallback disconnectedCallback;
public ErrorCallback errorCallback;
public JoinCallback joinCallback;
public SpectateCallback spectateCallback;
public RequestCallback requestCallback;
if (string.IsNullOrEmpty(input)) return IntPtr.Zero;
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);
}
_buffers.Add(buffer);
Marshal.Copy(Encoding.UTF8.GetBytes(input), 0, buffer, convbytecnt);
return buffer;
}

[Serializable]
public struct JoinRequest
/// <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)
{
public string userId;
public string username;
public string avatar;
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));
}

public enum Reply
/// <summary>
/// Free the allocated memory for conversion to <see cref="RichPresenceStruct"/>
/// </summary>
internal void FreeMem()
{
No = 0,
Yes = 1,
Ignore = 2
for (var i = _buffers.Count - 1; i >= 0; i--)
{
Marshal.FreeHGlobal(_buffers[i]);
_buffers.RemoveAt(i);
}
}
}
}
}
Loading

0 comments on commit 6b48ab5

Please sign in to comment.