using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Security; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using BoneLib; using BoneLib.BoneMenu; using BoneLib.Notifications; using DiscordRPC; using DiscordRPC.Events; using DiscordRPC.Exceptions; using DiscordRPC.Helper; using DiscordRPC.IO; using DiscordRPC.Logging; using DiscordRPC.Message; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.Interaction; using Il2CppSLZ.Marrow.Pool; using Il2CppSLZ.Marrow.SceneStreaming; using Il2CppSLZ.Marrow.Utilities; using Il2CppSLZ.Marrow.Warehouse; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using LabFusion.Data; using LabFusion.Entities; using LabFusion.Network; using LabFusion.Player; using LabFusion.SDK.Gamemodes; using LabFusion.SDK.Metadata; using LabFusion.UI.Popups; using LabFusion.Utilities; using LabPresence; using LabPresence.Config; using LabPresence.Managers; using LabPresence.Plugins; using LabPresence.Plugins.Default; using LabPresence.Utilities; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Scriban; using Scriban.Parsing; using Scriban.Runtime; using Semver; using Tomlet.Attributes; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Core), "LabPresence", "1.3.0", "HAHOOS", "https://thunderstore.io/c/bonelab/p/HAHOOS/LabPresence/")] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: MelonPriority(-1000)] [assembly: MelonOptionalDependencies(new string[] { "LabFusion", "DiscordRPC", "Scriban" })] [assembly: MelonPlatform(/*Could not decode attribute arguments.*/)] [assembly: AssemblyTitle("Adds Discord Rich Presence support to BONELAB")] [assembly: AssemblyDescription("Adds Discord Rich Presence support to BONELAB")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("HAHOOS")] [assembly: AssemblyProduct("LabPresence")] [assembly: AssemblyFileVersion("1.3.0")] [assembly: AssemblyInformationalVersion("1.3.0")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("1.3.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LabPresence { public class Core : MelonMod { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static OnJoinEvent <>9__31_4; public static OnJoinRequestedEvent <>9__31_5; public static OnSpectateEvent <>9__31_6; public static Action <>9__31_7; public static Action <>9__31_8; public static Action <>9__31_9; public static Action <>9__31_10; public static Action <>9__31_11; public static Action <>9__31_12; public static Action <>9__31_13; public static Func <>9__35_0; internal void b__31_4(object _, JoinMessage e) { Overwrites.OnJoin.Run(e); } internal void b__31_5(object _, JoinRequestMessage e) { Overwrites.OnJoinRequested.Run(e); } internal void b__31_6(object _, SpectateMessage e) { Overwrites.OnSpectate.Run(e); } internal void b__31_7() { RichPresenceManager.TrySetRichPresence(Config.LevelLoaded, (ActivityType)0); } internal void b__31_8() { RichPresenceManager.TrySetRichPresence(Config.LevelLoading, (ActivityType)0); } internal void b__31_9() { RichPresenceManager.TrySetRichPresence(Config.AssetWarehouseLoaded, (ActivityType)0); } internal void b__31_10() { RichPresenceManager.TrySetRichPresence(Config.PreGameStarted, (ActivityType)0); } internal void b__31_11(LevelCrate _) { if (!FirstLevelLoad) { FirstLevelLoad = true; Thunderstore.BL_SendNotification(); } LevelLaunch = DateTime.Now; ConfigureTimestamp(autoUpdate: true); Overwrites.OnLevelLoaded.Run(); } internal void b__31_12(LevelCrate _) { Overwrites.OnLevelLoading.Run(); } internal void b__31_13(LevelCrate _) { Overwrites.OnLevelUnloaded.Run(); } internal ScriptObject b__35_0() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown ScriptObject val = new ScriptObject((IEqualityComparer)StringComparer.OrdinalIgnoreCase); val.Add("game", (object)new ScribanGame()); val.Add("player", (object)new ScribanPlayer()); val.Add("ammo", (object)new ScribanAmmo()); return val; } } public const string Version = "1.3.0"; private const string ClientID = "1338522973421965382"; private static float _elapsedSecondsDateCheck = 0f; public static DiscordRpcClient Client { get; private set; } internal static Instance Logger { get; private set; } internal static MelonPreferences_ReflectiveCategory Category { get; private set; } internal static DefaultConfig Config { get { MelonPreferences_ReflectiveCategory category = Category; return (category != null) ? category.GetValue() : null; } } public static DateTimeOffset GameLaunch { get; } = DateTimeOffset.Now; public static DateTimeOffset LevelLaunch { get; private set; } = DateTimeOffset.Now; public static Thunderstore Thunderstore { get; private set; } public static bool FirstLevelLoad { get; private set; } private static int LastDay { get; set; } = -1; public override void OnInitializeMelon() { //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Expected O, but got Unknown //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Expected O, but got Unknown //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Expected O, but got Unknown //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Expected O, but got Unknown if (HelperMethods.IsAndroid()) { ((MelonBase)this).LoggerInstance.Error("This mod is not supported as it is unlikely for it to actually work."); ((MelonBase)this).Unregister("Unsupported platform", false); return; } Logger = ((MelonBase)this).LoggerInstance; ((MelonBase)this).LoggerInstance.Msg("Creating preferences"); DirectoryInfo directoryInfo = Directory.CreateDirectory(Path.Combine(MelonEnvironment.UserDataDirectory, "LabPresence")); Category = MelonPreferences.CreateCategory("LabPresenceConfig", "Lab Presence Config"); Category.SetFilePath(Path.Combine(directoryInfo.FullName, "default.cfg"), true, false); Category.SaveToFile(false); ((MelonBase)this).LoggerInstance.Msg("Initializing Thunderstore"); Thunderstore = new Thunderstore("LabPresence / 1.3.0 A BONELAB Mod"); Thunderstore.BL_FetchPackage("LabPresence", "HAHOOS", "1.3.0", ((MelonBase)this).LoggerInstance); ((MelonBase)this).LoggerInstance.Msg("Adding placeholders"); AddDefaultPlaceholders(); ((MelonBase)this).LoggerInstance.Msg("Initializing..."); Client = new DiscordRpcClient("1338522973421965382", -1, (ILogger)null, false, (INamedPipeClient)null) { Logger = (ILogger)(object)new Logger(((MelonBase)this).LoggerInstance, Config.RPCLogLevel, "RPC") }; Client.OnReady += (OnReadyEvent)delegate(object _, ReadyMessage e) { ((MelonBase)this).LoggerInstance.Msg("User @" + e.User.Username + " is ready"); ((MelonBase)this).LoggerInstance.Msg("Registering URI Scheme"); RegisterURIScheme(); Client.SynchronizeState(); }; Client.OnConnectionEstablished += (OnConnectionEstablishedEvent)delegate { ((MelonBase)this).LoggerInstance.Msg("Successfully established connection"); }; Client.OnConnectionFailed += (OnConnectionFailedEvent)delegate(object _, ConnectionFailedMessage e) { ((MelonBase)this).LoggerInstance.Error($"Failed to establish connection with pipe {e.FailedPipe}"); }; Client.OnError += (OnErrorEvent)delegate(object _, ErrorMessage e) { ((MelonBase)this).LoggerInstance.Error("An unexpected error has occurred when sending a message, error: " + e.Message); }; DiscordRpcClient client = Client; object obj = <>c.<>9__31_4; if (obj == null) { OnJoinEvent val = delegate(object _, JoinMessage e) { Overwrites.OnJoin.Run(e); }; <>c.<>9__31_4 = val; obj = (object)val; } client.OnJoin += (OnJoinEvent)obj; DiscordRpcClient client2 = Client; object obj2 = <>c.<>9__31_5; if (obj2 == null) { OnJoinRequestedEvent val2 = delegate(object _, JoinRequestMessage e) { Overwrites.OnJoinRequested.Run(e); }; <>c.<>9__31_5 = val2; obj2 = (object)val2; } client2.OnJoinRequested += (OnJoinRequestedEvent)obj2; DiscordRpcClient client3 = Client; object obj3 = <>c.<>9__31_6; if (obj3 == null) { OnSpectateEvent val3 = delegate(object _, SpectateMessage e) { Overwrites.OnSpectate.Run(e); }; <>c.<>9__31_6 = val3; obj3 = (object)val3; } client3.OnSpectate += (OnSpectateEvent)obj3; Client.SkipIdenticalPresence = true; Client.SetSubscription((EventType)7); Client.Initialize(); Overwrites.OnLevelLoaded.SetDefault(delegate { RichPresenceManager.TrySetRichPresence(Config.LevelLoaded, (ActivityType)0); }); Overwrites.OnLevelLoading.SetDefault(delegate { RichPresenceManager.TrySetRichPresence(Config.LevelLoading, (ActivityType)0); }); Overwrites.OnAssetWarehouseLoaded.SetDefault(delegate { RichPresenceManager.TrySetRichPresence(Config.AssetWarehouseLoaded, (ActivityType)0); }); Overwrites.OnPreGame.SetDefault(delegate { RichPresenceManager.TrySetRichPresence(Config.PreGameStarted, (ActivityType)0); }); try { if (Fusion.HasFusion) { PluginsManager.Register(); } } catch (Exception value) { Logger.Error($"An unexpected error has occurred while attempting to register the Fusion Plugin, exception:\n{value}"); } LevelHooks.OnLevelLoaded = (Action)Delegate.Combine(LevelHooks.OnLevelLoaded, (Action)delegate { if (!FirstLevelLoad) { FirstLevelLoad = true; Thunderstore.BL_SendNotification(); } LevelLaunch = DateTime.Now; ConfigureTimestamp(autoUpdate: true); Overwrites.OnLevelLoaded.Run(); }); LevelHooks.OnLevelLoading = (Action)Delegate.Combine(LevelHooks.OnLevelLoading, (Action)delegate { Overwrites.OnLevelLoading.Run(); }); LevelHooks.OnLevelUnloaded = (Action)Delegate.Combine(LevelHooks.OnLevelUnloaded, (Action)delegate { Overwrites.OnLevelUnloaded.Run(); }); AssetWarehouse.OnReady(Action.op_Implicit((Action)Overwrites.OnAssetWarehouseLoaded.Run)); Overwrites.OnPreGame.Run(); ((MelonBase)this).LoggerInstance.Msg("Creating BoneMenu"); MenuManager.Init(); ((MelonBase)this).LoggerInstance.Msg("Initialized."); } private void RegisterURIScheme() { try { Client.RegisterUriScheme((string)null, (string)null); } catch (Exception value) { ((MelonBase)this).LoggerInstance.Error($"An unexpected error has occurred while registering URI scheme, exception:\n{value}"); } } public static void ConfigureTimestamp(bool autoUpdate = false) { if (Config.TimeMode == TimeMode.CurrentTime) { RichPresenceManager.SetTimestampToCurrentTime(autoUpdate); } else if (Config.TimeMode == TimeMode.GameSession) { RichPresenceManager.SetTimestamp((ulong)GameLaunch.ToUnixTimeMilliseconds(), null, autoUpdate); } else if (Config.TimeMode == TimeMode.Level) { RichPresenceManager.SetTimestamp((ulong)LevelLaunch.ToUnixTimeMilliseconds(), null, autoUpdate); } } public static SpawnableCrate GetInHand(Handedness handType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 Hand val = (((int)handType == 1) ? Player.LeftHand : Player.RightHand); object result; if (val == null) { result = null; } else { HandReciever attachedReceiver = val.AttachedReceiver; if (attachedReceiver == null) { result = null; } else { IGrippable host = attachedReceiver.Host; if (host == null) { result = null; } else { Grip grip = host.GetGrip(); if (grip == null) { result = null; } else { MarrowEntity marrowEntity = grip._marrowEntity; if (marrowEntity == null) { result = null; } else { Poolee poolee = marrowEntity._poolee; result = ((poolee != null) ? poolee.SpawnableCrate : null); } } } } } return (SpawnableCrate)result; } private static void AddDefaultPlaceholders() { PlaceholderManager.RegisterPlaceholder("default", delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown ScriptObject val = new ScriptObject((IEqualityComparer)StringComparer.OrdinalIgnoreCase); val.Add("game", (object)new ScribanGame()); val.Add("player", (object)new ScribanPlayer()); val.Add("ammo", (object)new ScribanAmmo()); return val; }); } public static string CleanLevelName() { StreamSession session = SceneStreamer.Session; object obj; if (session == null) { obj = null; } else { LevelCrate level = session.Level; obj = ((level != null) ? ((Scannable)level).Title : null); } string text = (string)obj; if (text == null) { return "N/A"; } if (Config.RemoveLevelNumbers) { text = RemoveBONELABLevelNumbers(text); } return text; } public static string RemoveUnityRichText(string text) { if (string.IsNullOrWhiteSpace(text)) { return text; } return Regex.Replace(text, "<(.*?)>", string.Empty); } public static string RemoveBONELABLevelNumbers(string levelName) { return Regex.Replace(levelName, "[0-9][0-9] - ", string.Empty); } public override void OnUpdate() { ((MelonBase)this).OnUpdate(); Internal_OnUpdate(); } private static void Internal_OnUpdate() { DiscordRpcClient client = Client; if (client != null && client.IsInitialized) { DiscordRpcClient client2 = Client; if (client2 != null) { client2.Invoke(); } } RichPresenceManager.OnUpdate(); Fps.OnUpdate(); LevelHooks.OnUpdate(); _elapsedSecondsDateCheck += Time.deltaTime; if (LastDay == -1) { LastDay = DateTime.Now.Day; } if (RichPresenceManager.CurrentConfig == null || !(_elapsedSecondsDateCheck >= 5f)) { return; } _elapsedSecondsDateCheck = 0f; if (Config.TimeMode == TimeMode.CurrentTime) { DateTime now = DateTime.Now; if (now.Day != LastDay) { LastDay = now.Day; RichPresenceManager.SetTimestampToCurrentTime(autoUpdate: true); } } } public override void OnApplicationQuit() { DiscordRpcClient client = Client; if (client != null) { client.ClearPresence(); } DiscordRpcClient client2 = Client; if (client2 != null) { client2.Dispose(); } } } public class Logger : ILogger { public LogLevel Level { get; set; } public string Prefix { get; internal set; } private Instance LoggerInstance { get; } public Logger(Instance logger, string prefix) { LoggerInstance = logger; Prefix = prefix; } public Logger(Instance logger, LogLevel level, string prefix) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) LoggerInstance = logger; Level = level; Prefix = prefix; } public void Error(string message, params object[] args) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)Level <= 4) { LoggerInstance.Error("[" + Prefix + "] [ERROR] " + message, args); } } public void Info(string message, params object[] args) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)Level <= 2) { LoggerInstance.Msg("[" + Prefix + "] [INFO] " + message, args); } } public void Trace(string message, params object[] args) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)Level <= 1) { LoggerInstance.Msg("[" + Prefix + "] [TRACE] " + message, args); } } public void Warning(string message, params object[] args) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)Level <= 3) { LoggerInstance.Warning("[" + Prefix + "] [WARN] " + message, args); } } } } namespace LabPresence.Utilities { public static class Fps { private static float updateTime; public static int FramesPerSecond { get; private set; } internal static int FramesRendered { get; private set; } internal static DateTime LastTime { get; private set; } internal static void OnUpdate() { FramesRendered++; updateTime += Time.deltaTime; if (updateTime >= 1f) { updateTime = 0f; FramesPerSecond = FramesRendered; FramesRendered = 0; LastTime = DateTime.Now; } } } public static class ImageConversion { private delegate IntPtr TextureOnlyDelegate(IntPtr tex); private delegate IntPtr TextureAndQualityDelegate(IntPtr tex, int quality); private delegate IntPtr TextureAndFlagDelegate(IntPtr tex, EXRFlags flags); private delegate bool LoadImageDelegate(IntPtr tex, IntPtr data, bool markNonReadable); private const string NullTextureError = "The texture cannot be null."; private static readonly TextureAndFlagDelegate EncodeToEXRDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToEXR"); private static readonly TextureOnlyDelegate EncodeToTGADelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToTGA"); private static readonly TextureOnlyDelegate EncodeToPNGDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToPNG"); private static readonly TextureAndQualityDelegate EncodeToJPGDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::EncodeToJPG"); private static readonly LoadImageDelegate LoadImageDelegateField = IL2CPP.ResolveICall("UnityEngine.ImageConversion::LoadImage"); public static Il2CppStructArray EncodeToTGA(this Texture2D tex) { if ((Object)(object)tex == (Object)null) { throw new ArgumentNullException("tex", "The texture cannot be null."); } if (EncodeToTGADelegateField == null) { throw new InvalidOperationException("The EncodeToTGADelegateField cannot be null."); } IntPtr intPtr = EncodeToTGADelegateField(IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)tex)); if (intPtr != IntPtr.Zero) { return new Il2CppStructArray(intPtr); } return null; } public static Il2CppStructArray EncodeToPNG(this Texture2D tex) { if ((Object)(object)tex == (Object)null) { throw new ArgumentNullException("tex", "The texture cannot be null."); } if (EncodeToPNGDelegateField == null) { throw new InvalidOperationException("The EncodeToPNGDelegateField cannot be null."); } IntPtr intPtr = EncodeToPNGDelegateField(IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)tex)); if (intPtr != IntPtr.Zero) { return new Il2CppStructArray(intPtr); } return null; } public static Il2CppStructArray EncodeToJPG(this Texture2D tex, int quality) { if ((Object)(object)tex == (Object)null) { throw new ArgumentNullException("tex", "The texture cannot be null."); } if (EncodeToJPGDelegateField == null) { throw new InvalidOperationException("The EncodeToJPGDelegateField cannot be null."); } IntPtr intPtr = EncodeToJPGDelegateField(IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)tex), quality); if (intPtr != IntPtr.Zero) { return new Il2CppStructArray(intPtr); } return null; } public static Il2CppStructArray EncodeToJPG(this Texture2D tex) { return tex.EncodeToJPG(75); } public static Il2CppStructArray EncodeToEXR(this Texture2D tex, EXRFlags flags) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)tex == (Object)null) { throw new ArgumentNullException("tex", "The texture cannot be null."); } if (EncodeToEXRDelegateField == null) { throw new InvalidOperationException("The EncodeToEXRDelegateField cannot be null."); } IntPtr intPtr = EncodeToEXRDelegateField(IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)tex), flags); if (intPtr != IntPtr.Zero) { return new Il2CppStructArray(intPtr); } return null; } public static Il2CppStructArray EncodeToEXR(this Texture2D tex) { return tex.EncodeToEXR((EXRFlags)0); } public static bool LoadImage(this Texture2D tex, Il2CppStructArray data, bool markNonReadable) { if ((Object)(object)tex == (Object)null) { throw new ArgumentNullException("tex", "The texture cannot be null."); } if (data == null) { throw new ArgumentNullException("data", "The data cannot be null."); } if (LoadImageDelegateField == null) { throw new InvalidOperationException("The LoadImageDelegateField cannot be null."); } return LoadImageDelegateField(IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)tex), IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)data), markNonReadable); } public static bool LoadImage(this Texture2D tex, Il2CppStructArray data) { return tex.LoadImage(data, markNonReadable: false); } public static Texture2D LoadTexture(string name, byte[] bytes) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown Texture2D val = new Texture2D(2, 2); val.LoadImage(Il2CppStructArray.op_Implicit(bytes), markNonReadable: false); ((Object)val).name = name; ((Object)val).hideFlags = (HideFlags)32; return val; } } public static class LevelHooks { public static LevelCrate CurrentLevel { get { StreamSession session = SceneStreamer.Session; return ((session != null) ? session.Level : null) ?? null; } } public static Action OnLevelLoaded { get; set; } public static Action OnLevelLoading { get; set; } public static Action OnLevelUnloaded { get; set; } internal static LastStatus LastStatus { get; private set; } internal static void OnUpdate() { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Invalid comparison between Unknown and I4 //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Invalid comparison between Unknown and I4 //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Invalid comparison between Unknown and I4 //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Invalid comparison between Unknown and I4 if (!MarrowGame.IsInitialized || SceneStreamer.Session == null || (Object)(object)SceneStreamer.Session.Level == (Object)null) { return; } LastStatus lastStatus = LastStatus; if (lastStatus != null && lastStatus.UpToDate(SceneStreamer.Session.Level, SceneStreamer.Session.Status)) { return; } try { if ((int)SceneStreamer.Session.Status == 2) { OnLevelLoaded?.Invoke(SceneStreamer.Session.Level); } else if ((int)SceneStreamer.Session.Status == 1) { OnLevelLoading?.Invoke(SceneStreamer.Session.Level); } if ((int)SceneStreamer.Session.Status != 2) { LastStatus lastStatus2 = LastStatus; if (lastStatus2 != null && (int)lastStatus2.Status == 2) { OnLevelUnloaded?.Invoke(LastStatus?.Level); } } } finally { LastStatus = new LastStatus(SceneStreamer.Session.Status, SceneStreamer.Session.Level); } } } internal class LastStatus { internal StreamStatus Status { get; set; } internal LevelCrate Level { get; set; } public LastStatus(StreamStatus status, LevelCrate level) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Status = status; Level = level; base..ctor(); } internal bool UpToDate(string barcode, StreamStatus status) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) LevelCrate level = Level; object obj; if (level == null) { obj = null; } else { Barcode barcode2 = ((Scannable)level).Barcode; obj = ((barcode2 != null) ? barcode2.ID : null); } return (string?)obj == barcode && Status == status; } internal bool UpToDate(LevelCrate level, StreamStatus status) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) LevelCrate level2 = Level; object obj; if (level2 == null) { obj = null; } else { Barcode barcode = ((Scannable)level2).Barcode; obj = ((barcode != null) ? barcode.ID : null); } object obj2; if (level == null) { obj2 = null; } else { Barcode barcode2 = ((Scannable)level).Barcode; obj2 = ((barcode2 != null) ? barcode2.ID : null); } return (string?)obj == (string?)obj2 && Status == status; } } public class ScribanCrate { public enum CrateType { Spawnable, Avatar, Level, VFX } public CrateType Type { get; } public string Barcode { get; } public string Title { get; } public string Description { get; } public bool Redacted { get; } public bool Unlockable { get; } public ScriptArray Tags { get; } public ScriptArray BoneTags { get; } public ScribanPallet Pallet { get; } public ScribanCrate(Crate crate, ScribanPallet pallet = null) { Title = ((Scannable)crate).Title; Description = ((Scannable)crate).Description; Redacted = ((Scannable)crate).Redacted; Barcode = ((Scannable)crate).Barcode.ID; Unlockable = ((Scannable)crate).Unlockable; if (crate.Tags == null) { Tags = new ScriptArray(); } else { ScriptArray val = new ScriptArray(); Enumerator enumerator = crate.Tags.GetEnumerator(); while (enumerator.MoveNext()) { string current = enumerator.Current; val.Add(current); } Tags = val; } Pallet = pallet ?? new ScribanPallet(crate.Pallet); if (crate.BoneTags == null || crate.BoneTags.Tags == null) { BoneTags = new ScriptArray(); } else { List scribanBoneTags = new List(); crate.BoneTags.Tags.ForEach(Action.op_Implicit((Action)delegate(BoneTagReference c) { scribanBoneTags.Add(new ScribanBoneTag(((DataCardReference)(object)c).DataCard, Pallet)); })); ScriptArray val2 = new ScriptArray(); foreach (ScribanBoneTag item in scribanBoneTags) { val2.Add(item); } BoneTags = val2; } if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "SpawnableCrate") { Type = CrateType.Spawnable; return; } if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "AvatarCrate") { Type = CrateType.Avatar; return; } if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "LevelCrate") { Type = CrateType.Level; return; } if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "VFXCrate") { Type = CrateType.VFX; return; } throw new ArgumentOutOfRangeException("Crate type " + ((MemberInfo)((Object)crate).GetIl2CppType()).Name + " is not supported."); } } public class ScribanPallet { public string Title { get; } public string Description { get; } public string Author { get; } public string Barcode { get; } public string[] Tags { get; } public bool Redacted { get; } public bool Unlockable { get; } public string Version { get; } public string SDKVersion { get; } public ScriptArray Crates { get; } public ScriptArray ChangeLogs { get; } public ScriptArray DataCards { get; } public string[] Dependencies { get; } public ScribanPallet(Pallet pallet) { Barcode = ((Scannable)pallet).Barcode.ID; Unlockable = ((Scannable)pallet).Unlockable; Redacted = ((Scannable)pallet).Redacted; Title = ((Scannable)pallet).Title; if (pallet.Tags == null) { Tags = Array.Empty(); } else { Tags = Il2CppArrayBase.op_Implicit(pallet.Tags.ToArray()); } Version = pallet.Version; if (pallet.Crates == null) { Crates = new ScriptArray(); } else { List scribanCrates = new List(); pallet.Crates.ForEach(Action.op_Implicit((Action)delegate(Crate c) { scribanCrates.Add(new ScribanCrate(c, this)); })); ScriptArray val = new ScriptArray(); foreach (ScribanCrate item in scribanCrates) { val.Add(item); } Crates = val; } Author = pallet.Author; Description = ((Scannable)pallet).Description; SDKVersion = pallet.SDKVersion; if (pallet.ChangeLogs == null) { ChangeLogs = new ScriptArray(); } else { List list = new List(); Enumerator enumerator2 = pallet.ChangeLogs.GetEnumerator(); while (enumerator2.MoveNext()) { ChangeLog current2 = enumerator2.Current; list.Add(new ScribanChangeLog(current2)); } ScriptArray val2 = new ScriptArray(); foreach (ScribanChangeLog item2 in list) { val2.Add(item2); } ChangeLogs = val2; } if (pallet.DataCards == null) { DataCards = new ScriptArray(); } else { List scribanDataCards = new List(); pallet.DataCards.ForEach(Action.op_Implicit((Action)delegate(DataCard c) { scribanDataCards.Add(new ScribanDataCard(c, this)); })); ScriptArray val3 = new ScriptArray(); foreach (ScribanDataCard item3 in scribanDataCards) { val3.Add(item3); } DataCards = val3; } if (pallet.PalletDependencies == null) { Dependencies = Array.Empty(); return; } List dependencies = new List(); pallet.PalletDependencies.ForEach(Action.op_Implicit((Action)delegate(PalletReference p) { dependencies.Add(((ScannableReference)p).Barcode.ID); })); Dependencies = dependencies.ToArray(); } } public class ScribanChangeLog { public string Title { get; } = changelog.title; public string Version { get; } = changelog.version; public string Text { get; } = changelog.text; public ScribanChangeLog(ChangeLog changelog) { } } public class ScribanDataCard { public string Title { get; } = ((Scannable)dataCard).Title; public string Description { get; } = ((Scannable)dataCard).Description; public string Barcode { get; } = ((Scannable)dataCard).Barcode.ID; public bool Redacted { get; } = ((Scannable)dataCard).Redacted; public bool Unlockable { get; } = ((Scannable)dataCard).Unlockable; public ScribanPallet Pallet { get; } = pallet ?? new ScribanPallet(dataCard.Pallet); public ScribanDataCard(DataCard dataCard, ScribanPallet pallet = null) { } } public class ScribanBoneTag { public string Title { get; } = ((Scannable)boneTag).Title; public string Description { get; } = ((Scannable)boneTag).Description; public string Barcode { get; } = ((Scannable)boneTag).Barcode.ID; public bool Redacted { get; } = ((Scannable)boneTag).Redacted; public bool Unlockable { get; } = ((Scannable)boneTag).Unlockable; public ScribanPallet Pallet { get; } = pallet ?? new ScribanPallet(((DataCard)boneTag).Pallet); public ScribanBoneTag(BoneTag boneTag, ScribanPallet pallet = null) { } } public class ScribanAmmo : ScriptObject { public static int Light => GetAmmo("light"); public static int Medium => GetAmmo("medium"); public static int Heavy => GetAmmo("heavy"); public static int GetAmmo(string type) { AmmoInventory instance = AmmoInventory.Instance; return (instance != null) ? instance._groupCounts[type] : 0; } } public class ScribanPlayer : ScriptObject { public static ScribanCrate Avatar { get { RigManager rigManager = Player.RigManager; object result; if (!((Object)(object)((rigManager == null) ? null : ((CrateReferenceT)(object)rigManager.AvatarCrate)?.Crate) != (Object)null)) { result = null; } else { RigManager rigManager2 = Player.RigManager; result = new ScribanCrate((Crate)(object)((rigManager2 == null) ? null : ((CrateReferenceT)(object)rigManager2.AvatarCrate)?.Crate)); } return (ScribanCrate)result; } } public static ScribanCrate LeftHand => ((Object)(object)Core.GetInHand((Handedness)1) != (Object)null) ? new ScribanCrate((Crate)(object)Core.GetInHand((Handedness)1)) : null; public static ScribanCrate RightHand => ((Object)(object)Core.GetInHand((Handedness)2) != (Object)null) ? new ScribanCrate((Crate)(object)Core.GetInHand((Handedness)2)) : null; public static float Health { get { RigManager rigManager = Player.RigManager; float? obj; if (rigManager == null) { obj = null; } else { Health health = rigManager.health; obj = ((health != null) ? new float?(health.curr_Health) : null); } float? num = obj; return num.GetValueOrDefault(); } } public static float MaxHealth { get { RigManager rigManager = Player.RigManager; float? obj; if (rigManager == null) { obj = null; } else { Health health = rigManager.health; obj = ((health != null) ? new float?(health.max_Health) : null); } float? num = obj; return num.GetValueOrDefault(); } } public static float HealthPercentange => Health / MaxHealth * 100f; } public class ScribanGame : ScriptObject { public static ScribanCrate Level { get { StreamSession session = SceneStreamer.Session; object result; if (!((Object)(object)((session != null) ? session.Level : null) != (Object)null)) { result = null; } else { StreamSession session2 = SceneStreamer.Session; result = new ScribanCrate((Crate)(object)((session2 != null) ? session2.Level : null)); } return (ScribanCrate)result; } } public static string LevelName => Core.CleanLevelName(); public static string MLVersion => AppDomain.CurrentDomain?.GetAssemblies()?.FirstOrDefault((Assembly x) => x.GetName().Name == "MelonLoader")?.GetName()?.Version?.ToString() ?? "N/A"; public static int FPS => Fps.FramesPerSecond; public static string OperatingSystem => SystemInfo.operatingSystem; public static int ModsCount { get { int result = 0; if (AssetWarehouse.ready && AssetWarehouse.Instance != null) { result = AssetWarehouse.Instance.PalletCount() - AssetWarehouse.Instance.gamePallets.Count; } return result; } } public static int CodeModsCount => MelonTypeBase.RegisteredMelons.Count; } public class ScribanUtils : ScriptObject { public static ScribanPallet GetPallet(string barcode) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown Pallet pallet = default(Pallet); if (AssetWarehouse.Instance.TryGetPallet(new Barcode(barcode), ref pallet)) { return new ScribanPallet(pallet); } return null; } public static string CleanString(string str) { return Core.RemoveUnityRichText(str); } } public class Thunderstore { public readonly string UserAgent; private Package _fetchedPackage; private bool _isLatestVersion; private string _currentVersion; public bool IsV1Deprecated { get; set; } public Thunderstore(string userAgent) { UserAgent = userAgent; } public Thunderstore(string userAgent, bool isV1Deprecated) : this(userAgent) { IsV1Deprecated = isV1Deprecated; } public Thunderstore() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); if (executingAssembly != null) { AssemblyName name = executingAssembly.GetName(); if (name != null) { UserAgent = $"{name.Name} / {name.Version} C# Application"; } } } public Thunderstore(bool isV1Deprecated) { IsV1Deprecated = isV1Deprecated; Assembly executingAssembly = Assembly.GetExecutingAssembly(); if (executingAssembly != null) { AssemblyName name = executingAssembly.GetName(); if (name != null) { UserAgent = $"{name.Name} / {name.Version}"; } } } public void BL_FetchPackage(string name, string author, string currentVersion, Instance logger = null) { if (_fetchedPackage != null) { return; } _currentVersion = currentVersion; try { _fetchedPackage = GetPackage(author, name); if (_fetchedPackage == null && logger != null) { logger.Warning("Could not find Thunderstore package for " + name); } if (string.IsNullOrWhiteSpace(_fetchedPackage.Latest?.Version) && logger != null) { logger.Warning("Latest version could not be found or the version is empty"); } _isLatestVersion = _fetchedPackage.IsLatestVersion(currentVersion); if (!_isLatestVersion) { if (logger != null) { logger.Warning($"A new version of {name} is available: v{_fetchedPackage.Latest.Version} while the current is v{currentVersion}. It is recommended that you update"); } } else if (SemVersion.Parse(currentVersion, false) == _fetchedPackage.Latest.SemanticVersion) { if (logger != null) { logger.Msg($"Latest version of {name} is installed! (v{currentVersion})"); } } else if (logger != null) { logger.Msg($"Beta release of {name} is installed (v{_fetchedPackage.Latest.Version} is newest, v{currentVersion} is installed)"); } } catch (ThunderstorePackageNotFoundException) { if (logger != null) { logger.Warning("Could not find Thunderstore package for " + name); } } catch (Exception ex2) { if (logger != null) { logger.Error("An unexpected error has occurred while trying to check if " + name + " is the latest version", ex2); } } } public void BL_CreateMenuLabel(Page page, bool createBlankSpace = true) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (_fetchedPackage != null) { if (createBlankSpace) { ((Element)page.CreateFunction("", Color.white, (Action)null)).SetProperty((ElementProperties)1); } ((Element)page.CreateFunction("Current Version: v" + _currentVersion + ((_isLatestVersion || _fetchedPackage == null) ? string.Empty : "
(Update available!)"), Color.white, (Action)null)).SetProperty((ElementProperties)1); } } public void BL_SendNotification() { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown if (_fetchedPackage != null && !_isLatestVersion) { NotificationText message = default(NotificationText); ((NotificationText)(ref message))..ctor($"There is a new version of {_fetchedPackage.Name}. Go to Thunderstore and download the latest version which is v{_fetchedPackage.Latest.Version}", Color.white, true); Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Update!"), Message = message, PopupLength = 5f, ShowTitleOnPopup = true, Type = (NotificationType)1 }); } } public Package GetPackage(string @namespace, string name, string version = null) { Package package = SendRequest($"https://thunderstore.io/api/experimental/package/{@namespace}/{name}/{version ?? string.Empty}"); if (!IsV1Deprecated && package != null) { V1PackageMetrics packageMetrics = GetPackageMetrics(@namespace, name); if (packageMetrics != null) { package.TotalDownloads = packageMetrics.Downloads; package.RatingScore = packageMetrics.RatingScore; } } return package; } private static bool IsTaskGood(Task task) where T : class { return task != null && task.IsCompletedSuccessfully && task.Result != null; } public T SendRequest(string url) { HttpClientHandler handler = new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Manual, ServerCertificateCustomValidationCallback = (HttpRequestMessage _, X509Certificate2? _, X509Chain? _, SslPolicyErrors _) => true }; HttpClient httpClient = new HttpClient(handler); httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); Task async = httpClient.GetAsync(url); async.Wait(); HttpResponseMessage httpResponseMessage = async?.Result; if (IsTaskGood(async)) { if (!httpResponseMessage.IsSuccessStatusCode) { HandleHttpError(httpResponseMessage); return default(T); } Task task = httpResponseMessage.Content.ReadAsStringAsync(); task.Wait(); string text = task?.Result; if (IsTaskGood(task)) { return JsonConvert.DeserializeObject(text); } } return default(T); } private static void HandleHttpError(HttpResponseMessage result) { if (IsThunderstoreError(result, out var details)) { if (IsPackageNotFound(result, details)) { throw new ThunderstorePackageNotFoundException("Thunderstore could not find the package"); } throw new ThunderstoreErrorException("Thunderstore API has thrown an unexpected error!", result); } result.EnsureSuccessStatusCode(); } public V1PackageMetrics GetPackageMetrics(string @namespace, string name) { if (IsV1Deprecated) { return null; } return SendRequest($"https://thunderstore.io/api/v1/package-metrics/{@namespace}/{name}/"); } public bool IsLatestVersion(string @namespace, string name, string currentVersion) { SemVersion currentVersion2 = default(SemVersion); if (SemVersion.TryParse(currentVersion, ref currentVersion2, false)) { return IsLatestVersion(@namespace, name, currentVersion2); } return false; } public bool IsLatestVersion(string @namespace, string name, Version currentVersion) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown return IsLatestVersion(@namespace, name, new SemVersion(currentVersion)); } public bool IsLatestVersion(string @namespace, string name, SemVersion currentVersion) { if (!IsV1Deprecated) { return GetPackageMetrics(@namespace, name)?.IsLatestVersion(currentVersion) ?? false; } return GetPackage(@namespace, name)?.IsLatestVersion(currentVersion) ?? false; } private static bool IsPackageNotFound(HttpResponseMessage response, string details = "") { if (response.StatusCode != HttpStatusCode.NotFound) { return false; } if (details != null && details.Length == 0) { details = GetDetails(response); } return string.Equals(details, "Not found.", StringComparison.OrdinalIgnoreCase); } private static bool IsThunderstoreError(HttpResponseMessage response, out string details) { details = GetDetails(response); return !string.IsNullOrWhiteSpace(details); } private static string GetDetails(HttpResponseMessage response) { if (response.IsSuccessStatusCode) { return null; } Task task = response.Content.ReadAsStringAsync(); task.Wait(); string result = task.Result; if (string.IsNullOrWhiteSpace(result)) { return null; } ThunderstoreErrorResponse thunderstoreErrorResponse; try { thunderstoreErrorResponse = JsonConvert.DeserializeObject(result); } catch (JsonException) { return null; } if (!string.IsNullOrWhiteSpace(thunderstoreErrorResponse?.Details)) { return thunderstoreErrorResponse.Details; } return null; } } public class Package { [JsonProperty("namespace")] public string Namespace { get; internal set; } [JsonProperty("name")] public string Name { get; internal set; } [JsonProperty("full_name")] public string FullName { get; internal set; } [JsonProperty("owner")] public string Owner { get; internal set; } [JsonProperty("package_url")] public string PackageURL { get; internal set; } [JsonProperty("date_created")] public DateTime CreatedAt { get; internal set; } [JsonProperty("date_updated")] public DateTime UpdatedAt { get; internal set; } [JsonProperty("rating_score")] public int RatingScore { get; internal set; } [JsonProperty("is_pinned")] public bool IsPinned { get; internal set; } [JsonProperty("is_deprecated")] public bool IsDeprecated { get; internal set; } [JsonProperty("total_downloads")] public int TotalDownloads { get; internal set; } [JsonProperty("latest")] public PackageVersion Latest { get; internal set; } [JsonProperty("community_listings")] public PackageListing[] CommunityListings { get; internal set; } public bool IsLatestVersion(string current) { if (string.IsNullOrWhiteSpace(current)) { return false; } if (Latest == null || Latest.SemanticVersion == (SemVersion)null) { return false; } SemVersion val = default(SemVersion); if (SemVersion.TryParse(current, ref val, false)) { return val >= Latest.SemanticVersion; } return false; } public bool IsLatestVersion(SemVersion current) { if (current == (SemVersion)null) { return false; } if (Latest == null || Latest.SemanticVersion == (SemVersion)null) { return false; } return current >= Latest.SemanticVersion; } public bool IsLatestVersion(Version current) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown if (current == null) { return false; } if (Latest == null || Latest.SemanticVersion == (SemVersion)null) { return false; } return new SemVersion(current) >= Latest.SemanticVersion; } } public class PackageVersion { [JsonProperty("namespace")] public string Namespace { get; internal set; } [JsonProperty("name")] public string Name { get; internal set; } [JsonProperty("version_number")] public string Version { get { return ((object)SemanticVersion).ToString(); } internal set { SemanticVersion = SemVersion.Parse(value, false); } } [JsonIgnore] public SemVersion SemanticVersion { get; internal set; } [JsonProperty("full_name")] public string FullName { get; internal set; } [JsonProperty("description")] public string Description { get; internal set; } [JsonProperty("icon")] public string Icon { get; internal set; } [JsonProperty("dependencies")] public List Dependencies { get; internal set; } [JsonProperty("download_url")] public string DownloadURL { get; internal set; } [JsonProperty("date_created")] public DateTime CreatedAt { get; internal set; } [JsonProperty("downloads")] public int Downloads { get; internal set; } [JsonProperty("website_url")] public string WebsiteURL { get; internal set; } [JsonProperty("is_active")] public bool IsActive { get; internal set; } } public class PackageListing { public enum ReviewStatus { UNREVIEWED, APPROVED, REJECTED } [JsonProperty("has_nsfw_content")] public bool HasNSFWContent { get; internal set; } [JsonProperty("categories")] public List Categories { get; internal set; } [JsonProperty("community")] public string Community { get; internal set; } [JsonProperty("review_status")] public string ReviewStatusString { get { return ReviewStatusValue.ToString(); } internal set { if (value == null) { throw new ArgumentNullException("value"); } if (string.Equals(value, "unreviewed", StringComparison.OrdinalIgnoreCase)) { ReviewStatusValue = ReviewStatus.UNREVIEWED; } else if (string.Equals(value, "approved", StringComparison.OrdinalIgnoreCase)) { ReviewStatusValue = ReviewStatus.APPROVED; } else if (string.Equals(value, "rejected", StringComparison.OrdinalIgnoreCase)) { ReviewStatusValue = ReviewStatus.REJECTED; } } } [JsonIgnore] public ReviewStatus ReviewStatusValue { get; internal set; } } public class V1PackageMetrics { [JsonProperty("downloads")] public int Downloads { get; internal set; } [JsonProperty("rating_score")] public int RatingScore { get; internal set; } [JsonProperty("latest_version")] public string LatestVersion { get { return ((object)LatestSemanticVersion).ToString(); } internal set { LatestSemanticVersion = SemVersion.Parse(value, false); } } [JsonIgnore] public SemVersion LatestSemanticVersion { get; internal set; } public bool IsLatestVersion(string current) { if (string.IsNullOrWhiteSpace(current)) { return false; } if (LatestSemanticVersion == (SemVersion)null) { return false; } SemVersion val = default(SemVersion); if (SemVersion.TryParse(current, ref val, false)) { return val >= LatestSemanticVersion; } return false; } public bool IsLatestVersion(SemVersion current) { if (current == (SemVersion)null) { return false; } if (LatestSemanticVersion == (SemVersion)null) { return false; } return current >= LatestSemanticVersion; } public bool IsLatestVersion(Version current) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown if (current == null) { return false; } if (LatestSemanticVersion == (SemVersion)null) { return false; } return new SemVersion(current) >= LatestSemanticVersion; } } public class ThunderstoreErrorResponse { [JsonProperty("detail")] public string Details { get; internal set; } } public class ThunderstoreErrorException : Exception { public string Details { get; } public HttpStatusCode HttpStatusCode { get; } public ThunderstoreErrorException() { } public ThunderstoreErrorException(string message) : base(message) { } public ThunderstoreErrorException(string message, Exception innerException) : base(message, innerException) { } public ThunderstoreErrorException(string message, string details, HttpStatusCode httpStatusCode, Exception innerException) : base(message, innerException) { Details = details; HttpStatusCode = httpStatusCode; } public ThunderstoreErrorException(string message, HttpResponseMessage response) : base(message) { if (response.IsSuccessStatusCode) { return; } HttpStatusCode = response.StatusCode; Task task = response.Content.ReadAsStringAsync(); task.Wait(); string result = task.Result; if (string.IsNullOrWhiteSpace(result)) { Details = string.Empty; return; } ThunderstoreErrorResponse thunderstoreErrorResponse; try { thunderstoreErrorResponse = JsonConvert.DeserializeObject(result); } catch (JsonException) { Details = string.Empty; return; } if (thunderstoreErrorResponse != null) { Details = thunderstoreErrorResponse.Details; } } } public class ThunderstorePackageNotFoundException : ThunderstoreErrorException { public string Namespace { get; } public string Name { get; } public string Version { get; } public ThunderstorePackageNotFoundException(string message, string @namespace, string name, string details, HttpStatusCode httpStatusCode, Exception innerException) : base(message, details, httpStatusCode, innerException) { Namespace = @namespace; Name = name; } public ThunderstorePackageNotFoundException(string message, string @namespace, string name, string version, string details, HttpStatusCode httpStatusCode, Exception innerException) : base(message, details, httpStatusCode, innerException) { Namespace = @namespace; Name = name; Version = version; } public ThunderstorePackageNotFoundException(string message, string @namespace, string name, HttpResponseMessage response) : base(message, response) { Namespace = @namespace; Name = name; } public ThunderstorePackageNotFoundException(string message, string @namespace, string name, string version, HttpResponseMessage response) : base(message, response) { Namespace = @namespace; Name = name; Version = version; } public ThunderstorePackageNotFoundException() { } public ThunderstorePackageNotFoundException(string message) : base(message) { } public ThunderstorePackageNotFoundException(string message, Exception innerException) : base(message, innerException) { } public ThunderstorePackageNotFoundException(string message, string details, HttpStatusCode httpStatusCode, Exception innerException) : base(message, details, httpStatusCode, innerException) { } public ThunderstorePackageNotFoundException(string message, HttpResponseMessage response) : base(message, response) { } } } namespace LabPresence.Plugins { public static class Overwrites { public static OverwritesType OnLevelLoaded { get; } = new OverwritesType(); public static OverwritesType OnLevelLoading { get; } = new OverwritesType(); public static OverwritesType OnLevelUnloaded { get; } = new OverwritesType(); public static OverwritesType OnAssetWarehouseLoaded { get; } = new OverwritesType(); public static OverwritesType OnPreGame { get; } = new OverwritesType(); public static OverwritesType OnJoin { get; } = new OverwritesType(); public static OverwritesType OnJoinRequested { get; } = new OverwritesType(); public static OverwritesType OnSpectate { get; } = new OverwritesType(); } public class OverwritesType where T : new() { private readonly List> _Overwrites = new List>(); public IReadOnlyCollection> Overwrites => _Overwrites.AsReadOnly(); internal Action Default { get; private set; } internal void SetDefault(Action action) { Default = action; } public void RegisterOverwrite(Overwrite overwrite) { if (IsRegistered(overwrite)) { throw new ArgumentException("An overwrite with the same ID is already registered!"); } _Overwrites.Add(overwrite); } public void RegisterOverwrite(Func callback, out Guid ID, int priority = 0) { Overwrite overwrite = new Overwrite(callback, priority); RegisterOverwrite(overwrite); ID = overwrite.ID; } public bool RemoveOverwrite(Guid ID) { return _Overwrites.RemoveAll((Overwrite x) => x.ID == ID) > 0; } public bool RemoveOverwrite(Overwrite overwrite) { return RemoveOverwrite(overwrite.ID); } public bool IsRegistered(Guid ID) { return Overwrites.Any((Overwrite x) => x.ID == ID); } public bool IsRegistered(Overwrite overwrite) { return IsRegistered(overwrite.ID); } public void Run(T arg) { if (_Overwrites.Count == 0) { Default?.Invoke(arg); return; } List> list = Overwrites.OrderByDescending((Overwrite x) => x.Priority).ToList(); for (int i = 0; i < list.Count; i++) { if (list.Count == 0) { break; } Overwrite overwrite = list[0]; if (overwrite == null) { list.RemoveAt(0); continue; } Func callback = overwrite.Callback; if (callback == null || !callback(arg)) { list.RemoveAt(0); continue; } return; } Default?.Invoke(arg); } } public class Overwrite where T : new() { public int Priority { get; set; } public Guid ID { get; set; } public Func Callback { get; set; } public Overwrite(Guid id, Func callback, int priority = 0) { ID = id; Priority = priority; Callback = callback; } public Overwrite(Func callback, int priority = 0) { ID = Guid.NewGuid(); Priority = priority; Callback = callback; } } public class OverwritesType { private readonly List _Overwrites = new List(); public IReadOnlyCollection Overwrites => _Overwrites.AsReadOnly(); internal Action Default { get; private set; } internal void SetDefault(Action action) { Default = action; } public void RegisterOverwrite(Overwrite overwrite) { if (IsRegistered(overwrite)) { throw new ArgumentException("An overwrite with the same ID is already registered!"); } _Overwrites.Add(overwrite); } public void RegisterOverwrite(Func callback, out Guid ID, int priority = 0) { Overwrite overwrite = new Overwrite(callback, priority); RegisterOverwrite(overwrite); ID = overwrite.ID; } public bool RemoveOverwrite(Guid ID) { return _Overwrites.RemoveAll((Overwrite x) => x.ID == ID) > 0; } public bool RemoveOverwrite(Overwrite overwrite) { return RemoveOverwrite(overwrite.ID); } public bool IsRegistered(Guid ID) { return Overwrites.Any((Overwrite x) => x.ID == ID); } public bool IsRegistered(Overwrite overwrite) { return IsRegistered(overwrite.ID); } public void Run() { if (_Overwrites.Count == 0) { Default?.Invoke(); return; } List list = Overwrites.OrderByDescending((Overwrite x) => x.Priority).ToList(); for (int i = 0; i < list.Count; i++) { if (list.Count == 0) { break; } Overwrite overwrite = list[0]; if (overwrite == null) { list.RemoveAt(0); continue; } Func callback = overwrite.Callback; if (callback == null || !callback()) { list.RemoveAt(0); continue; } return; } Default?.Invoke(); } } public class Overwrite { public int Priority { get; set; } public Guid ID { get; set; } public Func Callback { get; set; } public Overwrite(Guid id, Func callback, int priority = 0) { ID = id; Priority = priority; Callback = callback; } public Overwrite(Func callback, int priority = 0) { ID = Guid.NewGuid(); Priority = priority; Callback = callback; } } public interface IPlugin { string Name { get; } SemVersion Version { get; } string Author { get; } bool CreatesMenu => false; Color MenuColor => Color.white; Logger Logger { get; internal set; } Page MenuPage { get; internal set; } void Internal_Init(); internal void Internal_PopulateMenu() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (MenuPage == null && CreatesMenu) { MenuPage = MenuManager.PluginsPage.CreatePage(Name, MenuColor, 0, true); ((Element)MenuPage.CreateFunction($"Version: v{Version}", Color.white, (Action)null)).SetProperty((ElementProperties)1); ((Element)MenuPage.CreateFunction("Author: " + Author, Color.white, (Action)null)).SetProperty((ElementProperties)1); ((Element)MenuPage.CreateFunction(string.Empty, Color.white, (Action)null)).SetProperty((ElementProperties)1); PopulateMenu(MenuPage); } } void PopulateMenu(Page page) { } } public abstract class Plugin : IPlugin { public abstract string Name { get; } public abstract SemVersion Version { get; } public abstract string Author { get; } public virtual bool CreatesMenu => false; public virtual Color MenuColor => Color.white; public MelonPreferences_Category Category { get; private set; } public Logger Logger { get; set; } public Page MenuPage { get; set; } public void Internal_Init() { Logger = new Logger(Core.Logger, Name); Category = MelonPreferences.CreateCategory("LabPresence_" + Name + "_Config"); Category.SetFilePath(Path.Combine(MelonEnvironment.UserDataDirectory, "LabPresence", Name.ToLower() + ".cfg")); Category.SaveToFile(false); Init(); } public virtual void Init() { } public virtual void PopulateMenu(Page page) { } } public abstract class Plugin : IPlugin where ConfigT : new() { public abstract string Name { get; } public abstract SemVersion Version { get; } public abstract string Author { get; } public Logger Logger { get; set; } public Page MenuPage { get; set; } public virtual bool CreatesMenu => false; public virtual Color MenuColor => Color.white; public MelonPreferences_ReflectiveCategory Category { get; private set; } public void Internal_Init() { Logger = new Logger(Core.Logger, Name); Category = MelonPreferences.CreateCategory("LabPresence_" + Name + "_Config", (string)null); Category.SetFilePath(Path.Combine(MelonEnvironment.UserDataDirectory, "LabPresence", Name.ToLower() + ".cfg"), true, true); Category.SaveToFile(false); Init(); } public virtual void Init() { } public virtual void PopulateMenu(Page page) { } public ConfigT GetConfig() { return Category.GetValue(); } } public static class PluginsManager { private static readonly List _Plugins = new List(); public static IReadOnlyCollection Plugins => _Plugins.AsReadOnly(); public static void Register(Type plugin) { IPlugin pluginFromType = GetPluginFromType(plugin); if (IsRegistered(plugin)) { throw new ArgumentException("A plugin with the same name is already registered!"); } Core.Logger.Msg($"Plugin '{pluginFromType.Name}' v{pluginFromType.Version} by {pluginFromType.Author} has been registered!"); _Plugins.Add(pluginFromType); pluginFromType.Internal_Init(); if (MenuManager.IsInitialized) { pluginFromType.Internal_PopulateMenu(); } } public static void Register() where PluginT : IPlugin { Register(typeof(PluginT)); } public static void Unregister(string name) { List list = new List(_Plugins); list.ForEach(delegate(IPlugin x) { if (!(x.Name != name)) { if (x.MenuPage != null) { Menu.DestroyPage(x.MenuPage); } _Plugins.Remove(x); } }); } public static void Unregister(Type plugin) { Unregister(GetPluginFromType(plugin).Name); } public static void Unregister() where PluginT : IPlugin { Unregister(GetPluginFromType(typeof(PluginT)).Name); } public static bool IsRegistered(string name) { return Plugins.Any((IPlugin x) => x.Name == name); } public static bool IsRegistered(Type plugin) { return IsRegistered(GetPluginFromType(plugin).Name); } public static bool IsRegistered() where PluginT : IPlugin { return IsRegistered(GetPluginFromType(typeof(PluginT)).Name); } private static IPlugin GetPluginFromType(Type plugin) { if (!plugin.IsTypeOf()) { throw new ArgumentException("Type must be a sub class of IPlugin!", "plugin"); } return (IPlugin)Activator.CreateInstance(plugin); } public static bool IsTypeOf(this Type type) { return typeof(T).IsAssignableFrom(type); } } } namespace LabPresence.Plugins.Default { internal class FusionPlugin : Plugin { [CompilerGenerated] private static class <>O { public static ServerEvent <0>__OnDisconnect; } [CompilerGenerated] private sealed class d__24 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Action callback; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__24(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } StreamSession session = SceneStreamer.Session; if (session == null || (int)session.Status != 2) { <>2__current = null; <>1__state = 1; return true; } callback?.Invoke(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private float ElapsedSeconds = 0f; private bool turnedOff = false; public override string Name => "Fusion"; public override SemVersion Version => new SemVersion(1, 0, 0, "", ""); public override string Author => "HAHOOS"; public override bool CreatesMenu => true; public override Color MenuColor => Color.cyan; internal static FusionPlugin Instance { get; set; } public override void Init() { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown Instance = this; if (!Fusion.HasFusion) { base.Logger.Error("LabFusion is not installed, so FusionPlugin will not be set up!"); return; } PlaceholderManager.RegisterPlaceholder("labfusion", delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown ScriptObject val = new ScriptObject((IEqualityComparer)StringComparer.OrdinalIgnoreCase); val.Add("fusion", (object)new ScribanFusion()); return val; }); Overwrites.OnLevelLoaded.RegisterOverwrite(OnLevelLoaded, out var ID, 100); Overwrites.OnLevelLoaded.RegisterOverwrite(OnLevelLoading, out ID, 100); Overwrites.OnJoin.RegisterOverwrite(Join, out ID, 100); Overwrites.OnJoinRequested.RegisterOverwrite(JoinRequested, out ID, 100); ((MelonEventBase)(object)MelonEvents.OnUpdate).Subscribe(new LemonAction(Update), 0, false); HasFusion(); } public override void PopulateMenu(Page page) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) ((Element)page.CreateBool("Override Time", Color.yellow, GetConfig().OverrideTimeToLobby, (Action)delegate(bool val) { GetConfig().OverrideTimeToLobby = val; base.Category.SaveToFile(false); Fusion.SetTimestamp(setLobbyLaunch: false); })).SetTooltip("If the time mode will be set to 'Level', when in a fusion lobby it will override the time to display how long you are in the lobby instead of the level"); ((Element)page.CreateBool("Show Join Request Pop Up", Color.cyan, GetConfig().ShowJoinRequestPopUp, (Action)delegate(bool val) { GetConfig().ShowJoinRequestPopUp = val; base.Category.SaveToFile(false); })).SetTooltip("If true, a notification will be shown when someone requests to join your server"); ((Element)page.CreateBool("Allow Players To Invite", Color.white, GetConfig().AllowPlayersToInvite, (Action)delegate(bool val) { GetConfig().AllowPlayersToInvite = val; base.Category.SaveToFile(false); })).SetTooltip("If true, when hosting a private server, players will be able to let others join the server through Discord"); ((Element)page.CreateBool("Show Custom Gamemode Tooltips", MenuManager.FromRGB(191, 255, 0), GetConfig().ShowCustomGamemodeToolTips, (Action)delegate(bool val) { GetConfig().ShowCustomGamemodeToolTips = val; base.Category.SaveToFile(false); })).SetTooltip("If true, gamemodes that support custom tooltips will display custom text on the small icon. Disabling this option will cause the tooltip to only show the name of the gamemode"); ((Element)page.CreateBool("Joins", Color.cyan, GetConfig().Joins, (Action)delegate(bool val) { GetConfig().Joins = val; base.Category.SaveToFile(false); })).SetTooltip("If true, the rich presence will allow discord users to join your server when available, otherwise if false, the join button will never be shown"); } private void HasFusion() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown Fusion.Init(base.Logger); object obj = <>O.<0>__OnDisconnect; if (obj == null) { ServerEvent val = OnDisconnect; <>O.<0>__OnDisconnect = val; obj = (object)val; } MultiplayerHooking.OnDisconnected += (ServerEvent)obj; } private static void OnDisconnect() { if (RichPresenceManager.OverrideTimestamp?.Origin == "fusion") { RichPresenceManager.ResetOverrideTimestamp(); } Overwrites.OnLevelLoaded.Run(); } private bool JoinRequested(JoinRequestMessage message) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown try { base.Logger.Info("Join requested"); MelonCoroutines.Start(AfterLevelLoaded(after)); } catch (Exception value) { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Failure | LabPresence"), Message = NotificationText.op_Implicit("An unexpected error has occurred while handling join request, check the console or logs for more details"), Type = (NotificationType)2, PopupLength = 5f, ShowTitleOnPopup = true }); base.Logger.Error($"An unexpected error has occurred while handling join request, exception:\n{value}"); return false; } return true; void after() { Fusion.JoinRequest(message); } } private bool OnLevelLoaded() { if (Fusion.IsConnected) { RichPresenceManager.TrySetRichPresence(GetConfig().LevelLoaded, (ActivityType)0, GetParty(), GetSecrets()); } return Fusion.IsConnected; } private bool OnLevelLoading() { if (Fusion.IsConnected) { RichPresenceManager.TrySetRichPresence(GetConfig().LevelLoading, (ActivityType)0, GetParty(), GetSecrets()); } return Fusion.IsConnected; } private bool Join(JoinMessage e) { if (!Fusion.HasFusion) { return false; } try { base.Logger.Info("Received Join Request"); string text = RichPresenceManager.Decrypt(e.Secret); string[] array = text.Split("|"); if (array.Length <= 1) { throw new ArgumentException("Secret provided to join the lobby did not include all of the necessary info"); } if (array.Length > 2) { throw new ArgumentException("Secret provided to join the lobby was invalid, the name of the network layer or code to the server may have contained the '|' character used to separate network layer & code, causing unexpected results"); } string layer = array[0]; string code = array[1]; MelonCoroutines.Start(AfterLevelLoaded(join)); void join() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown base.Logger.Info("Attempting to join with the following code: " + code); if (code != Fusion.GetLobbyCode()) { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("LabPresence"), Message = NotificationText.op_Implicit("Attempting to join the target lobby, this might take a few seconds..."), PopupLength = 4f, ShowTitleOnPopup = true, Type = (NotificationType)0 }); if (Fusion.EnsureNetworkLayer(layer)) { Fusion.JoinByCode(code); } else { Fusion.ErrorNotif("Failed to ensure network layer, check the console/logs for errors. If none are present, it's likely the user is playing on a network layer that you do not have.", 5f); } } else { base.Logger.Error("Player is already in the lobby"); Fusion.ErrorNotif("Could not join, because you are already in the lobby!", 5f); } } } catch (Exception value) { Fusion.ErrorNotif("An unexpected error has occurred while trying to join the lobby, check the console or logs for more details", 5f); base.Logger.Error($"An unexpected error has occurred while trying to join the lobby, exception:\n{value}"); return false; } return true; } private static Party GetParty() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown if (!Fusion.IsConnected) { return null; } ulong lobbyID = Fusion.GetLobbyID(); if (lobbyID == 0L || lobbyID.ToString().Length < 2) { return null; } return new Party { ID = Fusion.GetLobbyID().ToString(), Privacy = (PrivacySetting)((Fusion.GetPrivacy() == Fusion.ServerPrivacy.Public) ? 1 : 0), Max = Fusion.GetPlayerCount().max, Size = Fusion.GetPlayerCount().current }; } private static Secrets GetSecrets() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown if (!Fusion.IsConnected) { return null; } if ((int)SceneStreamer.Session.Status == 1) { return null; } var (num, num2) = Fusion.GetPlayerCount(); if (num >= num2) { return null; } switch (Fusion.GetPrivacy()) { case Fusion.ServerPrivacy.Locked: return null; default: if (!Fusion.IsAllowedToInvite()) { return null; } break; case Fusion.ServerPrivacy.Public: case Fusion.ServerPrivacy.Friends_Only: break; } string currentNetworkLayerTitle = Fusion.GetCurrentNetworkLayerTitle(); if (string.IsNullOrWhiteSpace(currentNetworkLayerTitle)) { return null; } string lobbyCode = Fusion.GetLobbyCode(); if (string.IsNullOrWhiteSpace(lobbyCode)) { return null; } string joinSecret = RichPresenceManager.Encrypt(currentNetworkLayerTitle + "|" + lobbyCode); return new Secrets { JoinSecret = joinSecret }; } [IteratorStateMachine(typeof(d__24))] private static IEnumerator AfterLevelLoaded(Action callback) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__24(0) { callback = callback }; } private void Update() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Invalid comparison between Unknown and I4 if (base.Category != null && GetConfig() != null) { Fusion.EnsureMetaDataSync(); } ElapsedSeconds += Time.deltaTime; if (!(ElapsedSeconds >= 5f)) { return; } ElapsedSeconds = 0f; if (Fusion.IsConnected) { StreamSession session = SceneStreamer.Session; if (session != null && (int)session.Status == 2) { RichPresenceManager.AutoUpdate = false; turnedOff = true; var (key, tooltip) = Fusion.GetGamemodeRPC(); RichPresenceManager.TrySetRichPresence(RichPresenceManager.CurrentConfig, (ActivityType)0, GetParty(), GetSecrets(), null, new Asset(key, tooltip)); return; } } if (!RichPresenceManager.AutoUpdate && turnedOff) { RichPresenceManager.AutoUpdate = true; turnedOff = false; } } } public static class Fusion { public enum ServerPrivacy { Unknown = -1, Public, Private, Friends_Only, Locked } [CompilerGenerated] private static class <>O { public static ServerEvent <0>__Update; public static ServerEvent <1>__OnLobby; public static Func <2>__HideAndSeekTooltip; public static Func <3>__DeathmatchTooltip; public static Func <4>__TeamDeathmatchTooltip; public static Func <5>__EntangledTooltip; public static Func <6>__SmashBonesTooltip; public static Func <7>__JuggernautTooltip; } private const string AllowKey = "LabPresence.AllowInvites"; private static Logger Logger; internal static DateTimeOffset LobbyLaunch { get; set; } public static bool HasFusion => MelonBase.FindMelon("LabFusion", "Lakatrazz") != null; public static bool IsConnected { get { if (HasFusion) { return Internal_IsConnected(); } return false; } } public static bool IsGamemodeStarted { get { if (!IsConnected) { return false; } return Internal_IsGamemodeStarted(); } } private static bool Internal_IsGamemodeStarted() { return GamemodeManager.IsGamemodeStarted; } internal static bool Internal_IsConnected() { return NetworkInfo.HasServer; } public static string GetLobbyName() { if (!IsConnected) { return "N/A"; } return Internal_GetLobbyName(); } public static string GetPermissionLevel() { if (!IsConnected) { return "N/A"; } return Internal_GetPermissionLevel(); } private static string Internal_GetPermissionLevel() { return LocalPlayer.Metadata.PermissionLevel.GetValue(); } internal static string Internal_GetLobbyName() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; string text = ((lobbyInfo != null) ? lobbyInfo.LobbyName : null); if (lobbyInfo == null) { return "N/A"; } return string.IsNullOrWhiteSpace(text) ? (lobbyInfo.LobbyHostName + "'s lobby") : text; } public static string GetHost() { if (!IsConnected) { return "N/A"; } return Internal_GetHost(); } internal static string Internal_GetHost() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; string text = ((lobbyInfo != null) ? lobbyInfo.LobbyHostName : null); if (lobbyInfo == null) { return "N/A"; } return string.IsNullOrWhiteSpace(text) ? "N/A" : text; } public static (int current, int max) GetPlayerCount() { if (!IsConnected) { return (-1, -1); } return Internal_GetPlayerCount(); } private static (int current, int max) Internal_GetPlayerCount() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return (-1, -1); } int playerCount = lobbyInfo.PlayerCount; int maxPlayers = lobbyInfo.MaxPlayers; return (playerCount, maxPlayers); } public static ServerPrivacy GetPrivacy() { if (!IsConnected) { return ServerPrivacy.Unknown; } return Internal_GetPrivacy(); } private static ServerPrivacy Internal_GetPrivacy() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected I4, but got Unknown LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return ServerPrivacy.Unknown; } ServerPrivacy privacy = lobbyInfo.Privacy; return (ServerPrivacy)privacy; } public static ulong GetLobbyID() { if (!IsConnected) { return 0uL; } return Internal_GetLobbyID(); } private static ulong Internal_GetLobbyID() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return 0uL; } return lobbyInfo.LobbyID; } public static string GetLobbyCode() { if (!IsConnected) { return string.Empty; } return Internal_GetLobbyCode(); } private static string Internal_GetLobbyCode() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return string.Empty; } return lobbyInfo.LobbyCode; } public static (string username, string displayName) GetCurrentPlayer() { if (!IsConnected) { return (null, null); } return Internal_GetCurrentPlayer(); } private static (string username, string displayName) Internal_GetCurrentPlayer() { string username = LocalPlayer.Username; string item = default(string); if (MetadataHelper.TryGetDisplayName(PlayerIDManager.LocalID, ref item)) { item = username; } return (username, item); } public static string GetCurrentNetworkLayerTitle() { if (!IsConnected) { return null; } return Internal_GetCurrentNetworkLayerTitle(); } private static string Internal_GetCurrentNetworkLayerTitle() { NetworkLayer layer = NetworkLayerManager.Layer; return (layer != null) ? layer.Title : null; } public static void EnsureMetaDataSync() { if (IsConnected) { Internal_EnsureMetadataSync(); } } internal static void Internal_EnsureMetadataSync() { string value = default(string); bool result; if (!NetworkInfo.IsHost) { LocalPlayer.Metadata.Metadata.TryRemoveMetadata("LabPresence.AllowInvites"); } else if (!LocalPlayer.Metadata.Metadata.TryGetMetadata("LabPresence.AllowInvites", ref value) || !bool.TryParse(value, out result) || result != FusionPlugin.Instance.GetConfig().AllowPlayersToInvite) { LocalPlayer.Metadata.Metadata.TrySetMetadata("LabPresence.AllowInvites", FusionPlugin.Instance.GetConfig().AllowPlayersToInvite.ToString()); } } public static bool IsAllowedToInvite() { if (!IsConnected) { return false; } return Internal_IsAllowedToInvite(); } private static bool Internal_IsAllowedToInvite() { if (!FusionPlugin.Instance.GetConfig().Joins) { return false; } if (NetworkInfo.IsHost) { return true; } if (PlayerIDManager.GetHostID() == null) { return true; } NetworkPlayer val = default(NetworkPlayer); if (!NetworkPlayerManager.TryGetPlayer(PlayerID.op_Implicit(PlayerIDManager.GetHostID()), ref val)) { return true; } if (val == null) { return true; } PlayerID playerID = val.PlayerID; object value; if (playerID == null) { value = null; } else { PlayerMetadata metadata = playerID.Metadata; if (metadata == null) { value = null; } else { NetworkMetadata metadata2 = metadata.Metadata; value = ((metadata2 != null) ? metadata2.GetMetadata("LabPresence.AllowInvites") : null); } } if (string.IsNullOrWhiteSpace((string?)value)) { return true; } PlayerID playerID2 = val.PlayerID; object obj; if (playerID2 == null) { obj = null; } else { PlayerMetadata metadata3 = playerID2.Metadata; if (metadata3 == null) { obj = null; } else { NetworkMetadata metadata4 = metadata3.Metadata; obj = ((metadata4 != null) ? metadata4.GetMetadata("LabPresence.AllowInvites") : null); } } return (string?)obj == bool.TrueString; } public static bool EnsureNetworkLayer(string title) { if (!HasFusion) { return false; } return Internal_EnsureNetworkLayer(title); } private static bool Internal_EnsureNetworkLayer(string title) { if (!NetworkLayer.LayerLookup.TryGetValue(title, out var value)) { Logger.Error("Could find network layer '" + title + "'"); return false; } try { if (NetworkLayerManager.LoggedIn && NetworkLayerManager.Layer == value) { return true; } if (NetworkLayerManager.LoggedIn) { NetworkLayerManager.LogOut(); } NetworkLayerManager.LogIn(value); } catch (Exception value2) { Logger.Error($"An unexpected error has occurred while ensuring fusion is on the right network layer, exception:\n{value2}"); return false; } return true; } public static void JoinByCode(string code) { if (HasFusion && !string.IsNullOrWhiteSpace(code)) { Internal_JoinByCode(code); } } private static void Internal_JoinByCode(string code) { if (string.Equals(NetworkHelper.GetServerCode(), code, StringComparison.OrdinalIgnoreCase)) { ErrorNotif("You are already in the lobby!"); return; } if (NetworkLayerManager.Layer.Matchmaker != null) { NetworkLayerManager.Layer.Matchmaker.RequestLobbiesByCode(code, (Action)delegate(MatchmakerCallbackInfo x) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) AttemptJoin(x, code); }); return; } if (IsConnected) { NetworkHelper.Disconnect("Joining another lobby"); } NetworkHelper.JoinServerByCode(code); } private static void AttemptJoin(MatchmakerCallbackInfo x, string code) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Invalid comparison between Unknown and I4 //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Invalid comparison between Unknown and I4 LobbyInfo val = x.Lobbies.FirstOrDefault(); LobbyInfo targetLobby = ((LobbyMetadataInfo)(ref val.Metadata)).LobbyInfo; if (targetLobby == null || targetLobby.LobbyCode == null) { Core.Logger.Error("The lobby was not found"); ErrorNotif("The lobby you wanted to join was not found!"); return; } if ((int)targetLobby.Privacy == 2) { PlayerList playerList = targetLobby.PlayerList; PlayerInfo val2 = ((playerList == null) ? null : playerList.Players?.FirstOrDefault((Func)((PlayerInfo x) => x.Username == targetLobby.LobbyHostName))); if (val2 == null) { Core.Logger.Warning("Could not find host, unable to verify if you can join the lobby (Privacy: Friends Only)"); } else if (!NetworkLayerManager.Layer.IsFriend(val2.PlatformID)) { Core.Logger.Error("The lobby is friends only and you are not friends with the host, cannot join"); ErrorNotif("Cannot join the lobby, because it is friends only and you are not friends with the host!"); return; } } if ((int)targetLobby.Privacy == 3) { Core.Logger.Error("The lobby is locked, cannot join"); ErrorNotif("Cannot join the lobby, because it is locked"); return; } if (IsConnected) { NetworkHelper.Disconnect("Joining another lobby"); } NetworkHelper.JoinServerByCode(code); } internal static void ErrorNotif(string msg, float length = 3.5f) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Error | FLB"), Message = NotificationText.op_Implicit(msg), PopupLength = length, Type = (NotificationType)2 }); } internal static void JoinRequest(JoinRequestMessage message) { if (IsConnected && message != null) { Internal_JoinRequest(message); } } private static void Internal_JoinRequest(JoinRequestMessage message) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (message != null) { FusionConfig config = FusionPlugin.Instance.GetConfig(); if (config != null && config.ShowJoinRequestPopUp) { Texture2D customIcon = (Texture2D)(((object)RichPresenceManager.GetAvatar(message.User, (AvatarSize)512)) ?? ((object)new Texture2D(1, 1))); Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Join Request"), Message = NotificationText.op_Implicit(message.User.DisplayName + " (@" + message.User.Username + ") wants to join you! Go to the LabFusion menu to accept or deny the request"), PopupLength = 5f, ShowTitleOnPopup = true, Type = (NotificationType)4, CustomIcon = customIcon }); } Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Join Request"), Message = NotificationText.op_Implicit(message.User.DisplayName + " (@" + message.User.Username + ") wants to join you!"), PopupLength = 5f, SaveToMenu = true, ShowPopup = false, Type = (NotificationType)0, OnAccepted = delegate { Core.Client.Respond(message, true); }, OnDeclined = delegate { Core.Client.Respond(message, false); } }); } } internal static void Init(Logger logger) { if (HasFusion) { Internal_Init(logger); } } private static void Internal_Init(Logger logger) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown Logger = logger; object obj = <>O.<0>__Update; if (obj == null) { ServerEvent val = Update; <>O.<0>__Update = val; obj = (object)val; } MultiplayerHooking.OnDisconnected -= (ServerEvent)obj; object obj2 = <>O.<0>__Update; if (obj2 == null) { ServerEvent val2 = Update; <>O.<0>__Update = val2; obj2 = (object)val2; } MultiplayerHooking.OnDisconnected += (ServerEvent)obj2; object obj3 = <>O.<1>__OnLobby; if (obj3 == null) { ServerEvent val3 = OnLobby; <>O.<1>__OnLobby = val3; obj3 = (object)val3; } MultiplayerHooking.OnJoinedServer += (ServerEvent)obj3; object obj4 = <>O.<1>__OnLobby; if (obj4 == null) { ServerEvent val4 = OnLobby; <>O.<1>__OnLobby = val4; obj4 = (object)val4; } MultiplayerHooking.OnStartedServer += (ServerEvent)obj4; GamemodeManager.OnGamemodeStarted += delegate { RichPresenceManager.SetOverrideTimestamp(new TimestampOverride(GetGamemodeOverrideTime(), "fusion"), autoUpdate: true); }; GamemodeManager.OnGamemodeStopped += delegate { if (RichPresenceManager.OverrideTimestamp?.Origin == "fusion") { RichPresenceManager.ResetOverrideTimestamp(autoUpdate: true); } }; GamemodeManager.RegisterGamemode("Lakatrazz.Hide And Seek", (Func)HideAndSeekTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Deathmatch", (Func)DeathmatchTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Team Deathmatch", (Func)TeamDeathmatchTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Entangled", (Func)EntangledTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Smash Bones", (Func)SmashBonesTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Juggernaut", (Func)JuggernautTooltip); } private static void OnLobby() { if (IsConnected) { SetTimestamp(); } } private static string JuggernautTooltip() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown Gamemode activeGamemode = GamemodeManager.ActiveGamemode; if (((activeGamemode != null) ? activeGamemode.Barcode : null) != "Lakatrazz.Juggernaut") { return string.Empty; } Juggernaut val = (Juggernaut)GamemodeManager.ActiveGamemode; Team localTeam = val.TeamManager.GetLocalTeam(); PlayerID localID = PlayerIDManager.LocalID; return $"{((localTeam != null) ? localTeam.DisplayName : null) ?? "N/A"} | #{val.JuggernautScoreKeeper.GetPlace(localID)} place with {val.JuggernautScoreKeeper.GetScore(localID)} points!"; } private static string SmashBonesTooltip() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown Gamemode activeGamemode = GamemodeManager.ActiveGamemode; if (((activeGamemode != null) ? activeGamemode.Barcode : null) != "Lakatrazz.Smash Bones") { return string.Empty; } SmashBones val = (SmashBones)GamemodeManager.ActiveGamemode; PlayerID localID = PlayerIDManager.LocalID; return $"#{val.PlayerScoreKeeper.GetPlace(localID)} place with {val.PlayerScoreKeeper.GetScore(localID)} points! | {val.PlayerStocksKeeper.GetScore(localID)} stocks left"; } private static string HideAndSeekTooltip() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown Gamemode activeGamemode = GamemodeManager.ActiveGamemode; if (((activeGamemode != null) ? activeGamemode.Barcode : null) != "Lakatrazz.Hide And Seek") { return string.Empty; } HideAndSeek val = (HideAndSeek)GamemodeManager.ActiveGamemode; Team localTeam = val.TeamManager.GetLocalTeam(); return $"{((localTeam != null) ? localTeam.DisplayName : null) ?? "N/A"} | {val.HiderTeam.PlayerCount} hiders left!"; } private static string DeathmatchTooltip() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown Gamemode activeGamemode = GamemodeManager.ActiveGamemode; if (((activeGamemode != null) ? activeGamemode.Barcode : null) != "Lakatrazz.Deathmatch") { return string.Empty; } Deathmatch val = (Deathmatch)GamemodeManager.ActiveGamemode; PlayerID localID = PlayerIDManager.LocalID; return $"#{val.ScoreKeeper.GetPlace(localID)} place with {val.ScoreKeeper.GetScore(localID)} points!"; } private static string TeamDeathmatchTooltip() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown Gamemode activeGamemode = GamemodeManager.ActiveGamemode; if (((activeGamemode != null) ? activeGamemode.Barcode : null) != "Lakatrazz.Team Deathmatch") { return string.Empty; } TeamDeathmatch val = (TeamDeathmatch)GamemodeManager.ActiveGamemode; Team localTeam = val.TeamManager.GetLocalTeam(); int score = ((ScoreKeeper)(object)val.ScoreKeeper).GetScore(localTeam); int num = ((ScoreKeeper)(object)val.ScoreKeeper).GetTotalScore() - score; string value = ((localTeam != null) ? Core.RemoveUnityRichText(localTeam.DisplayName) : "N/A"); string value2 = ((score > num) ? "winning!" : ((score >= num) ? "neither winning or losing.." : "losing :(")); return $"Team '{value}' with {score} points and {value2}"; } private static string EntangledTooltip() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown Gamemode activeGamemode = GamemodeManager.ActiveGamemode; if (((activeGamemode != null) ? activeGamemode.Barcode : null) != "Lakatrazz.Entangled") { return string.Empty; } Entangled val = (Entangled)GamemodeManager.ActiveGamemode; string text = default(string); if (!((Gamemode)val).Metadata.TryGetMetadata($"InternalEntangledMetadata.Partner.{PlayerIDManager.LocalPlatformID}", ref text) || text == "-1") { return "With no partner :("; } if (!ulong.TryParse(text, out var result)) { return "With no partner :("; } PlayerID playerID = PlayerIDManager.GetPlayerID(result); if (playerID == null) { return "With no partner :("; } string text2 = default(string); if (!MetadataHelper.TryGetDisplayName(playerID, ref text2)) { return "With no partner :("; } return "Entangled with " + Core.RemoveUnityRichText(text2); } public static void SetTimestamp(bool setLobbyLaunch = true) { if (setLobbyLaunch) { LobbyLaunch = DateTimeOffset.Now; } if (FusionPlugin.Instance.GetConfig().OverrideTimeToLobby && Core.Config.TimeMode == TimeMode.Level) { RichPresenceManager.SetTimestamp((ulong)LobbyLaunch.ToUnixTimeMilliseconds(), null, autoUpdate: true); } else { Core.ConfigureTimestamp(autoUpdate: true); } } public static Timestamp GetGamemodeOverrideTime() { if (!IsConnected) { return null; } return Internal_GetGamemodeOverrideTime(); } private static Timestamp Internal_GetGamemodeOverrideTime() { if (!GamemodeManager.IsGamemodeStarted) { return null; } if (GamemodeManager.ActiveGamemode == null) { return null; } Gamemode activeGamemode = GamemodeManager.ActiveGamemode; if (GamemodeManager.GetGamemode(activeGamemode.Barcode)?.OverrideTime == null) { return null; } return GamemodeManager.GetOverrideTime(activeGamemode.Barcode); } public static (string key, string tooltip) GetGamemodeRPC() { if (!IsConnected) { return (null, null); } return Internal_GetGamemodeRPC(); } private static (string key, string tooltip) Internal_GetGamemodeRPC() { if (!GamemodeManager.IsGamemodeStarted) { return (null, null); } if (GamemodeManager.ActiveGamemode == null) { return (null, null); } Gamemode activeGamemode = GamemodeManager.ActiveGamemode; GamemodeOverrides gamemode = GamemodeManager.GetGamemode(activeGamemode.Barcode); string text = ((gamemode != null && gamemode.CustomToolTip != null) ? GamemodeManager.GetToolTipValue(activeGamemode.Barcode) : string.Empty); if (FusionPlugin.Instance.GetConfig().ShowCustomGamemodeToolTips) { return (GamemodeManager.GetGamemodeKey(activeGamemode.Barcode), (!string.IsNullOrWhiteSpace(text)) ? (activeGamemode.Title + " | " + text) : activeGamemode.Title); } return (GamemodeManager.GetGamemodeKey(activeGamemode.Barcode), activeGamemode.Title); } private static void Update() { if (Core.Config.TimeMode == TimeMode.Level && FusionPlugin.Instance.GetConfig().OverrideTimeToLobby && !IsConnected) { Core.ConfigureTimestamp(autoUpdate: true); } if (RichPresenceManager.CurrentConfig == FusionPlugin.Instance.GetConfig().LevelLoaded && !IsConnected) { Overwrites.OnLevelLoaded.Run(); } else if (RichPresenceManager.CurrentConfig == FusionPlugin.Instance.GetConfig().LevelLoading && !IsConnected) { Overwrites.OnLevelLoading.Run(); } } } public class ScribanFusion : ScriptObject { public static string Username => Fusion.GetCurrentPlayer().username; public static string DisplayName => Fusion.GetCurrentPlayer().displayName; public static string LobbyName => Fusion.GetLobbyName(); public static ulong LobbyID => Fusion.GetLobbyID(); public static string Host => Fusion.GetHost(); public static string PermissionLevel => Fusion.GetPermissionLevel(); public static int CurrentPlayers => Fusion.GetPlayerCount().current; public static int MaxPlayers => Fusion.GetPlayerCount().max; public static string NetworkLayer => Fusion.GetCurrentNetworkLayerTitle(); public static string PrivacyLevel => Enum.GetName(Fusion.GetPrivacy()).Replace("_", " "); } } namespace LabPresence.Managers { public static class GamemodeManager { private static readonly Dictionary _Gamemodes = new Dictionary(); private static JsonDocument KnownGamemodesCache; public static IReadOnlyDictionary Gamemodes => _Gamemodes; public static void RegisterGamemode(this GamemodeOverrides gamemode, string barcode) { ArgumentNullException.ThrowIfNull(gamemode, "gamemode"); if (_Gamemodes.ContainsKey(barcode)) { throw new ArgumentException("Gamemode is already registered!"); } if (string.IsNullOrWhiteSpace(barcode)) { throw new ArgumentNullException("gamemode", "The barcode cannot be empty or null!"); } if (IsGamemodeRegistered(barcode)) { throw new ArgumentException("A gamemode with the same barcode is already registered!"); } if (gamemode.CustomToolTip == null && gamemode.OverrideTime == null) { throw new ArgumentException("The gamemode needs to have a custom tooltip and/or override time"); } _Gamemodes.Add(barcode, gamemode); } public static void RegisterGamemode(string barcode, Func customToolTip) { new GamemodeOverrides(null, customToolTip).RegisterGamemode(barcode); } public static void RegisterGamemode(string barcode, Func overrideTime) { new GamemodeOverrides(null, null, overrideTime).RegisterGamemode(barcode); } public static void RegisterGamemode(string barcode, Func customToolTip, string smallImage) { new GamemodeOverrides(smallImage, customToolTip).RegisterGamemode(barcode); } public static void RegisterGamemode(string barcode, Func overrideTime, string smallImage) { new GamemodeOverrides(smallImage, null, overrideTime).RegisterGamemode(barcode); } public static void RegisterGamemode(string barcode, Func customToolTip, Func overrideTime) { new GamemodeOverrides(null, customToolTip, overrideTime).RegisterGamemode(barcode); } public static void RegisterGamemode(string barcode, Func customToolTip, Func overrideTime, string smallImage) { new GamemodeOverrides(smallImage, customToolTip, overrideTime).RegisterGamemode(barcode); } public static bool UnregisterGamemode(string barcode) { if (string.IsNullOrWhiteSpace(barcode)) { throw new ArgumentNullException("barcode", "The barcode cannot be null or empty!"); } if (_Gamemodes.Count == 0) { return false; } return _Gamemodes.Remove(barcode); } public static bool IsGamemodeRegistered(string barcode) { return _Gamemodes.ContainsKey(barcode); } public static GamemodeOverrides GetGamemode(string barcode) { return _Gamemodes.ContainsKey(barcode) ? _Gamemodes[barcode] : null; } public static int GetGamemodeCount() { return _Gamemodes.Count; } public static string[] GetGamemodeBarcodes() { return _Gamemodes.Keys.ToArray(); } public static string GetToolTipValue(string barcode) { GamemodeOverrides gamemode = GetGamemode(barcode); if (gamemode == null || gamemode.CustomToolTip == null) { return string.Empty; } string result; try { result = gamemode.CustomToolTip?.Invoke(); } catch (Exception value) { Core.Logger.Error($"An unexpected error has occurred while trying to get value of tooltip of the gamemode with barcode '{barcode}', exception:\n{value}"); result = string.Empty; } return result; } public static Timestamp GetOverrideTime(string barcode) { GamemodeOverrides gamemode = GetGamemode(barcode); if (gamemode == null || gamemode.OverrideTime == null) { return null; } Timestamp result; try { result = gamemode.OverrideTime?.Invoke(); } catch (Exception value) { Core.Logger.Error($"An unexpected error has occurred while trying to get value of tooltip of the gamemode with barcode '{barcode}', exception:\n{value}"); result = null; } return result; } public static string GetGamemodeKey(string barcode) { if (IsGamemodeRegistered(barcode)) { string text = GetGamemode(barcode)?.SmallImage; if (!string.IsNullOrWhiteSpace(text)) { return text; } } return GetRemoteGamemodeKey(barcode); } public static string GetRemoteGamemodeKey(string barcode) { try { if (KnownGamemodesCache == null) { HttpClient httpClient = new HttpClient(); Task async = httpClient.GetAsync("https://raw.githubusercontent.com/HAHOOS/LabPresence/refs/heads/master/Data/gamemodes.json"); async.Wait(); if (async.IsCompletedSuccessfully && async.Result.IsSuccessStatusCode) { Task task = async.Result.Content.ReadAsStringAsync(); task.Wait(); if (task.IsCompletedSuccessfully) { KnownGamemodesCache = JsonDocument.Parse(task.Result); } } } if (KnownGamemodesCache != null && KnownGamemodesCache.RootElement.TryGetProperty(barcode, out var value)) { return value.GetString(); } } catch (Exception value2) { Core.Logger.Error($"An unexpected error has occurred while trying to remotely get a key for the gamemode, defaulting to unknown key. Exception:\n{value2}"); } return "unknown_gamemode"; } } public class GamemodeOverrides { public string SmallImage { get; } public Func CustomToolTip { get; } public Func OverrideTime { get; } public GamemodeOverrides(string smallImage = null, Func customToolTip = null, Func overrideTime = null) { SmallImage = smallImage; CustomToolTip = customToolTip; OverrideTime = overrideTime; base..ctor(); } } internal static class MenuManager { public static Page ModPage { get; private set; } public static Page ConfigPage { get; private set; } public static Page PluginsPage { get; private set; } public static bool IsInitialized { get; private set; } public static Color FromRGB(int r, int g, int b) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) return new Color((float)r / 255f, (float)g / 255f, (float)b / 255f); } public static void Init() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) ModPage = Page.Root.CreatePage("LabPresence", Color.cyan, 0, true); Core.Thunderstore.BL_CreateMenuLabel(ModPage, createBlankSpace: false); ConfigPage = ModPage.CreatePage("Config", FromRGB(255, 172, 28), 0, true); PopulateConfig(); PluginsPage = ModPage.CreatePage("Plugins", Color.cyan, 0, true); PopulatePlugins(); IsInitialized = true; } public static void PopulateConfig() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) if (ConfigPage == null) { return; } ConfigPage.CreateEnum("RPC Log Level", FromRGB(255, 172, 28), (Enum)(object)Core.Config.RPCLogLevel, (Action)delegate(Enum val) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) Core.Config.RPCLogLevel = (LogLevel)(object)val; Core.Category.SaveToFile(false); DiscordRpcClient client = Core.Client; if (client != null) { ILogger logger = client.Logger; if (logger != null) { logger.Level = (LogLevel)(object)val; } } }); ((Element)ConfigPage.CreateEnum("Time Mode", FromRGB(191, 255, 0), (Enum)Core.Config.TimeMode, (Action)delegate(Enum val) { Core.Config.TimeMode = (TimeMode)(object)val; Core.Category.SaveToFile(false); Core.ConfigureTimestamp(autoUpdate: true); })).SetTooltip("What the Rich Presence will display as time, available options: Level (since the current level was loaded), CurrentTime (the current time, example: 15:53:50) and GameSession (since the game was launched)"); ((Element)ConfigPage.CreateBool("Remove Level Numbers", Color.red, Core.Config.RemoveLevelNumbers, (Action)delegate(bool val) { Core.Config.RemoveLevelNumbers = val; Core.Category.SaveToFile(false); })).SetTooltip("If true, in for example '15 - Void G114' the '15 - ' will be removed and only 'Void G114' will be shown in the 'levelName' placeholder"); ((Element)ConfigPage.CreateBool("Use Animated Logo", Color.cyan, Core.Config.UseAnimatedLogo, (Action)delegate(bool val) { Core.Config.UseAnimatedLogo = val; Core.Category.SaveToFile(false); })).SetTooltip("If true, the large image will be animated (with a black blackground), otherwise a transparent static image will be used"); } public static void PopulatePlugins() { if (PluginsPage == null) { return; } foreach (IPlugin plugin in PluginsManager.Plugins) { if (plugin.CreatesMenu) { plugin.Internal_PopulateMenu(); } } } } public static class PlaceholderManager { private static readonly List _Placeholders = new List(); public static void RegisterPlaceholder(this Placeholder placeholder) { if (placeholder == null) { throw new ArgumentNullException("placeholder"); } if (string.IsNullOrWhiteSpace(placeholder.ID)) { throw new ArgumentNullException("placeholder", "The ID cannot be null or empty!"); } if (placeholder.Values == null) { throw new ArgumentNullException("placeholder", "The values callback cannot be null!"); } if (_Placeholders.Contains(placeholder)) { throw new ArgumentException("The placeholder is already registered"); } if (_Placeholders.Any((Placeholder x) => x.ID == placeholder.ID)) { throw new ArgumentException("A placeholder with the same ID already exists!"); } _Placeholders.Add(placeholder); } public static void RegisterPlaceholder(string id, Func values) { new Placeholder(id, values).RegisterPlaceholder(); } public static bool UnregisterPlaceholder(string name) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException("name", "The name cannot be empty or null!"); } if (_Placeholders.Count == 0) { return false; } int num = _Placeholders.FindIndex((Placeholder x) => x.ID == name); if (num == -1) { return false; } _Placeholders.RemoveAt(num); return true; } public static bool UnregisterPlaceholder(this Placeholder placeholder) { return UnregisterPlaceholder(placeholder?.ID); } public static bool IsPlaceholderRegistered(string id) { return _Placeholders.Any((Placeholder x) => x.ID == id); } public static bool IsPlaceholderRegistered(this Placeholder placeholder) { return IsPlaceholderRegistered(placeholder?.ID); } public static int GetPlaceholderCount() { return _Placeholders.Count; } public static string ApplyPlaceholders(this string text) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) Template val = Template.Parse(text, (string)null, (ParserOptions?)null, (LexerOptions?)null); if (val.HasErrors) { Core.Logger.Error("An error occurred while parsing the text! '" + text + "'"); foreach (LogMessage message in val.Messages) { Core.Logger.Error((((int)message.Type == 0) ? "[ERR]" : "[WARN]") + " " + message.Message); } return text; } TemplateContext val2 = new TemplateContext(); ScriptObject val3 = new ScriptObject((IEqualityComparer)StringComparer.OrdinalIgnoreCase); val3.Add("utils", (object)new ScribanUtils()); ScriptObject val4 = val3; val2.PushGlobal((IScriptObject)(object)val4); foreach (Placeholder placeholder in _Placeholders) { try { val2.PushGlobal((IScriptObject)(object)placeholder.Values?.Invoke()); } catch (Exception ex) { Core.Logger.Error("An error occurred while retrieving placeholders with ID '" + placeholder.ID + "'!", ex); } } return val.Render(val2); } } public class Placeholder { public string ID { get; set; } public Func Values { get; set; } public Placeholder(string id, Func values) { ID = id; Values = values; base..ctor(); } } public static class RichPresenceManager { private static float time = 0f; private static Presence old = null; private static readonly Dictionary _avatarCache = new Dictionary(); private static Presence CurrentPresence { get; set; } public static RpcConfig CurrentConfig { get; private set; } public static Timestamp Timestamp { get; private set; } public static TimestampOverride OverrideTimestamp { get; private set; } public static bool AutoUpdate { get; set; } = true; public static void UpdateTimestamp() { if (Core.Client.CurrentPresence != null) { Core.Client.Update((Action)delegate(RichPresence x) { ((BaseRichPresence)x).Timestamps = ((OverrideTimestamp?.Timestamp == null) ? Timestamp.ToRPC() : OverrideTimestamp?.Timestamp?.ToRPC()); }); } } internal static void OnUpdate() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (CurrentPresence != null && AutoUpdate) { time += Time.deltaTime; if (CurrentPresence != old) { time = 0f; old = CurrentPresence; } if (time >= 5f) { TrySetRichPresence(CurrentPresence.Config, CurrentPresence.Type, CurrentPresence.Party, CurrentPresence.Secrets, CurrentPresence.LargeImage, CurrentPresence.SmallImage); time = 0f; } } else { time = 0f; } } public static void SetTimestamp(Timestamp timestamp, bool autoUpdate = false) { Timestamp = timestamp; if (autoUpdate) { UpdateTimestamp(); } } public static void SetTimestamp(ulong? start, ulong? end, bool autoUpdate = false) { SetTimestamp(new Timestamp(start, end), autoUpdate); } public static void SetTimestampStartToNow(bool autoUpdate = false) { SetTimestamp(Timestamp.Now, autoUpdate); } public static void SetTimestampToCurrentTime(bool autoUpdate = false) { SetTimestamp(Timestamp.CurrentTime, autoUpdate); } public static void SetOverrideTimestamp(TimestampOverride timestamp, bool autoUpdate = false) { OverrideTimestamp = timestamp; if (autoUpdate) { UpdateTimestamp(); } } public static void ResetOverrideTimestamp(bool autoUpdate = false) { OverrideTimestamp = null; if (autoUpdate) { UpdateTimestamp(); } } internal static bool ValidateString(string str, out string result, bool useBytes, int length, Encoding encoding = null) { result = str; if (str == null) { return true; } string text = str.Trim(); if ((useBytes && !StringTools.WithinLength(text, length, encoding)) || text.Length > length) { return false; } result = StringTools.GetNullOrString(text); return true; } private static void SetRichPresence(string details, string state, ActivityType type = 0, Party party = null, Secrets secrets = null, Asset largeImage = null, Asset smallImage = null) { //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Expected O, but got Unknown DiscordRpcClient client = Core.Client; if (client == null || !client.IsInitialized) { throw new InvalidOperationException("The RPC client is not initialized!"); } if (Core.Config.UseAnimatedLogo) { if (largeImage == null) { largeImage = new Asset("https://raw.githubusercontent.com/HAHOOS/LabPresence/refs/heads/master/Data/animated.gif", "BONELAB"); } } else if (largeImage == null) { largeImage = new Asset("icon", "BONELAB"); } if (!ValidateString(Core.RemoveUnityRichText(details?.ApplyPlaceholders()), out var result, useBytes: false, 128, Encoding.UTF8) || !ValidateString(Core.RemoveUnityRichText(state?.ApplyPlaceholders()), out var result2, useBytes: false, 128, Encoding.UTF8) || !ValidateString(Core.RemoveUnityRichText(largeImage?.ToolTip?.ApplyPlaceholders()), out var result3, useBytes: false, 128, Encoding.UTF8) || !ValidateString(Core.RemoveUnityRichText(smallImage?.ToolTip?.ApplyPlaceholders()), out var result4, useBytes: false, 128, Encoding.UTF8)) { throw new ArgumentException("State, Details and/or an asset tooltip is/are over 128 bytes which Rich Presence cannot handle, try to lower the amount of characters"); } if (largeImage != null) { largeImage.ToolTip = result3; } if (smallImage != null) { smallImage.ToolTip = result4; } Core.Client.SetPresence(new RichPresence { Details = result, State = result2, Timestamps = ((OverrideTimestamp?.Timestamp == null) ? Timestamp?.ToRPC() : OverrideTimestamp?.Timestamp?.ToRPC()), Type = type, Assets = CreateAssets(largeImage, smallImage), Party = party, Secrets = secrets }); } private static Assets CreateAssets(Asset largeImage, Asset smallImage) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown return new Assets { LargeImageKey = largeImage?.Key, LargeImageText = largeImage?.ToolTip, SmallImageKey = smallImage?.Key, SmallImageText = smallImage?.ToolTip }; } private static bool TrySetRichPresence(string details, string state, ActivityType type = 0, Party party = null, Secrets secrets = null, Asset largeImage = null, Asset smallImage = null) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) try { SetRichPresence(details, state, type, party, secrets, largeImage, smallImage); return true; } catch (StringOutOfRangeException) { Core.Logger.Error("State, Details and/or an asset tooltip is/are over 128 bytes which Rich Presence cannot handle, try to lower the amount of characters"); } catch (InvalidOperationException) { Core.Logger.Error("The RPC client is not initialized!"); } catch (Exception value) { Core.Logger.Error($"An unexpected error has occurred while setting the rich presence, exception:\n{value}"); } return false; } public static bool TrySetRichPresence(RpcConfig config, ActivityType type = 0, Party party = null, Secrets secrets = null, Asset largeImage = null, Asset smallImage = null) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (!config.Use) { return true; } bool flag = TrySetRichPresence(config.Details, config.State, type, party, secrets, largeImage, smallImage); if (flag) { CurrentConfig = config; CurrentPresence = new Presence(config, type, party, secrets, largeImage, smallImage); } return flag; } public static void SetRichPresence(RpcConfig config, ActivityType type = 0, Party party = null, Secrets secrets = null, Asset largeImage = null, Asset smallImage = null) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (config.Use) { SetRichPresence(config.Details, config.State, type, party, secrets, largeImage, smallImage); CurrentPresence = new Presence(config, type, party, secrets, largeImage, smallImage); CurrentConfig = config; } } public static Texture2D GetAvatar(User user, AvatarSize size = 512, bool cache = false) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) ArgumentNullException.ThrowIfNull(user, "user"); if (cache && _avatarCache.ContainsKey(user.ID) && (Object)(object)_avatarCache[user.ID] != (Object)null) { return _avatarCache[user.ID]; } Texture2D val = null; string avatarURL = user.GetAvatarURL((AvatarFormat)0, size); HttpClient httpClient = new HttpClient(); Task async = httpClient.GetAsync(avatarURL); async.Wait(); if (async.IsCompletedSuccessfully) { Task task = async.Result.Content.ReadAsByteArrayAsync(); if (task.IsCompletedSuccessfully) { val = ImageConversion.LoadTexture(user.DisplayName + " (@" + user.Username + ")", task.Result); } } if (cache && (Object)(object)val != (Object)null) { _avatarCache[user.ID] = val; } return val; } public static string Encrypt(string secret) { if (string.IsNullOrWhiteSpace(secret)) { return string.Empty; } byte[] bytes = Encoding.UTF8.GetBytes(secret); return Convert.ToBase64String(bytes); } public static string Decrypt(string secret) { if (string.IsNullOrWhiteSpace(secret)) { return string.Empty; } byte[] bytes = Convert.FromBase64String(secret); return Encoding.UTF8.GetString(bytes); } } public class Presence { public RpcConfig Config { get; internal set; } public ActivityType Type { get; internal set; } public Party Party { get; internal set; } public Secrets Secrets { get; internal set; } public Asset LargeImage { get; internal set; } public Asset SmallImage { get; internal set; } public Presence(RpcConfig config, ActivityType type, Party party, Secrets secrets, Asset largeImage, Asset smallImage) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Config = config; Type = type; Party = party; Secrets = secrets; LargeImage = largeImage; SmallImage = smallImage; base..ctor(); } } public class Timestamp { public ulong? Start { get; private set; } public ulong? End { get; private set; } public static Timestamp Now => new Timestamp((ulong)DateTimeOffset.Now.ToUnixTimeMilliseconds(), null); public static Timestamp CurrentTime { get { DateTimeOffset now = DateTimeOffset.Now; TimeSpan value = new TimeSpan(0, now.Hour, now.Minute, now.Second, now.Millisecond); return new Timestamp((ulong)now.Subtract(value).ToUnixTimeMilliseconds(), null); } } public Timestamp(ulong? start, ulong? end) { Start = start; End = end; base..ctor(); } public static Timestamp FromNow(int days = 0, int hours = 0, int minutes = 0, int seconds = 0, int milliseconds = 0) { DateTimeOffset now = DateTimeOffset.Now; DateTimeOffset dateTimeOffset = now.Add(new TimeSpan(days, hours, minutes, seconds, milliseconds)); return new Timestamp((ulong)now.ToUnixTimeMilliseconds(), (ulong)dateTimeOffset.ToUnixTimeMilliseconds()); } public static Timestamp FromNow(TimeSpan span) { DateTimeOffset now = DateTimeOffset.Now; DateTimeOffset dateTimeOffset = now.Add(span); return new Timestamp((ulong)now.ToUnixTimeMilliseconds(), (ulong)dateTimeOffset.ToUnixTimeMilliseconds()); } public Timestamps ToRPC() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown return new Timestamps { EndUnixMilliseconds = End, StartUnixMilliseconds = Start }; } } public class TimestampOverride { public Timestamp Timestamp { get; set; } public string Origin { get; set; } public TimestampOverride(Timestamp timestamp, string origin) { Timestamp = timestamp; Origin = origin; base..ctor(); } } public class Asset { public string Key { get; set; } public string ToolTip { get; set; } public Asset(string key, string tooltip) { Key = key; ToolTip = tooltip; } public Asset(Uri uri, string tooltip) { Key = uri.ToString(); ToolTip = tooltip; } } } namespace LabPresence.Helper { public static class ListHelper { public static bool RemoveFirst(this List list, Predicate predicate) { for (int i = 0; i < list.Count; i++) { if (predicate(list[i])) { list.RemoveAt(i); return true; } } return false; } } } namespace LabPresence.Config { public class DefaultConfig { [TomlPrecedingComment("The logs of RPC that will be displayed, available: None, Trace, Info, Warning, Error")] [TomlProperty("RPCLogLevel")] public LogLevel RPCLogLevel { get; set; } = (LogLevel)4; [TomlPrecedingComment("What the Rich Presence will display as time, available options: Level (since the current level was loaded), CurrentTime (the current time, example: 15:53:50) and GameSession (since the game was launched)")] [TomlProperty("TimeMode")] public TimeMode TimeMode { get; set; } = TimeMode.GameSession; [TomlPrecedingComment("If true, in for example '15 - Void G114' the '15 - ' will be removed and only 'Void G114' will be shown in the 'levelName' placeholder")] [TomlProperty("RemoveLevelNumbers")] public bool RemoveLevelNumbers { get; set; } = true; [TomlPrecedingComment("If true, the large image will be animated (with a black blackground), otherwise a transparent static image will be used")] [TomlProperty("UseAnimatedLogo")] public bool UseAnimatedLogo { get; set; } = true; [TomlProperty("PreGameStarted")] public RpcConfig PreGameStarted { get; set; } = new RpcConfig("Game loading...", "{{ game.code_mods_count }} melons"); [TomlProperty("AssetWarehouseLoaded")] public RpcConfig AssetWarehouseLoaded { get; set; } = new RpcConfig("Asset Warehouse loaded", "{{ game.mods_count }} mods"); [TomlProperty("LevelLoaded")] public RpcConfig LevelLoaded { get; set; } = new RpcConfig("Level: {{ game.level_name }}", "Avatar: {{ player.avatar?.title | utils.clean_string }}"); [TomlProperty("LevelLoading")] public RpcConfig LevelLoading { get; set; } = new RpcConfig("Loading {{ game.level_name }}"); } public enum TimeMode { Level, CurrentTime, GameSession } public class FusionConfig { [TomlPrecedingComment("If the time mode will be set to 'Level', when in a fusion lobby it will override the time to display how long you are in the lobby instead of the level")] [TomlProperty("OverrideTimeToLobby")] public bool OverrideTimeToLobby { get; set; } = true; [TomlPrecedingComment("If true, a notification will be shown when someone requests to join your server")] [TomlProperty("ShowJoinRequestPopUp")] public bool ShowJoinRequestPopUp { get; set; } = true; [TomlPrecedingComment("If true, when hosting a private server, players will be able to let others join the server through Discord")] [TomlProperty("AllowPlayersToInvite")] public bool AllowPlayersToInvite { get; set; } = true; [TomlPrecedingComment("If true, gamemodes that support custom tooltips will display custom text on the small icon. Disabling this option will cause the tooltip to only show the name of the gamemode")] [TomlProperty("ShowCustomGamemodeToolTips")] public bool ShowCustomGamemodeToolTips { get; set; } = true; [TomlPrecedingComment("If true, the rich presence will allow discord users to join your server when available, otherwise if false, the join button will never be shown")] [TomlProperty("Joins")] public bool Joins { get; set; } = true; [TomlProperty("LevelLoaded")] public RpcConfig LevelLoaded { get; set; } = new RpcConfig("{{ game.level_name }}", "{{ fusion.lobby_name }}"); [TomlProperty("LevelLoading")] public RpcConfig LevelLoading { get; set; } = new RpcConfig("Loading {{ game.level_name }}", "{{ fusion.lobby_name }}"); } public class RpcConfig { [TomlProperty("Use")] public bool Use { get; set; } = true; [TomlProperty("Details")] public string Details { get; set; } [TomlProperty("State")] public string State { get; set; } public RpcConfig() { } public RpcConfig(bool use) { Use = use; } public RpcConfig(string details) { Details = details; Use = true; } public RpcConfig(string details, string state) { Details = details; State = state; Use = true; } } }