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 { public static string DiffToString(int diff) { string[] diffArr = { " Easy ", " Normal ", " Hard ", " Extreme ", " Extra ", " Tower ", " Dan " }; int level = TJAPlayer3.stage選曲.r確定された曲.nLevel[diff]; return (diffArr[Math.Min(diff, 6)] + "Lv." + level); } private static readonly List _buffers = new List(10); /// /// Discord Rich Presenceの初期化をします。 /// /// Discord APIのクライアントID。 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); } /// /// Discord Rich Presenceの更新をします。 /// /// 現在の説明。 /// 現在の状態。 /// 開始時間(Unix時間) /// 終了時間(Unix時間) /// 小さなアイコン用キー。 /// 小さなアイコンのツールチップに表示するテキスト。 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("logo".ToLowerInvariant()); presence.largeImageText = StrToPtr(TJAPlayer3.AppDisplayThreePartVersion); if (!string.IsNullOrEmpty(smallImageKey)) presence.smallImageKey = StrToPtr(smallImageKey); if (!string.IsNullOrEmpty(smallImageText)) presence.smallImageText = StrToPtr(smallImageText); DiscordRpc.UpdatePresence(ref presence); FreeMem(); } /// /// Discord Rich Presenceのシャットダウンを行います。 /// 終了時に必ず呼び出す必要があります。 /// public static void Shutdown() { DiscordRpc.Shutdown(); Trace.TraceInformation("[Discord] Shutdowned."); } private static void ReadyCallback() { Trace.TraceInformation("[Discord] Ready."); } /// /// Discordとの接続が切断された場合呼び出されます。 /// /// エラーコード。 /// エラーメッセージ。 private static void DisconnectedCallback(int errorCode, string message) { Trace.TraceInformation("[Discord] Disconnected."); } /// /// Discordとの接続でエラーが発生した場合呼び出されます。 /// /// エラーコード。 /// エラーメッセージ。 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); } } /// /// 現在のUnix時間をlong型で返します。 /// /// Unix時間。 public static long GetUnixTime() { return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).Ticks / 10000000; } } }