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; using System.Threading.Tasks; using BoneLib; using BoneLib.BoneMenu; using BoneLib.Notifications; using DiscordRPC; using DiscordRPC.Events; using DiscordRPC.Exceptions; using DiscordRPC.IO; using DiscordRPC.Logging; using DiscordRPC.Message; using HarmonyLib; 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.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.Helper; 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.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Core), "LabPresence", "1.3.2", "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.2")] [assembly: AssemblyInformationalVersion("1.3.2")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("1.3.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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__35_4; public static OnJoinRequestedEvent <>9__35_5; public static OnSpectateEvent <>9__35_6; public static Action <>9__35_7; public static Action <>9__35_8; public static Action <>9__35_9; public static Action <>9__35_10; public static Action <>9__35_11; public static Action <>9__35_12; public static Func <>9__41_0; internal void b__35_4(object _, JoinMessage e) { Overwrites.OnJoin.Run(e); } internal void b__35_5(object _, JoinRequestMessage e) { Overwrites.OnJoinRequested.Run(e); } internal void b__35_6(object _, SpectateMessage e) { Overwrites.OnSpectate.Run(e); } internal void b__35_7() { RichPresenceManager.TrySetRichPresence(Config.LevelLoaded, (ActivityType)0); } internal void b__35_8() { RichPresenceManager.TrySetRichPresence(Config.LevelLoading, (ActivityType)0); } internal void b__35_9() { RichPresenceManager.TrySetRichPresence(Config.AssetWarehouseLoaded, (ActivityType)0); } internal void b__35_10() { RichPresenceManager.TrySetRichPresence(Config.PreGameStarted, (ActivityType)0); } internal void b__35_11(LevelCrate _) { if (!FirstLevelLoad) { FirstLevelLoad = true; Thunderstore.BL_SendNotification(); } LevelLaunch = DateTime.Now; ConfigureTimestamp(autoUpdate: true); Overwrites.OnLevelLoaded.Run(); } internal void b__35_12(LevelCrate _) { Overwrites.OnLevelLoading.Run(); } internal ScriptObject b__41_0() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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.2"; 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; if (category == null) { return null; } return category.GetValue(); } } 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; } public static bool Deinit { get; private set; } private static int LastDay { get; set; } = -1; public override void OnInitializeMelon() { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Expected O, but got Unknown //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Expected O, but got Unknown //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Expected O, but got Unknown //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Expected O, but got Unknown //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0212: 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; LevelHooks.Setup(); RichPresenceManager.Setup(); ((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.2 A BONELAB Mod"); Thunderstore.BL_FetchPackage("LabPresence", "HAHOOS", "1.3.2", ((MelonBase)this).LoggerInstance); ((MelonBase)this).LoggerInstance.Msg("Adding placeholders"); AddDefaultPlaceholders(); ((MelonBase)this).LoggerInstance.Msg("Validating config for obsolete placeholders"); ValidateDefaultConfig(); ((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__35_4; if (obj == null) { OnJoinEvent val = delegate(object _, JoinMessage e) { Overwrites.OnJoin.Run(e); }; <>c.<>9__35_4 = val; obj = (object)val; } client.OnJoin += (OnJoinEvent)obj; DiscordRpcClient client2 = Client; object obj2 = <>c.<>9__35_5; if (obj2 == null) { OnJoinRequestedEvent val2 = delegate(object _, JoinRequestMessage e) { Overwrites.OnJoinRequested.Run(e); }; <>c.<>9__35_5 = val2; obj2 = (object)val2; } client2.OnJoinRequested += (OnJoinRequestedEvent)obj2; DiscordRpcClient client3 = Client; object obj3 = <>c.<>9__35_6; if (obj3 == null) { OnSpectateEvent val3 = delegate(object _, SpectateMessage e) { Overwrites.OnSpectate.Run(e); }; <>c.<>9__35_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 ex) { Logger.Error("An unexpected error has occurred while attempting to register the Fusion Plugin", ex); } 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, new Action(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."); } public override void OnDeinitializeMelon() { Deinit = true; } public static void ValidateDefaultConfig() { if (!Config.LevelLoaded.ValidateConfig()) { Config.LevelLoaded = new DefaultConfig().LevelLoaded; } if (!Config.LevelLoading.ValidateConfig()) { Config.LevelLoading = new DefaultConfig().LevelLoading; } if (!Config.PreGameStarted.ValidateConfig()) { Config.PreGameStarted = new DefaultConfig().PreGameStarted; } if (!Config.AssetWarehouseLoaded.ValidateConfig()) { Config.AssetWarehouseLoaded = new DefaultConfig().AssetWarehouseLoaded; } Category.SaveToFile(false); } private void RegisterURIScheme() { try { Client.RegisterUriScheme((string)null, (string)null); } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("An unexpected error has occurred while registering URI scheme", ex); } } 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_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 Hand obj = (((int)handType == 1) ? Player.LeftHand : Player.RightHand); if (obj == null) { return null; } HandReciever attachedReceiver = obj.AttachedReceiver; if (attachedReceiver == null) { return null; } IGrippable host = attachedReceiver.Host; if (host == null) { return null; } Grip grip = host.GetGrip(); if (grip == null) { return null; } MarrowEntity marrowEntity = grip._marrowEntity; if (marrowEntity == null) { return null; } Poolee poolee = marrowEntity._poolee; if (poolee == null) { return null; } return poolee.SpawnableCrate; } private static void AddDefaultPlaceholders() { PlaceholderManager.RegisterPlaceholder("default", delegate { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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(); _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 static void Repeat(Action callback, out CancellationTokenSource token, float seconds = 5f) { token = new CancellationTokenSource(); MelonCoroutines.Start(RepeatedDelay(callback, seconds, token)); } private static IEnumerator RepeatedDelay(Action callback, float seconds = 5f, CancellationTokenSource token = null) { while (!token.IsCancellationRequested) { yield return (object)new WaitForSeconds(seconds); callback?.Invoke(); } } 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_000e: 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_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Level <= 3) { LoggerInstance.Warning("[" + Prefix + "] [WARN] " + message, args); } } } } namespace LabPresence.Utilities { public static class Fps { public static int FramesPerSecond { get; private set; } internal static void OnUpdate() { FramesPerSecond = (int)(1f / Time.unscaledDeltaTime); } } 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_0036: 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_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0015: 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_0025: Expected O, but got Unknown Texture2D val = new Texture2D(2, 2); LoadImage(val, Il2CppStructArray.op_Implicit(bytes), markNonReadable: false); ((Object)val).name = name; ((Object)val).hideFlags = (HideFlags)32; return val; } } [HarmonyPatch(typeof(StreamSession))] public static class LevelHooks { private static StreamStatus _lastStatus; 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; } public static void Setup() { Hooking.OnLevelLoaded += delegate { OnLevelLoaded?.Invoke(CurrentLevel); }; Hooking.OnLevelUnloaded += delegate { OnLevelUnloaded?.Invoke(); }; } [HarmonyPatch("Load")] [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] public static void OnLoading(StreamSession __instance) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 if (__instance != null && _lastStatus != __instance.Status) { _lastStatus = __instance.Status; if ((int)__instance.Status == 1) { OnLevelLoading(__instance.Level); } } } } [HarmonyPatch(typeof(SceneStreamer))] public static class SceneStreamerPatches { [HarmonyPatch("Load", new Type[] { typeof(Barcode), typeof(Barcode) })] [HarmonyPatch("Load", new Type[] { typeof(LevelCrateReference), typeof(LevelCrateReference) })] [HarmonyPatch("LoadAsync")] [HarmonyPostfix] public static void OnLoading() { LevelHooks.OnLoading(SceneStreamer.Session); } } 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 string Pallet { get; } public ScribanCrate(Crate crate) { Title = ((Scannable)crate).Title; Description = ((Scannable)crate).Description; Redacted = ((Scannable)crate).Redacted; Barcode = ((Scannable)crate).Barcode.ID; Unlockable = ((Scannable)crate).Unlockable; ScriptArray val; if (crate.Tags == null) { Tags = new ScriptArray(); } else { val = new ScriptArray(); Enumerator enumerator = crate.Tags.GetEnumerator(); while (enumerator.MoveNext()) { string current = enumerator.Current; val.Add(current); } Tags = val; } Pallet = ((Scannable)crate.Pallet).Barcode.ID; val = new ScriptArray(); TagList boneTags = crate.BoneTags; string[] array = ((boneTags != null) ? boneTags.Tags.GetBarcodes() : null); foreach (string current in array) { val.Add(current); } BoneTags = val; Type = ((MemberInfo)((Object)crate).GetIl2CppType()).Name switch { "SpawnableCrate" => CrateType.Spawnable, "AvatarCrate" => CrateType.Avatar, "LevelCrate" => CrateType.Level, "VFXCrate" => CrateType.VFX, _ => 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; Author = pallet.Author; Description = ((Scannable)pallet).Description; SDKVersion = pallet.SDKVersion; if (pallet.ChangeLogs == null) { ChangeLogs = new ScriptArray(); } else { List list = new List(); Enumerator enumerator = pallet.ChangeLogs.GetEnumerator(); while (enumerator.MoveNext()) { ChangeLog current = enumerator.Current; list.Add(new ScribanChangeLog(current)); } ScriptArray val = new ScriptArray(); foreach (ScribanChangeLog item in list) { val.Add(item); } ChangeLogs = val; } ScriptArray val2 = new ScriptArray(); string[] barcodes = pallet.Crates.GetBarcodes(); foreach (string text in barcodes) { val2.Add(text); } Crates = val2; val2 = new ScriptArray(); barcodes = pallet.DataCards.GetBarcodes(); foreach (string text in barcodes) { val2.Add(text); } DataCards = val2; Dependencies = pallet.PalletDependencies.GetBarcodes().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 string Pallet { get; } = ((Scannable)dataCard.Pallet).Barcode.ID; public ScribanDataCard(DataCard dataCard) { } } 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; if (instance == null) { return 0; } return instance._groupCounts[type]; } } public class ScribanPlayer : ScriptObject { public static ScribanCrate Avatar { get { RigManager rigManager = Player.RigManager; if (!((Object)(object)((rigManager == null) ? null : ((CrateReferenceT)(object)rigManager.AvatarCrate)?.Crate) != (Object)null)) { return null; } RigManager rigManager2 = Player.RigManager; return new ScribanCrate((Crate)(object)((rigManager2 == null) ? null : ((CrateReferenceT)(object)rigManager2.AvatarCrate)?.Crate)); } } public static ScribanCrate LeftHand { get { if (!((Object)(object)Core.GetInHand((Handedness)1) != (Object)null)) { return null; } return new ScribanCrate((Crate)(object)Core.GetInHand((Handedness)1)); } } public static ScribanCrate RightHand { get { if (!((Object)(object)Core.GetInHand((Handedness)2) != (Object)null)) { return null; } return new ScribanCrate((Crate)(object)Core.GetInHand((Handedness)2)); } } 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) : ((float?)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) : ((float?)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; if (!((Object)(object)((session != null) ? session.Level : null) != (Object)null)) { return null; } StreamSession session2 = SceneStreamer.Session; return new ScribanCrate((Crate)(object)((session2 != null) ? session2.Level : null)); } } 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_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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 ScribanCrate GetCrate(string barcode) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown Crate crate = default(Crate); if (AssetWarehouse.Instance.TryGetCrate(new Barcode(barcode), ref crate)) { return new ScribanCrate(crate); } return null; } public static ScribanDataCard GetDataCard(string barcode) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown DataCard dataCard = default(DataCard); if (AssetWarehouse.Instance.TryGetDataCard(new Barcode(barcode), ref dataCard)) { return new ScribanDataCard(dataCard); } 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_0012: 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) 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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 { if (task != null && task.IsCompletedSuccessfully) { return task.Result != null; } return false; } public T SendRequest(string url) { Task async = new HttpClient(new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Manual, ServerCertificateCustomValidationCallback = (HttpRequestMessage _, X509Certificate2? _, X509Chain? _, SslPolicyErrors _) => true }) { DefaultRequestHeaders = { { "User-Agent", UserAgent }, { "Accept", "application/json" } } }.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_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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 num = 0; num < list.Count; num++) { 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 num = 0; num < list.Count; num++) { 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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) { new List(_Plugins).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; } private float ElapsedSeconds; private bool turnedOff; 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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(); ValidateConfig(); } public void ValidateConfig() { if (!GetConfig().LevelLoaded.ValidateConfig()) { GetConfig().LevelLoaded = new FusionConfig().LevelLoaded; } if (!GetConfig().LevelLoading.ValidateConfig()) { GetConfig().LevelLoading = new FusionConfig().LevelLoading; } base.Category.SaveToFile(false); } public override void PopulateMenu(Page page) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_007f: Expected O, but got Unknown try { base.Logger.Info("Join requested"); MelonCoroutines.Start(AfterLevelLoaded(after)); } catch (Exception ex) { 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", ex); 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; } string layer; string code; try { base.Logger.Info("Received Join Request"); string[] array = RichPresenceManager.Decrypt(e.Secret).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"); } layer = array[0]; code = array[1]; MelonCoroutines.Start(AfterLevelLoaded(join)); } catch (Exception ex) { 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", ex); return false; } return true; void join() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_0052: 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_0067: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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); } } } private static Party GetParty() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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), Max = Fusion.GetPlayerCount().max, Size = Fusion.GetPlayerCount().current }; } private static Secrets GetSecrets() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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 }; } private static IEnumerator AfterLevelLoaded(Action callback) { while (true) { StreamSession session = SceneStreamer.Session; if (session != null && (int)session.Status == 2) { break; } yield return null; } callback?.Invoke(); } private void Update() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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"; } if (!string.IsNullOrWhiteSpace(text)) { return text; } return lobbyInfo.LobbyHostName + "'s lobby"; } 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"; } if (!string.IsNullOrWhiteSpace(text)) { return text; } return "N/A"; } public static (int current, int max) GetPlayerCount() { if (!IsConnected) { return (current: -1, max: -1); } return Internal_GetPlayerCount(); } private static (int current, int max) Internal_GetPlayerCount() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return (current: -1, max: -1); } int playerCount = lobbyInfo.PlayerCount; int maxPlayers = lobbyInfo.MaxPlayers; return (current: playerCount, max: maxPlayers); } public static ServerPrivacy GetPrivacy() { if (!IsConnected) { return ServerPrivacy.Unknown; } return Internal_GetPrivacy(); } private static ServerPrivacy Internal_GetPrivacy() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return ServerPrivacy.Unknown; } return (ServerPrivacy)lobbyInfo.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 (username: null, displayName: 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: username, displayName: item); } public static string GetCurrentNetworkLayerTitle() { if (!IsConnected) { return null; } return Internal_GetCurrentNetworkLayerTitle(); } private static string Internal_GetCurrentNetworkLayerTitle() { NetworkLayer layer = NetworkLayerManager.Layer; if (layer == null) { return null; } return layer.Title; } 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 ex) { Logger.Error("An unexpected error has occurred while ensuring fusion is on the right network layer", ex); 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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Invalid comparison between Unknown and I4 //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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 val3) => val3.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_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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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) //IL_0034: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_00a2: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_004d: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0028: 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_0039: Unknown result type (might be due to invalid IL or missing references) 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_0028: Unknown result type (might be due to invalid IL or missing references) Gamemode activeGamemode = GamemodeManager.ActiveGamemode; if (((activeGamemode != null) ? activeGamemode.Barcode : null) != "Lakatrazz.Entangled") { return string.Empty; } string text = default(string); if (!((Gamemode)(Entangled)GamemodeManager.ActiveGamemode).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 (key: null, tooltip: null); } return Internal_GetGamemodeRPC(); } private static (string key, string tooltip) Internal_GetGamemodeRPC() { if (!GamemodeManager.IsGamemodeStarted) { return (key: null, tooltip: null); } if (GamemodeManager.ActiveGamemode == null) { return (key: null, tooltip: null); } Gamemode activeGamemode = GamemodeManager.ActiveGamemode; string text = ((GamemodeManager.GetGamemode(activeGamemode.Barcode)?.CustomToolTip != null) ? GamemodeManager.GetToolTipValue(activeGamemode.Barcode) : string.Empty); if (FusionPlugin.Instance.GetConfig().ShowCustomGamemodeToolTips) { return (key: GamemodeManager.GetGamemodeKey(activeGamemode.Barcode), tooltip: (!string.IsNullOrWhiteSpace(text)) ? (activeGamemode.Title + " | " + text) : activeGamemode.Title); } return (key: GamemodeManager.GetGamemodeKey(activeGamemode.Barcode), tooltip: 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) { if (!_Gamemodes.ContainsKey(barcode)) { return null; } return _Gamemodes[barcode]; } 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; } try { return gamemode.CustomToolTip?.Invoke(); } catch (Exception ex) { Core.Logger.Error("An unexpected error has occurred while trying to get value of tooltip of the gamemode with barcode '" + barcode + "'", ex); return string.Empty; } } public static Timestamp GetOverrideTime(string barcode) { GamemodeOverrides gamemode = GetGamemode(barcode); if (gamemode == null || gamemode.OverrideTime == null) { return null; } try { return gamemode.OverrideTime?.Invoke(); } catch (Exception ex) { Core.Logger.Error("An unexpected error has occurred while trying to get value of tooltip of the gamemode with barcode '" + barcode + "'", ex); return null; } } 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) { Task async = new 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 ex) { Core.Logger.Error("An unexpected error has occurred while trying to remotely get a key for the gamemode, defaulting to unknown key.", ex); } return "unknown_gamemode"; } } public class GamemodeOverrides(string smallImage = null, Func customToolTip = null, Func overrideTime = null) { public string SmallImage { get; } = smallImage; public Func CustomToolTip { get; } = customToolTip; public Func OverrideTime { get; } = overrideTime; } 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_000a: 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_0061: 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_001e: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_016e: 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) if (ConfigPage == null) { return; } ((Element)ConfigPage.CreateBool("Enabled", FromRGB(170, 51, 255), Core.Config.Enabled, (Action)delegate(bool val) { Core.Config.Enabled = val; Core.Category.SaveToFile(false); if (!Core.Config.Enabled) { DiscordRpcClient client = Core.Client; if (client != null) { client.ClearPresence(); } } })).SetTooltip("If false, the mod will not update the Discord Rich Presence"); ((Element)ConfigPage.CreateInt("Refresh Time", FromRGB(255, 56, 255), Core.Config.RefreshTime, 1, 5, 3600, (Action)delegate(int val) { Core.Config.RefreshTime = val; Core.Category.SaveToFile(false); })).SetTooltip("The interval in which the rich presence should be updated with up-to-date placeholder values"); ((Element)ConfigPage.CreateEnum("RPC Log Level", FromRGB(255, 172, 28), (Enum)(object)Core.Config.RPCLogLevel, (Action)delegate(Enum val) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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; } } })).SetTooltip("The logs of RPC that will be displayed, available: None, Trace, Info, Warning, Error"); ((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:\n-> Level (since the current level was loaded)\n-> CurrentTime (the current time, example: 15:53:50)\n-> 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 TemplateContext GetTemplateContext() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown TemplateContext val = new TemplateContext(); ScriptObject val2 = new ScriptObject((IEqualityComparer)StringComparer.OrdinalIgnoreCase); val2.Add("utils", (object)new ScribanUtils()); ScriptObject val3 = val2; val.PushGlobal((IScriptObject)(object)val3); foreach (Placeholder placeholder in _Placeholders) { try { val.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; } public static string ApplyPlaceholders(this string text, TemplateContext context = null) { //IL_0047: 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; } } if (context == null) { context = GetTemplateContext(); } return val.Render(context); } } public class Placeholder(string id, Func values) { public string ID { get; set; } = id; public Func Values { get; set; } = values; } public static class RichPresenceManager { 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 Setup() { MelonCoroutines.Start(OnUpdate()); } internal static IEnumerator OnUpdate() { while (!Core.Deinit) { yield return (object)new WaitForSeconds(((float?)Core.Config?.RefreshTime) ?? 5f); if (AutoUpdate && CurrentPresence != null) { TrySetRichPresence(CurrentPresence.Config, CurrentPresence.Type, CurrentPresence.Party, CurrentPresence.Secrets, CurrentPresence.LargeImage, CurrentPresence.SmallImage); } } } 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(); } } private static void SetRichPresence(string details, string state, ActivityType type = (ActivityType)0, Party party = null, Secrets secrets = null, Asset largeImage = null, Asset smallImage = null) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: 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_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015d: 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"); } TemplateContext templateContext = PlaceholderManager.GetTemplateContext(); if (largeImage != null) { largeImage.ToolTip = largeImage?.ToolTip?.Parse(templateContext); } if (smallImage != null) { smallImage.ToolTip = smallImage?.ToolTip?.Parse(templateContext); } if (Core.Config.Enabled) { Core.Client.SetPresence(new RichPresence { Details = details?.Parse(templateContext), State = state?.Parse(templateContext), Timestamps = ((OverrideTimestamp?.Timestamp == null) ? Timestamp?.ToRPC() : OverrideTimestamp?.Timestamp?.ToRPC()), Type = type, Assets = CreateAssets(largeImage, smallImage), Party = party, Secrets = secrets }); } else { Core.Client.ClearPresence(); } } public static string Parse(this string text, TemplateContext context = null) { return Core.RemoveUnityRichText(text?.ApplyPlaceholders(context)); } private static Assets CreateAssets(Asset largeImage, Asset smallImage) { //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_0017: 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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 = (ActivityType)0, Party party = null, Secrets secrets = null, Asset largeImage = null, Asset smallImage = null) { //IL_0002: 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 ex3) { Core.Logger.Error("An unexpected error has occurred while setting the rich presence", ex3); } return false; } public static bool TrySetRichPresence(RpcConfig config, ActivityType type = (ActivityType)0, Party party = null, Secrets secrets = null, Asset largeImage = null, Asset smallImage = null) { //IL_0016: 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) if (!config.Use) { return true; } bool num = TrySetRichPresence(config.Details, config.State, type, party, secrets, largeImage, smallImage); if (num) { CurrentConfig = config; CurrentPresence = new Presence(config, type, party, secrets, largeImage, smallImage); } return num; } public static void SetRichPresence(RpcConfig config, ActivityType type = (ActivityType)0, Party party = null, Secrets secrets = null, Asset largeImage = null, Asset smallImage = null) { //IL_0015: 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) 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 = (AvatarSize)512, bool cache = false) { //IL_004d: 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); Task async = new HttpClient().GetAsync(avatarURL); async.Wait(); if (async.IsCompletedSuccessfully) { Task task = async.Result.Content.ReadAsByteArrayAsync(); task.Wait(); 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; } return Convert.ToBase64String(Encoding.UTF8.GetBytes(secret)); } public static string Decrypt(string secret) { if (string.IsNullOrWhiteSpace(secret)) { return string.Empty; } byte[] bytes = Convert.FromBase64String(secret); return Encoding.UTF8.GetString(bytes); } public static bool ValidateConfig(this RpcConfig config) { if (config == null) { Core.Logger.Error("An empty RpcConfig was provided for validation!"); return true; } string[] array = new string[19] { "%levelName%", "%avatarName%", "%platform%", "%mlVersion%", "%health%", "%maxHealth%", "%healthPercentange%", "%fps%", "%operatingSystem%", "%codeModsCount%", "%modsCount%", "%ammoLight%", "%ammoMedium%", "%ammoHeavy%", "%fusion_lobbyName%", "%fusion_host%", "%fusion_currentPlayers%", "%fusion_maxPlayers%", "%fusion_privacy%" }; foreach (string value in array) { if (!string.IsNullOrWhiteSpace(config.Details) && config.Details.Contains(value)) { return false; } if (!string.IsNullOrWhiteSpace(config.State) && config.State.Contains(value)) { return false; } } return true; } } public class Presence(RpcConfig config, ActivityType type, Party party, Secrets secrets, Asset largeImage, Asset smallImage) { public RpcConfig Config { get; internal set; } = config; public ActivityType Type { get; internal set; } = type; public Party Party { get; internal set; } = party; public Secrets Secrets { get; internal set; } = secrets; public Asset LargeImage { get; internal set; } = largeImage; public Asset SmallImage { get; internal set; } = smallImage; } public class Timestamp(ulong? start, ulong? end) { public ulong? Start { get; private set; } = start; public ulong? End { get; private set; } = end; 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 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown return new Timestamps { EndUnixMilliseconds = End, StartUnixMilliseconds = Start }; } } public class TimestampOverride(Timestamp timestamp, string origin) { public Timestamp Timestamp { get; set; } = timestamp; public string Origin { get; set; } = origin; } 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 WarehouseHelper { public static string[] GetBarcodes(this List scannables) where T : Scannable { if (scannables == null || scannables.Count == 0) { return Array.Empty(); } List barcodes = new List(); scannables.ForEach(delegate(T c) { barcodes.Add(((Scannable)c).Barcode.ID); }); return barcodes.ToArray(); } public static string[] GetBarcodes(this List scannables) where T : Scannable { return ((IEnumerable)scannables.ToArray()).ToList().GetBarcodes(); } public static string[] GetBarcodes(this List references) where T : ScannableReference where TScannable : Scannable { return (references ?? new List()).ConvertAll((T x) => ((ScannableReference)x).Scannable).GetBarcodes(); } public static string[] GetBarcodes(this List references) where T : ScannableReference where TScannable : Scannable { return ((IEnumerable)(references ?? new List()).ToArray()).ToList().GetBarcodes(); } } } namespace LabPresence.Config { public class DefaultConfig { [TomlPrecedingComment("If false, the mod will not update the Discord Rich Presence")] [TomlProperty("Enabled")] public bool Enabled { get; set; } = true; [TomlPrecedingComment("The interval in which the rich presence should be updated with up-to-date placeholder values")] [TomlProperty("RefreshTime")] public int RefreshTime { get; set; } = 5; [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:\n -> Level (since the current level was loaded)\n -> CurrentTime (the current time, example: 15:53:50)\n -> 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; } } }