using System; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("QOLImprovementsCloverpit")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("QOLImprovementsCloverpit")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("7647f865-26ed-4efd-8e2c-eff13b732133")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace QOLImprovementsCloverpit; [BepInPlugin("com.dylan.cloverpit.discordrpc", "Cloverpit Discord RPC", "2.0.1")] public class QOLImprovementsPlugin : BaseUnityPlugin { private const string PluginGUID = "com.dylan.cloverpit.discordrpc"; private const string PluginName = "Cloverpit Discord RPC"; private const string PluginVersion = "2.0.1"; internal static ManualLogSource logger; private DiscordRpcManager discordManager; private void Awake() { logger = ((BaseUnityPlugin)this).Logger; discordManager = ((Component)this).gameObject.AddComponent(); logger.LogInfo((object)"Cloverpit Discord RPC v2.0.1 loaded!"); } private void OnDestroy() { if ((Object)(object)discordManager != (Object)null) { Object.Destroy((Object)(object)discordManager); } } private void OnApplicationQuit() { if ((Object)(object)discordManager != (Object)null) { discordManager.Shutdown(); } } } public class DiscordRpcManager : MonoBehaviour { private const string DISCORD_APP_ID = "1502207417927208980"; private const float UPDATE_INTERVAL = 3f; private bool initialized = false; private float lastUpdateTime = 0f; private long startTimestamp; private static DiscordRpc.EventHandlers discordEventHandlers = new DiscordRpc.EventHandlers { readyCallback = OnReady, disconnectedCallback = OnDisconnected, errorCallback = OnError }; private void Start() { try { startTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); DiscordRpc.Initialize("1502207417927208980", ref discordEventHandlers, autoRegister: true, null); initialized = true; QOLImprovementsPlugin.logger.LogInfo((object)"Discord RPC initialized successfully!"); UpdatePresence(); } catch (DllNotFoundException) { QOLImprovementsPlugin.logger.LogError((object)"===================================="); QOLImprovementsPlugin.logger.LogError((object)"Discord RPC: discord-rpc.dll NOT FOUND!"); QOLImprovementsPlugin.logger.LogError((object)"===================================="); QOLImprovementsPlugin.logger.LogError((object)"Please download discord-rpc.dll from:"); QOLImprovementsPlugin.logger.LogError((object)"https://github.com/discord/discord-rpc/releases"); QOLImprovementsPlugin.logger.LogError((object)""); QOLImprovementsPlugin.logger.LogError((object)"Extract discord-rpc.dll and place it in:"); QOLImprovementsPlugin.logger.LogError((object)"BepInEx/plugins/discord-rpc.dll"); QOLImprovementsPlugin.logger.LogError((object)"===================================="); } catch (Exception arg) { QOLImprovementsPlugin.logger.LogError((object)$"Failed to initialize Discord RPC: {arg}"); } } private void Update() { if (initialized) { float time = Time.time; if (time - lastUpdateTime >= 3f) { lastUpdateTime = time; UpdatePresence(); } DiscordRpc.RunCallbacks(); } } private void UpdatePresence() { try { Type type = AccessTools.TypeByName("GameplayData"); Type type2 = AccessTools.TypeByName("GameplayMaster"); if (type == null || type2 == null) { return; } object obj = AccessTools.Field(type2, "instance")?.GetValue(null); if (obj == null) { SetPresence("In Menu", "Preparing to gamble", null, null); return; } string numStr = (AccessTools.Method(type, "CoinsGet", (Type[])null, (Type[])null)?.Invoke(null, null))?.ToString() ?? "0"; string numStr2 = (AccessTools.Method(type, "DebtGet", (Type[])null, (Type[])null)?.Invoke(null, null))?.ToString() ?? "0"; string arg = (AccessTools.Method(type, "RoundsLeftToDeadline", (Type[])null, (Type[])null)?.Invoke(null, null))?.ToString() ?? "?"; object obj2 = AccessTools.Method(type, "DebtIndexGet", (Type[])null, (Type[])null)?.Invoke(null, null); int num = 0; if (obj2 != null) { int result; if (obj2 is int num2) { num = num2; } else if (int.TryParse(obj2.ToString(), out result)) { num = result; } } string state = "\ud83d\udcb0 " + FormatNumber(numStr) + " | \ud83d\udc80 " + FormatNumber(numStr2); string details = $"Deadline {num + 1} - {arg} rounds left"; SetPresence(details, state, "cloverpit_logo", "Cloverpit"); } catch (Exception ex) { QOLImprovementsPlugin.logger.LogError((object)("Error updating Discord presence: " + ex.GetType().Name + ": " + ex.Message)); QOLImprovementsPlugin.logger.LogError((object)("Stack trace: " + ex.StackTrace)); } } private string FormatNumber(string numStr) { try { if (numStr.Contains("E") || numStr.Contains("e")) { if (double.TryParse(numStr, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) { return result.ToString("0.00E+000", CultureInfo.InvariantCulture); } return numStr; } if (numStr.Length > 15) { string text = numStr.Substring(0, 1); string text2 = ((numStr.Length > 3) ? numStr.Substring(1, 2) : numStr.Substring(1)); int num = numStr.Length - 1; double num2 = double.Parse(text + "." + text2); return $"{num2:F2}E+{num:D3}"; } if (decimal.TryParse(numStr, NumberStyles.Any, CultureInfo.InvariantCulture, out var result2)) { if (result2 >= 1000000000m) { return ((double)result2).ToString("0.00E+000", CultureInfo.InvariantCulture); } if (result2 >= 1000000m) { return $"{result2 / 1000000m:F2}M"; } if (result2 >= 1000m) { return $"{result2 / 1000m:F2}K"; } return ((long)result2).ToString(); } return numStr; } catch { return numStr ?? "0"; } } private void SetPresence(string details, string state, string largeImageKey, string largeImageText) { DiscordRpc.UpdatePresence(default(DiscordRpc.RichPresence), details, state, largeImageKey, largeImageText, startTimestamp); } public void Shutdown() { if (initialized) { DiscordRpc.Shutdown(); initialized = false; QOLImprovementsPlugin.logger.LogInfo((object)"Discord RPC shutdown"); } } private void OnDestroy() { Shutdown(); } private static void OnReady(ref DiscordRpc.DiscordUser user) { QOLImprovementsPlugin.logger.LogInfo((object)("Discord RPC connected to user: " + user.username + "#" + user.discriminator)); } private static void OnDisconnected(int errorCode, string message) { QOLImprovementsPlugin.logger.LogWarning((object)$"Discord RPC disconnected: {errorCode} - {message}"); } private static void OnError(int errorCode, string message) { QOLImprovementsPlugin.logger.LogError((object)$"Discord RPC error: {errorCode} - {message}"); } } public static class DiscordRpc { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ReadyCallback(ref DiscordUser user); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void DisconnectedCallback(int errorCode, string message); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ErrorCallback(int errorCode, string message); [Serializable] public struct EventHandlers { public ReadyCallback readyCallback; public DisconnectedCallback disconnectedCallback; public ErrorCallback errorCallback; } [Serializable] 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; } [Serializable] public struct DiscordUser { public string userId; public string username; public string discriminator; public string avatar; } [DllImport("discord-rpc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Discord_Initialize")] public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId); [DllImport("discord-rpc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Discord_Shutdown")] public static extern void Shutdown(); [DllImport("discord-rpc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Discord_RunCallbacks")] public static extern void RunCallbacks(); [DllImport("discord-rpc", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Discord_UpdatePresence")] private static extern void UpdatePresenceNative(ref RichPresence presence); public static void UpdatePresence(RichPresence presence, string details = null, string state = null, string largeImageKey = null, string largeImageText = null, long startTimestamp = 0L, string smallImageKey = null, string smallImageText = null) { RichPresence richPresence = default(RichPresence); richPresence.state = ((state != null) ? Marshal.StringToHGlobalAnsi(state) : IntPtr.Zero); richPresence.details = ((details != null) ? Marshal.StringToHGlobalAnsi(details) : IntPtr.Zero); richPresence.startTimestamp = startTimestamp; richPresence.endTimestamp = 0L; richPresence.largeImageKey = ((largeImageKey != null) ? Marshal.StringToHGlobalAnsi(largeImageKey) : IntPtr.Zero); richPresence.largeImageText = ((largeImageText != null) ? Marshal.StringToHGlobalAnsi(largeImageText) : IntPtr.Zero); richPresence.smallImageKey = ((smallImageKey != null) ? Marshal.StringToHGlobalAnsi(smallImageKey) : IntPtr.Zero); richPresence.smallImageText = ((smallImageText != null) ? Marshal.StringToHGlobalAnsi(smallImageText) : IntPtr.Zero); richPresence.partyId = IntPtr.Zero; richPresence.partySize = 0; richPresence.partyMax = 0; richPresence.matchSecret = IntPtr.Zero; richPresence.joinSecret = IntPtr.Zero; richPresence.spectateSecret = IntPtr.Zero; richPresence.instance = false; RichPresence presence2 = richPresence; UpdatePresenceNative(ref presence2); if (presence2.state != IntPtr.Zero) { Marshal.FreeHGlobal(presence2.state); } if (presence2.details != IntPtr.Zero) { Marshal.FreeHGlobal(presence2.details); } if (presence2.largeImageKey != IntPtr.Zero) { Marshal.FreeHGlobal(presence2.largeImageKey); } if (presence2.largeImageText != IntPtr.Zero) { Marshal.FreeHGlobal(presence2.largeImageText); } if (presence2.smallImageKey != IntPtr.Zero) { Marshal.FreeHGlobal(presence2.smallImageKey); } if (presence2.smallImageText != IntPtr.Zero) { Marshal.FreeHGlobal(presence2.smallImageText); } } }