Big update

Switched from using the official discord rpc library that requires downloading a dll/so to using discord-rpc-csharp
This commit is contained in:
Xpl0itR 2019-05-22 15:18:57 +01:00 committed by Unknown
parent 8332a5e40e
commit 26a905b4f8
4 changed files with 32 additions and 269 deletions

View file

@ -1,241 +0,0 @@
/*
Copyright 2017 Discord, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
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
{
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 64 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 DiscordUser
{
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();
}
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 64 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();
}
_presence.state = StrToPtr(state, 128);
_presence.details = StrToPtr(details, 128);
_presence.startTimestamp = startTimestamp;
_presence.endTimestamp = endTimestamp;
_presence.largeImageKey = StrToPtr(largeImageKey, 64);
_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-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>
/// 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);
}
}
}
}

View file

@ -11,9 +11,9 @@ namespace Ryujinx
{
class Program
{
private static DiscordRpc.RichPresence Presence;
public static DiscordRPC.DiscordRpcClient DiscordClient;
private static DiscordRpc.EventHandlers Handlers;
public static DiscordRPC.RichPresence DiscordPresence;
public static string ApplicationDirectory => AppDomain.CurrentDomain.BaseDirectory;
@ -21,18 +21,18 @@ namespace Ryujinx
{
Console.Title = "Ryujinx Console";
if (File.Exists("./discord-rpc.dll") || File.Exists("./discord-rpc.so"))
{
Handlers = new DiscordRpc.EventHandlers();
Presence = new DiscordRpc.RichPresence();
DiscordRpc.Initialize("568815339807309834", ref Handlers, true, null);
Presence.details = "Ryujinx Console";
Presence.state = "Reading the console logs...";
Presence.startTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
Presence.largeImageKey = "ryujinx";
Presence.largeImageText = "Ryujinx";
DiscordRpc.UpdatePresence(Presence);
}
DiscordClient = new DiscordRPC.DiscordRpcClient("568815339807309834");
DiscordPresence = new DiscordRPC.RichPresence();
DiscordPresence.Assets = new DiscordRPC.Assets();
DiscordPresence.Timestamps = new DiscordRPC.Timestamps(DateTime.UtcNow);
DiscordPresence.Details = "Ryujinx Console";
DiscordPresence.State = "Reading the console logs...";
DiscordPresence.Assets.LargeImageKey = "ryujinx";
DiscordPresence.Assets.LargeImageText = "Ryujinx";
DiscordClient.Initialize();
DiscordClient.SetPresence(DiscordPresence);
IGalRenderer renderer = new OglRenderer();
@ -117,11 +117,15 @@ namespace Ryujinx
audioOut.Dispose();
Logger.Shutdown();
DiscordClient.Dispose();
}
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
Logger.Shutdown();
DiscordClient.Dispose();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
@ -133,26 +137,25 @@ namespace Ryujinx
if (e.IsTerminating)
{
Logger.Shutdown();
DiscordClient.Dispose();
}
}
private static void SetGamePresence(Switch device)
{
if (File.Exists("./discord-rpc.dll") || File.Exists("./discord-rpc.so"))
if (File.ReadAllLines("./RPsupported.dat").Contains(device.System.TitleID))
{
string[] RPsupported = File.ReadAllLines("./RPsupported");
if (RPsupported.Contains(device.System.TitleID))
{
Presence.largeImageKey = device.System.TitleID;
Presence.largeImageText = device.System.TitleName;
}
Presence.details = $"Playing {device.System.TitleName}";
Presence.state = device.System.TitleID.ToUpper();
Presence.smallImageKey = "ryujinx";
Presence.smallImageText = "Ryujinx";
Presence.startTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
DiscordRpc.UpdatePresence(Presence);
DiscordPresence.Assets.LargeImageKey = device.System.TitleID;
DiscordPresence.Assets.LargeImageText = device.System.TitleName;
}
DiscordPresence.Details = $"Playing {device.System.TitleName}";
DiscordPresence.State = device.System.TitleID.ToUpper();
DiscordPresence.Assets.SmallImageKey = "ryujinx";
DiscordPresence.Assets.SmallImageText = "Ryujinx";
DiscordPresence.Timestamps = new DiscordRPC.Timestamps(DateTime.UtcNow);
DiscordClient.SetPresence(DiscordPresence);
}
/// <summary>

View file

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DiscordRichPresence" Version="1.0.108" />
<PackageReference Include="OpenTK.NetStandard" Version="1.0.4" />
</ItemGroup>
@ -23,7 +24,7 @@
<None Update="Config.jsonc">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="RPsupported">
<None Update="RPsupported.dat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>