1
0
mirror of synced 2024-11-15 11:23:26 +01:00
OpenTaiko/TJAPlayer3/Common/Discord.cs
2021-09-21 00:16:38 +02:00

181 lines
7.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace TJAPlayer3
{
public static class DiscordRpc
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public delegate void ReadyCallback();
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public delegate void DisconnectedCallback(int errorCode, string message);
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public delegate void ErrorCallback(int errorCode, string message);
public struct EventHandlers
{
public ReadyCallback readyCallback;
public DisconnectedCallback disconnectedCallback;
public ErrorCallback errorCallback;
}
[Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct RichPresence
{
public IntPtr state;
public IntPtr details;
public long startTimestamp;
public long endTimestamp;
public IntPtr largeImageKey;
public IntPtr largeImageText;
public IntPtr smallImageKey;
public IntPtr smallImageText;
public IntPtr partyId;
public int partySize;
public int partyMax;
public IntPtr matchSecret;
public IntPtr joinSecret;
public IntPtr spectateSecret;
public bool instance;
}
[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)]
public static extern void UpdatePresence(ref RichPresence presence);
[DllImport("discord-rpc", EntryPoint = "Discord_ClearPresence", CallingConvention = CallingConvention.Cdecl)]
public static extern void ClearPresence();
[DllImport("discord-rpc", EntryPoint = "Discord_UpdateHandlers", CallingConvention = CallingConvention.Cdecl)]
public static extern void UpdateHandlers(ref EventHandlers handlers);
}
public static class Discord
{
private static readonly List<IntPtr> _buffers = new List<IntPtr>(10);
/// <summary>
/// Discord Rich Presenceの初期化をします。
/// </summary>
/// <param name="clientId">Discord APIのクライアントID。</param>
public static void Initialize(string clientId)
{
var handlers = new DiscordRpc.EventHandlers();
handlers.readyCallback = ReadyCallback;
handlers.disconnectedCallback += DisconnectedCallback;
handlers.errorCallback += ErrorCallback;
DiscordRpc.Initialize(clientId, ref handlers, true, null);
}
/// <summary>
/// Discord Rich Presenceの更新をします。
/// </summary>
/// <param name="details">現在の説明。</param>
/// <param name="state">現在の状態。</param>
/// <param name="startTimeStamp">開始時間(Unix時間)</param>
/// <param name="endTimeStamp">終了時間(Unix時間)</param>
/// <param name="smallImageKey">小さなアイコン用キー。</param>
/// <param name="smallImageText">小さなアイコンのツールチップに表示するテキスト。</param>
public static void UpdatePresence(string details, string state, long startTimeStamp = 0, long endTimeStamp = 0, string smallImageKey = null, string smallImageText = null)
{
var presence = new DiscordRpc.RichPresence();
presence.details = StrToPtr(details);
presence.state = StrToPtr(state);
if (startTimeStamp != 0) presence.startTimestamp = startTimeStamp;
if (endTimeStamp != 0) presence.endTimestamp = endTimeStamp;
presence.largeImageKey = StrToPtr("rewrite");
presence.largeImageText = StrToPtr("Ver." + TJAPlayer3.VERSION);
if (!string.IsNullOrEmpty(smallImageKey)) presence.smallImageKey = StrToPtr(smallImageKey);
if (!string.IsNullOrEmpty(smallImageText)) presence.smallImageText = StrToPtr(smallImageText);
DiscordRpc.UpdatePresence(ref presence);
FreeMem();
}
/// <summary>
/// Discord Rich Presenceのシャットダウンを行います。
/// 終了時に必ず呼び出す必要があります。
/// </summary>
public static void Shutdown()
{
DiscordRpc.Shutdown();
Trace.TraceInformation("[Discord] Shutdowned.");
}
private static void ReadyCallback()
{
Trace.TraceInformation("[Discord] Ready.");
}
/// <summary>
/// Discordとの接続が切断された場合呼び出されます。
/// </summary>
/// <param name="errorCode">エラーコード。</param>
/// <param name="message">エラーメッセージ。</param>
private static void DisconnectedCallback(int errorCode, string message)
{
Trace.TraceInformation("[Discord] Disconnected.");
}
/// <summary>
/// Discordとの接続でエラーが発生した場合呼び出されます。
/// </summary>
/// <param name="errorCode">エラーコード。</param>
/// <param name="message">エラーメッセージ。</param>
private static void ErrorCallback(int errorCode, string message)
{
Trace.TraceInformation("[Discord] Error occured: {0} {1}", errorCode, message);
}
// string型の文字列をポインタで参照させるようにするためのメソッド。
private static IntPtr StrToPtr(string input)
{
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;
}
internal static void FreeMem()
{
for (var i = _buffers.Count - 1; i >= 0; i--)
{
Marshal.FreeHGlobal(_buffers[i]);
_buffers.RemoveAt(i);
}
}
/// <summary>
/// 現在のUnix時間をlong型で返します。
/// </summary>
/// <returns>Unix時間。</returns>
public static long GetUnixTime()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).Ticks / 10000000;
}
}
}