Decompiled source of LabPresence v1.3.2
Mods/LabPresence.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
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<LevelCrate> <>9__35_11; public static Action<LevelCrate> <>9__35_12; public static Func<ScriptObject> <>9__41_0; internal void <OnInitializeMelon>b__35_4(object _, JoinMessage e) { Overwrites.OnJoin.Run(e); } internal void <OnInitializeMelon>b__35_5(object _, JoinRequestMessage e) { Overwrites.OnJoinRequested.Run(e); } internal void <OnInitializeMelon>b__35_6(object _, SpectateMessage e) { Overwrites.OnSpectate.Run(e); } internal void <OnInitializeMelon>b__35_7() { RichPresenceManager.TrySetRichPresence(Config.LevelLoaded, (ActivityType)0); } internal void <OnInitializeMelon>b__35_8() { RichPresenceManager.TrySetRichPresence(Config.LevelLoading, (ActivityType)0); } internal void <OnInitializeMelon>b__35_9() { RichPresenceManager.TrySetRichPresence(Config.AssetWarehouseLoaded, (ActivityType)0); } internal void <OnInitializeMelon>b__35_10() { RichPresenceManager.TrySetRichPresence(Config.PreGameStarted, (ActivityType)0); } internal void <OnInitializeMelon>b__35_11(LevelCrate _) { if (!FirstLevelLoad) { FirstLevelLoad = true; Thunderstore.BL_SendNotification(); } LevelLaunch = DateTime.Now; ConfigureTimestamp(autoUpdate: true); Overwrites.OnLevelLoaded.Run(); } internal void <OnInitializeMelon>b__35_12(LevelCrate _) { Overwrites.OnLevelLoading.Run(); } internal ScriptObject <AddDefaultPlaceholders>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<string>)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<DefaultConfig>(); } } 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<DefaultConfig>("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<FusionPlugin>(); } } catch (Exception ex) { Logger.Error("An unexpected error has occurred while attempting to register the Fusion Plugin", ex); } LevelHooks.OnLevelLoaded = (Action<LevelCrate>)Delegate.Combine(LevelHooks.OnLevelLoaded, (Action<LevelCrate>)delegate { if (!FirstLevelLoad) { FirstLevelLoad = true; Thunderstore.BL_SendNotification(); } LevelLaunch = DateTime.Now; ConfigureTimestamp(autoUpdate: true); Overwrites.OnLevelLoaded.Run(); }); LevelHooks.OnLevelLoading = (Action<LevelCrate>)Delegate.Combine(LevelHooks.OnLevelLoading, (Action<LevelCrate>)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<string>)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<TextureAndFlagDelegate>("UnityEngine.ImageConversion::EncodeToEXR"); private static readonly TextureOnlyDelegate EncodeToTGADelegateField = IL2CPP.ResolveICall<TextureOnlyDelegate>("UnityEngine.ImageConversion::EncodeToTGA"); private static readonly TextureOnlyDelegate EncodeToPNGDelegateField = IL2CPP.ResolveICall<TextureOnlyDelegate>("UnityEngine.ImageConversion::EncodeToPNG"); private static readonly TextureAndQualityDelegate EncodeToJPGDelegateField = IL2CPP.ResolveICall<TextureAndQualityDelegate>("UnityEngine.ImageConversion::EncodeToJPG"); private static readonly LoadImageDelegate LoadImageDelegateField = IL2CPP.ResolveICall<LoadImageDelegate>("UnityEngine.ImageConversion::LoadImage"); public static Il2CppStructArray<byte> 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<byte>(intPtr); } return null; } public static Il2CppStructArray<byte> 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<byte>(intPtr); } return null; } public static Il2CppStructArray<byte> 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<byte>(intPtr); } return null; } public static Il2CppStructArray<byte> EncodeToJPG(this Texture2D tex) { return tex.EncodeToJPG(75); } public static Il2CppStructArray<byte> 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<byte>(intPtr); } return null; } public static Il2CppStructArray<byte> EncodeToEXR(this Texture2D tex) { return tex.EncodeToEXR((EXRFlags)0); } public static bool LoadImage(this Texture2D tex, Il2CppStructArray<byte> 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<byte> 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<byte>.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<LevelCrate> OnLevelLoaded { get; set; } public static Action<LevelCrate> 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<string> Tags { get; } public ScriptArray<string> 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<string> val; if (crate.Tags == null) { Tags = new ScriptArray<string>(); } else { val = new ScriptArray<string>(); Enumerator<string> 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<string>(); TagList boneTags = crate.BoneTags; string[] array = ((boneTags != null) ? boneTags.Tags.GetBarcodes<BoneTagReference, DataCard>() : 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<string> Crates { get; } public ScriptArray<ScribanChangeLog> ChangeLogs { get; } public ScriptArray<string> 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<string>(); } else { Tags = Il2CppArrayBase<string>.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<ScribanChangeLog>(); } else { List<ScribanChangeLog> list = new List<ScribanChangeLog>(); Enumerator<ChangeLog> enumerator = pallet.ChangeLogs.GetEnumerator(); while (enumerator.MoveNext()) { ChangeLog current = enumerator.Current; list.Add(new ScribanChangeLog(current)); } ScriptArray<ScribanChangeLog> val = new ScriptArray<ScribanChangeLog>(); foreach (ScribanChangeLog item in list) { val.Add(item); } ChangeLogs = val; } ScriptArray<string> val2 = new ScriptArray<string>(); string[] barcodes = pallet.Crates.GetBarcodes<Crate>(); foreach (string text in barcodes) { val2.Add(text); } Crates = val2; val2 = new ScriptArray<string>(); barcodes = pallet.DataCards.GetBarcodes<DataCard>(); foreach (string text in barcodes) { val2.Add(text); } DataCards = val2; Dependencies = pallet.PalletDependencies.GetBarcodes<PalletReference, Pallet>().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<AvatarCrate>)(object)rigManager.AvatarCrate)?.Crate) != (Object)null)) { return null; } RigManager rigManager2 = Player.RigManager; return new ScribanCrate((Crate)(object)((rigManager2 == null) ? null : ((CrateReferenceT<AvatarCrate>)(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<MelonMod>.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 : "<br><color=#00FF00>(Update available!)</color>"), 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 <color=#00FF00>v{_fetchedPackage.Latest.Version}</color>", 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<Package>($"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<T>(Task<T> task) where T : class { if (task != null && task.IsCompletedSuccessfully) { return task.Result != null; } return false; } public T SendRequest<T>(string url) { Task<HttpResponseMessage> 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<string> task = httpResponseMessage.Content.ReadAsStringAsync(); task.Wait(); string text = task?.Result; if (IsTaskGood(task)) { return JsonConvert.DeserializeObject<T>(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<V1PackageMetrics>($"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<string> task = response.Content.ReadAsStringAsync(); task.Wait(); string result = task.Result; if (string.IsNullOrWhiteSpace(result)) { return null; } ThunderstoreErrorResponse thunderstoreErrorResponse; try { thunderstoreErrorResponse = JsonConvert.DeserializeObject<ThunderstoreErrorResponse>(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<string> 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<string> 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<string> task = response.Content.ReadAsStringAsync(); task.Wait(); string result = task.Result; if (string.IsNullOrWhiteSpace(result)) { Details = string.Empty; return; } ThunderstoreErrorResponse thunderstoreErrorResponse; try { thunderstoreErrorResponse = JsonConvert.DeserializeObject<ThunderstoreErrorResponse>(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<JoinMessage> OnJoin { get; } = new OverwritesType<JoinMessage>(); public static OverwritesType<JoinRequestMessage> OnJoinRequested { get; } = new OverwritesType<JoinRequestMessage>(); public static OverwritesType<SpectateMessage> OnSpectate { get; } = new OverwritesType<SpectateMessage>(); } public class OverwritesType<T> where T : new() { private readonly List<Overwrite<T>> _Overwrites = new List<Overwrite<T>>(); public IReadOnlyCollection<Overwrite<T>> Overwrites => _Overwrites.AsReadOnly(); internal Action<T> Default { get; private set; } internal void SetDefault(Action<T> action) { Default = action; } public void RegisterOverwrite(Overwrite<T> overwrite) { if (IsRegistered(overwrite)) { throw new ArgumentException("An overwrite with the same ID is already registered!"); } _Overwrites.Add(overwrite); } public void RegisterOverwrite(Func<T, bool> callback, out Guid ID, int priority = 0) { Overwrite<T> overwrite = new Overwrite<T>(callback, priority); RegisterOverwrite(overwrite); ID = overwrite.ID; } public bool RemoveOverwrite(Guid ID) { return _Overwrites.RemoveAll((Overwrite<T> x) => x.ID == ID) > 0; } public bool RemoveOverwrite(Overwrite<T> overwrite) { return RemoveOverwrite(overwrite.ID); } public bool IsRegistered(Guid ID) { return Overwrites.Any((Overwrite<T> x) => x.ID == ID); } public bool IsRegistered(Overwrite<T> overwrite) { return IsRegistered(overwrite.ID); } public void Run(T arg) { if (_Overwrites.Count == 0) { Default?.Invoke(arg); return; } List<Overwrite<T>> list = Overwrites.OrderByDescending((Overwrite<T> x) => x.Priority).ToList(); for (int num = 0; num < list.Count; num++) { if (list.Count == 0) { break; } Overwrite<T> overwrite = list[0]; if (overwrite == null) { list.RemoveAt(0); continue; } Func<T, bool> callback = overwrite.Callback; if (callback == null || !callback(arg)) { list.RemoveAt(0); continue; } return; } Default?.Invoke(arg); } } public class Overwrite<T> where T : new() { public int Priority { get; set; } public Guid ID { get; set; } public Func<T, bool> Callback { get; set; } public Overwrite(Guid id, Func<T, bool> callback, int priority = 0) { ID = id; Priority = priority; Callback = callback; } public Overwrite(Func<T, bool> callback, int priority = 0) { ID = Guid.NewGuid(); Priority = priority; Callback = callback; } } public class OverwritesType { private readonly List<Overwrite> _Overwrites = new List<Overwrite>(); public IReadOnlyCollection<Overwrite> 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<bool> 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<Overwrite> 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<bool> 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<bool> Callback { get; set; } public Overwrite(Guid id, Func<bool> callback, int priority = 0) { ID = id; Priority = priority; Callback = callback; } public Overwrite(Func<bool> 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<ConfigT> : 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<ConfigT>("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<ConfigT>(); } } public static class PluginsManager { private static readonly List<IPlugin> _Plugins = new List<IPlugin>(); public static IReadOnlyCollection<IPlugin> 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<PluginT>() where PluginT : IPlugin { Register(typeof(PluginT)); } public static void Unregister(string name) { new List<IPlugin>(_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<PluginT>() 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<PluginT>() where PluginT : IPlugin { return IsRegistered(GetPluginFromType(typeof(PluginT)).Name); } private static IPlugin GetPluginFromType(Type plugin) { if (!plugin.IsTypeOf<IPlugin>()) { throw new ArgumentException("Type must be a sub class of IPlugin!", "plugin"); } return (IPlugin)Activator.CreateInstance(plugin); } public static bool IsTypeOf<T>(this Type type) { return typeof(T).IsAssignableFrom(type); } } } namespace LabPresence.Plugins.Default { internal class FusionPlugin : Plugin<FusionConfig> { [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<string>)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<LemonAction>)(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<bool>)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<bool>)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<bool>)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<bool>)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<bool>)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<string> <2>__HideAndSeekTooltip; public static Func<string> <3>__DeathmatchTooltip; public static Func<string> <4>__TeamDeathmatchTooltip; public static Func<string> <5>__EntangledTooltip; public static Func<string> <6>__SmashBonesTooltip; public static Func<string> <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<MatchmakerCallbackInfo>)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, bool>)((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<string>)HideAndSeekTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Deathmatch", (Func<string>)DeathmatchTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Team Deathmatch", (Func<string>)TeamDeathmatchTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Entangled", (Func<string>)EntangledTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Smash Bones", (Func<string>)SmashBonesTooltip); GamemodeManager.RegisterGamemode("Lakatrazz.Juggernaut", (Func<string>)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
UserLibs/DiscordRPC.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using DiscordRPC.Converters; using DiscordRPC.Events; using DiscordRPC.Exceptions; using DiscordRPC.Helper; using DiscordRPC.IO; using DiscordRPC.Logging; using DiscordRPC.Message; using DiscordRPC.RPC; using DiscordRPC.RPC.Commands; using DiscordRPC.RPC.Payload; using DiscordRPC.Registry; using Microsoft.Win32; using Newtonsoft.Json; using Newtonsoft.Json.Linq; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Discord RPC")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Discord RPC")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("819d20d6-8d88-45c1-a4d2-aa21f10abd19")] [assembly: AssemblyFileVersion("1.6.2.72")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyVersion("1.6.2.72")] namespace DiscordRPC { public class Configuration { [JsonProperty("api_endpoint")] public string ApiEndpoint { get; set; } [JsonProperty("cdn_host")] public string CdnHost { get; set; } [JsonProperty("environment")] public string Environment { get; set; } } public sealed class DiscordRpcClient : IDisposable { private ILogger _logger; private RpcConnection connection; private bool _shutdownOnly = true; private object _sync = new object(); public bool HasRegisteredUriScheme { get; private set; } public string ApplicationID { get; private set; } public string SteamID { get; private set; } public int ProcessID { get; private set; } public int MaxQueueSize { get; private set; } public bool IsDisposed { get; private set; } public ILogger Logger { get { return _logger; } set { _logger = value; if (connection != null) { connection.Logger = value; } } } public bool AutoEvents { get; private set; } public bool SkipIdenticalPresence { get; set; } public int TargetPipe { get; private set; } public RichPresence CurrentPresence { get; private set; } public EventType Subscription { get; private set; } public User CurrentUser { get; private set; } public Configuration Configuration { get; private set; } public bool IsInitialized { get; private set; } public bool ShutdownOnly { get { return _shutdownOnly; } set { _shutdownOnly = value; if (connection != null) { connection.ShutdownOnly = value; } } } public event OnReadyEvent OnReady; public event OnCloseEvent OnClose; public event OnErrorEvent OnError; public event OnPresenceUpdateEvent OnPresenceUpdate; public event OnSubscribeEvent OnSubscribe; public event OnUnsubscribeEvent OnUnsubscribe; public event OnJoinEvent OnJoin; public event OnSpectateEvent OnSpectate; public event OnJoinRequestedEvent OnJoinRequested; public event OnConnectionEstablishedEvent OnConnectionEstablished; public event OnConnectionFailedEvent OnConnectionFailed; public event OnRpcMessageEvent OnRpcMessage; public DiscordRpcClient(string applicationID) : this(applicationID, -1, null, autoEvents: true, null) { } public DiscordRpcClient(string applicationID, int pipe = -1, ILogger logger = null, bool autoEvents = true, INamedPipeClient client = null) { if (string.IsNullOrEmpty(applicationID)) { throw new ArgumentNullException("applicationID"); } ApplicationID = applicationID.Trim(); TargetPipe = pipe; ProcessID = Process.GetCurrentProcess().Id; HasRegisteredUriScheme = false; AutoEvents = autoEvents; SkipIdenticalPresence = true; _logger = logger ?? new NullLogger(); connection = new RpcConnection(ApplicationID, ProcessID, TargetPipe, client ?? new ManagedNamedPipeClient(), (!autoEvents) ? 128u : 0u) { ShutdownOnly = _shutdownOnly, Logger = _logger }; connection.OnRpcMessage += delegate(object sender, IMessage msg) { if (this.OnRpcMessage != null) { this.OnRpcMessage(this, msg); } if (AutoEvents) { ProcessMessage(msg); } }; } public IMessage[] Invoke() { if (AutoEvents) { Logger.Error("Cannot Invoke client when AutomaticallyInvokeEvents has been set."); return new IMessage[0]; } IMessage[] array = connection.DequeueMessages(); foreach (IMessage message in array) { ProcessMessage(message); } return array; } private void ProcessMessage(IMessage message) { if (message == null) { return; } switch (message.Type) { case MessageType.PresenceUpdate: lock (_sync) { if (message is PresenceMessage presenceMessage) { if (presenceMessage.Presence == null) { CurrentPresence = null; } else if (CurrentPresence == null) { CurrentPresence = new RichPresence().Merge(presenceMessage.Presence); } else { CurrentPresence.Merge(presenceMessage.Presence); } presenceMessage.Presence = CurrentPresence; } } if (this.OnPresenceUpdate != null) { this.OnPresenceUpdate(this, message as PresenceMessage); } break; case MessageType.Ready: if (message is ReadyMessage readyMessage) { lock (_sync) { Configuration = readyMessage.Configuration; CurrentUser = readyMessage.User; } SynchronizeState(); } if (this.OnReady != null) { this.OnReady(this, message as ReadyMessage); } break; case MessageType.Close: if (this.OnClose != null) { this.OnClose(this, message as CloseMessage); } break; case MessageType.Error: if (this.OnError != null) { this.OnError(this, message as ErrorMessage); } break; case MessageType.JoinRequest: if (Configuration != null && message is JoinRequestMessage joinRequestMessage) { joinRequestMessage.User.SetConfiguration(Configuration); } if (this.OnJoinRequested != null) { this.OnJoinRequested(this, message as JoinRequestMessage); } break; case MessageType.Subscribe: lock (_sync) { SubscribeMessage subscribeMessage = message as SubscribeMessage; Subscription |= subscribeMessage.Event; } if (this.OnSubscribe != null) { this.OnSubscribe(this, message as SubscribeMessage); } break; case MessageType.Unsubscribe: lock (_sync) { UnsubscribeMessage unsubscribeMessage = message as UnsubscribeMessage; Subscription &= ~unsubscribeMessage.Event; } if (this.OnUnsubscribe != null) { this.OnUnsubscribe(this, message as UnsubscribeMessage); } break; case MessageType.Join: if (this.OnJoin != null) { this.OnJoin(this, message as JoinMessage); } break; case MessageType.Spectate: if (this.OnSpectate != null) { this.OnSpectate(this, message as SpectateMessage); } break; case MessageType.ConnectionEstablished: if (this.OnConnectionEstablished != null) { this.OnConnectionEstablished(this, message as ConnectionEstablishedMessage); } break; case MessageType.ConnectionFailed: if (this.OnConnectionFailed != null) { this.OnConnectionFailed(this, message as ConnectionFailedMessage); } break; default: Logger.Error("Message was queued with no appropriate handle! {0}", message.Type); break; } } public void Respond(JoinRequestMessage request, bool acceptRequest) { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } if (!IsInitialized) { throw new UninitializedException(); } connection.EnqueueCommand(new RespondCommand { Accept = acceptRequest, UserID = request.User.ID.ToString() }); } public void SetPresence(RichPresence presence) { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } if (!IsInitialized) { Logger.Warning("The client is not yet initialized, storing the presence as a state instead."); } if (presence == null) { if (!SkipIdenticalPresence || CurrentPresence != null) { connection.EnqueueCommand(new PresenceCommand { PID = ProcessID, Presence = null }); } } else { if (presence.HasSecrets() && !HasRegisteredUriScheme) { throw new BadPresenceException("Cannot send a presence with secrets as this object has not registered a URI scheme. Please enable the uri scheme registration in the DiscordRpcClient constructor."); } if (presence.HasParty() && presence.Party.Max < presence.Party.Size) { throw new BadPresenceException("Presence maximum party size cannot be smaller than the current size."); } if (presence.HasSecrets() && !presence.HasParty()) { Logger.Warning("The presence has set the secrets but no buttons will show as there is no party available."); } if (!SkipIdenticalPresence || !presence.Matches(CurrentPresence)) { connection.EnqueueCommand(new PresenceCommand { PID = ProcessID, Presence = presence.Clone() }); } } lock (_sync) { CurrentPresence = presence?.Clone(); } } public RichPresence Update(Action<RichPresence> func) { if (!IsInitialized) { throw new UninitializedException(); } RichPresence richPresence; lock (_sync) { richPresence = ((CurrentPresence == null) ? new RichPresence() : CurrentPresence.Clone()); } func(richPresence); SetPresence(richPresence); return richPresence; } public RichPresence UpdateType(ActivityType type) { return Update(delegate(RichPresence p) { p.Type = type; }); } public RichPresence UpdateStatusDisplayType(StatusDisplayType type) { return Update(delegate(RichPresence p) { p.StatusDisplay = type; }); } public RichPresence UpdateButtons(Button[] buttons = null) { return Update(delegate(RichPresence p) { p.Buttons = buttons; }); } public RichPresence SetButton(Button button, int index = 0) { return Update(delegate(RichPresence p) { p.Buttons[index] = button; }); } public RichPresence UpdateDetails(string details) { return Update(delegate(RichPresence p) { p.Details = details; }); } public RichPresence UpdateState(string state) { return Update(delegate(RichPresence p) { p.State = state; }); } public RichPresence UpdateParty(Party party) { return Update(delegate(RichPresence p) { p.Party = party; }); } public RichPresence UpdatePartySize(int size) { return Update(delegate(RichPresence p) { if (p.Party == null) { throw new BadPresenceException("Cannot set the size of the party if the party does not exist"); } p.Party.Size = size; }); } public RichPresence UpdatePartySize(int size, int max) { return Update(delegate(RichPresence p) { if (p.Party == null) { throw new BadPresenceException("Cannot set the size of the party if the party does not exist"); } p.Party.Size = size; p.Party.Max = max; }); } public RichPresence UpdateLargeAsset(string key = null, string tooltip = null) { return Update(delegate(RichPresence p) { if (p.Assets == null) { p.Assets = new Assets(); } p.Assets.LargeImageKey = key ?? p.Assets.LargeImageKey; p.Assets.LargeImageText = tooltip ?? p.Assets.LargeImageText; }); } public RichPresence UpdateSmallAsset(string key = null, string tooltip = null) { return Update(delegate(RichPresence p) { if (p.Assets == null) { p.Assets = new Assets(); } p.Assets.SmallImageKey = key ?? p.Assets.SmallImageKey; p.Assets.SmallImageText = tooltip ?? p.Assets.SmallImageText; }); } public RichPresence UpdateSecrets(Secrets secrets) { return Update(delegate(RichPresence p) { p.Secrets = secrets; }); } public RichPresence UpdateStartTime() { return UpdateStartTime(DateTime.UtcNow); } public RichPresence UpdateStartTime(DateTime time) { return Update(delegate(RichPresence p) { if (p.Timestamps == null) { p.Timestamps = new Timestamps(); } p.Timestamps.Start = time; }); } public RichPresence UpdateEndTime() { return UpdateEndTime(DateTime.UtcNow); } public RichPresence UpdateEndTime(DateTime time) { return Update(delegate(RichPresence p) { if (p.Timestamps == null) { p.Timestamps = new Timestamps(); } p.Timestamps.End = time; }); } public RichPresence UpdateClearTime() { return Update(delegate(RichPresence p) { p.Timestamps = null; }); } public void ClearPresence() { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (!IsInitialized) { throw new UninitializedException(); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } SetPresence(null); } public bool RegisterUriScheme(string steamAppID = null, string executable = null) { SchemeInfo schemeInfo = default(SchemeInfo); schemeInfo.ApplicationID = ApplicationID; schemeInfo.SteamAppID = steamAppID; schemeInfo.ExecutablePath = executable ?? Process.GetCurrentProcess().MainModule.FileName; SchemeInfo info = schemeInfo; return HasRegisteredUriScheme = UriScheme.Register(info, _logger); } public void Subscribe(EventType type) { SetSubscription(Subscription | type); } public void Unsubscribe(EventType type) { SetSubscription(Subscription & ~type); } public void SetSubscription(EventType type) { if (IsInitialized) { SubscribeToTypes(Subscription & ~type, isUnsubscribe: true); SubscribeToTypes(~Subscription & type, isUnsubscribe: false); } else { Logger.Warning("Client has not yet initialized, but events are being subscribed too. Storing them as state instead."); } lock (_sync) { Subscription = type; } } private void SubscribeToTypes(EventType type, bool isUnsubscribe) { if (type != 0) { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (!IsInitialized) { throw new UninitializedException(); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } if (!HasRegisteredUriScheme) { throw new InvalidConfigurationException("Cannot subscribe/unsubscribe to an event as this application has not registered a URI Scheme. Call RegisterUriScheme()."); } if ((type & EventType.Spectate) == EventType.Spectate) { connection.EnqueueCommand(new SubscribeCommand { Event = ServerEvent.ActivitySpectate, IsUnsubscribe = isUnsubscribe }); } if ((type & EventType.Join) == EventType.Join) { connection.EnqueueCommand(new SubscribeCommand { Event = ServerEvent.ActivityJoin, IsUnsubscribe = isUnsubscribe }); } if ((type & EventType.JoinRequest) == EventType.JoinRequest) { connection.EnqueueCommand(new SubscribeCommand { Event = ServerEvent.ActivityJoinRequest, IsUnsubscribe = isUnsubscribe }); } } } public void SynchronizeState() { if (!IsInitialized) { throw new UninitializedException(); } SetPresence(CurrentPresence); if (HasRegisteredUriScheme) { SubscribeToTypes(Subscription, isUnsubscribe: false); } } public bool Initialize() { if (IsDisposed) { throw new ObjectDisposedException("Discord IPC Client"); } if (IsInitialized) { throw new UninitializedException("Cannot initialize a client that is already initialized"); } if (connection == null) { throw new ObjectDisposedException("Connection", "Cannot initialize as the connection has been deinitialized"); } return IsInitialized = connection.AttemptConnection(); } public void Deinitialize() { if (!IsInitialized) { throw new UninitializedException("Cannot deinitialize a client that has not been initalized."); } connection.Close(); IsInitialized = false; } public void Dispose() { if (!IsDisposed) { if (IsInitialized) { Deinitialize(); } IsDisposed = true; } } } public enum ActivityType { Playing = 0, Listening = 2, Watching = 3, Competing = 5 } [Serializable] public class Assets { private const string EXTERNAL_KEY_PREFIX = "mp:external"; private string _largeimagekey; private string _largeimagetext; private string _largeimageurl; private string _smallimagekey; private string _smallimagetext; private string _smallimageurl; [JsonProperty(/*Could not decode attribute arguments.*/)] public string LargeImageKey { get { return _largeimagekey; } set { if (!BaseRichPresence.ValidateString(value, out _largeimagekey, useBytes: false, 256)) { throw new StringOutOfRangeException(256); } LargeImageID = null; } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string LargeImageText { get { return _largeimagetext; } set { if (!BaseRichPresence.ValidateString(value, out _largeimagetext, useBytes: false, 128)) { throw new StringOutOfRangeException(128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string LargeImageUrl { get { return _largeimageurl; } set { if (!BaseRichPresence.ValidateString(value, out _largeimageurl, useBytes: false, 256)) { throw new StringOutOfRangeException(256); } if (!BaseRichPresence.ValidateUrl(_largeimageurl)) { throw new ArgumentException("Url must be a valid URI"); } } } [JsonIgnore] public string LargeImageID { get; private set; } [JsonIgnore] public bool IsLargeImageKeyExternal { get; private set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public string SmallImageKey { get { return _smallimagekey; } set { if (!BaseRichPresence.ValidateString(value, out _smallimagekey, useBytes: false, 256)) { throw new StringOutOfRangeException(256); } SmallImageID = null; } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string SmallImageText { get { return _smallimagetext; } set { if (!BaseRichPresence.ValidateString(value, out _smallimagetext, useBytes: false, 128)) { throw new StringOutOfRangeException(128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string SmallImageUrl { get { return _smallimageurl; } set { if (!BaseRichPresence.ValidateString(value, out _smallimageurl, useBytes: false, 256)) { throw new StringOutOfRangeException(256); } if (!BaseRichPresence.ValidateUrl(_smallimageurl)) { throw new ArgumentException("Url must be a valid URI"); } } } [JsonIgnore] public string SmallImageID { get; private set; } [JsonIgnore] public bool IsSmallImageKeyExternal { get; private set; } internal void Merge(Assets other) { _smallimagetext = other._smallimagetext; _smallimageurl = other._smallimageurl; _largeimagetext = other._largeimagetext; _largeimageurl = other._largeimageurl; string text = other._largeimagekey ?? ""; ulong result; if (text.StartsWith("mp:external")) { IsLargeImageKeyExternal = true; LargeImageID = text; } else if (ulong.TryParse(text, out result)) { IsLargeImageKeyExternal = false; LargeImageID = text; } else { IsLargeImageKeyExternal = false; LargeImageID = null; _largeimagekey = text; } string text2 = other._smallimagekey ?? ""; if (text2.StartsWith("mp:external")) { IsSmallImageKeyExternal = true; SmallImageID = text2; } else if (ulong.TryParse(text2, out result)) { IsSmallImageKeyExternal = false; SmallImageID = text2; } else { IsSmallImageKeyExternal = false; SmallImageID = null; _smallimagekey = text2; } } } public class Button { private string _label; private string _url; [JsonProperty("label")] public string Label { get { return _label; } set { if (!BaseRichPresence.ValidateString(value, out _label, useBytes: true, 31, Encoding.UTF8)) { throw new StringOutOfRangeException(31); } } } [JsonProperty("url")] public string Url { get { return _url; } set { if (!BaseRichPresence.ValidateString(value, out _url, useBytes: false, 512)) { throw new StringOutOfRangeException(512); } if (!BaseRichPresence.ValidateUrl(_url)) { throw new ArgumentException("Url must be a valid URI"); } } } } [Serializable] public class Party { public enum PrivacySetting { Private, Public } private string _partyid; [JsonProperty(/*Could not decode attribute arguments.*/)] public string ID { get { return _partyid; } set { _partyid = value.GetNullOrString(); } } [JsonIgnore] public int Size { get; set; } [JsonIgnore] public int Max { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public PrivacySetting Privacy { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] private int[] _size { get { int num = Math.Max(1, Size); return new int[2] { num, Math.Max(num, Max) }; } set { if (value.Length != 2) { Size = 0; Max = 0; } else { Size = value[0]; Max = value[1]; } } } } [Serializable] [JsonObject(/*Could not decode attribute arguments.*/)] public class BaseRichPresence { protected internal string _state; protected internal string _stateUrl; protected internal string _details; protected internal string _detailsUrl; [JsonProperty(/*Could not decode attribute arguments.*/)] public string State { get { return _state; } set { if (!ValidateString(value, out _state, useBytes: false, 128)) { throw new StringOutOfRangeException("State", 0, 128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string StateUrl { get { return _stateUrl; } set { if (!ValidateString(value, out _stateUrl, useBytes: false, 256)) { throw new StringOutOfRangeException(256); } if (!ValidateUrl(_stateUrl)) { throw new ArgumentException("Url must be a valid URI"); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string Details { get { return _details; } set { if (!ValidateString(value, out _details, useBytes: false, 128)) { throw new StringOutOfRangeException(128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string DetailsUrl { get { return _detailsUrl; } set { if (!ValidateString(value, out _detailsUrl, useBytes: false, 256)) { throw new StringOutOfRangeException(256); } if (!ValidateUrl(_detailsUrl)) { throw new ArgumentException("Url must be a valid URI"); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public Timestamps Timestamps { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public Assets Assets { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public Party Party { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public Secrets Secrets { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public ActivityType Type { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public StatusDisplayType StatusDisplay { get; set; } public bool HasTimestamps() { if (Timestamps != null) { if (!Timestamps.Start.HasValue) { return Timestamps.End.HasValue; } return true; } return false; } public bool HasAssets() { return Assets != null; } public bool HasParty() { if (Party != null) { return Party.ID != null; } return false; } public bool HasSecrets() { if (Secrets != null) { if (Secrets.JoinSecret == null) { return Secrets.SpectateSecret != null; } return true; } return false; } internal static bool ValidateString(string str, out string result, bool useBytes, int length, Encoding encoding = null) { result = str; if (str == null) { return true; } string text = str.Trim(); if ((useBytes && !text.WithinLength(length, encoding)) || text.Length > length) { return false; } result = text.GetNullOrString(); return true; } internal static bool ValidateUrl(string url) { if (string.IsNullOrEmpty(url)) { return true; } if (!Uri.TryCreate(url, UriKind.Absolute, out Uri _)) { return false; } return true; } public static implicit operator bool(BaseRichPresence presence) { return presence != null; } internal virtual bool Matches(RichPresence other) { if (other == null) { return false; } if (State != other.State || StateUrl != other.StateUrl || Details != other.Details || DetailsUrl != other.DetailsUrl || Type != other.Type) { return false; } if (Timestamps != null) { if (other.Timestamps == null || other.Timestamps.StartUnixMilliseconds != Timestamps.StartUnixMilliseconds || other.Timestamps.EndUnixMilliseconds != Timestamps.EndUnixMilliseconds) { return false; } } else if (other.Timestamps != null) { return false; } if (Secrets != null) { if (other.Secrets == null || other.Secrets.JoinSecret != Secrets.JoinSecret || other.Secrets.SpectateSecret != Secrets.SpectateSecret) { return false; } } else if (other.Secrets != null) { return false; } if (Party != null) { if (other.Party == null || other.Party.ID != Party.ID || other.Party.Max != Party.Max || other.Party.Size != Party.Size || other.Party.Privacy != Party.Privacy) { return false; } } else if (other.Party != null) { return false; } if (Assets != null) { if (other.Assets == null || other.Assets.LargeImageKey != Assets.LargeImageKey || other.Assets.LargeImageText != Assets.LargeImageText || other.Assets.LargeImageUrl != Assets.LargeImageUrl || other.Assets.SmallImageKey != Assets.SmallImageKey || other.Assets.SmallImageText != Assets.SmallImageText || other.Assets.SmallImageUrl != Assets.SmallImageUrl) { return false; } } else if (other.Assets != null) { return false; } return true; } public RichPresence ToRichPresence() { RichPresence richPresence = new RichPresence(); richPresence.State = State; richPresence.StateUrl = StateUrl; richPresence.Details = Details; richPresence.DetailsUrl = DetailsUrl; richPresence.Type = Type; richPresence.StatusDisplay = StatusDisplay; richPresence.Party = ((!HasParty()) ? Party : null); richPresence.Secrets = ((!HasSecrets()) ? Secrets : null); if (HasAssets()) { richPresence.Assets = new Assets { SmallImageKey = Assets.SmallImageKey, SmallImageText = Assets.SmallImageText, SmallImageUrl = Assets.SmallImageUrl, LargeImageKey = Assets.LargeImageKey, LargeImageText = Assets.LargeImageText, LargeImageUrl = Assets.LargeImageUrl }; } if (HasTimestamps()) { richPresence.Timestamps = new Timestamps(); if (Timestamps.Start.HasValue) { richPresence.Timestamps.Start = Timestamps.Start; } if (Timestamps.End.HasValue) { richPresence.Timestamps.End = Timestamps.End; } } return richPresence; } } public sealed class RichPresence : BaseRichPresence { [JsonProperty(/*Could not decode attribute arguments.*/)] public Button[] Buttons { get; set; } public bool HasButtons() { if (Buttons != null) { return Buttons.Length != 0; } return false; } public RichPresence WithState(string state) { base.State = state; return this; } public RichPresence WithStateUrl(string stateUrl) { base.StateUrl = stateUrl; return this; } public RichPresence WithDetails(string details) { base.Details = details; return this; } public RichPresence WithDetailsUrl(string detailsUrl) { base.DetailsUrl = detailsUrl; return this; } public RichPresence WithType(ActivityType type) { base.Type = type; return this; } public RichPresence WithStatusDisplay(StatusDisplayType statusDisplay) { base.StatusDisplay = statusDisplay; return this; } public RichPresence WithTimestamps(Timestamps timestamps) { base.Timestamps = timestamps; return this; } public RichPresence WithAssets(Assets assets) { base.Assets = assets; return this; } public RichPresence WithParty(Party party) { base.Party = party; return this; } public RichPresence WithSecrets(Secrets secrets) { base.Secrets = secrets; return this; } public RichPresence WithButtons(Button topButton, Button bottomButton = null) { if (topButton != null && bottomButton != null) { Buttons = new Button[2] { topButton, bottomButton }; } else if (topButton == null && bottomButton == null) { Buttons = null; } else { Buttons = new Button[1] { topButton ?? bottomButton }; } return this; } public RichPresence Clone() { return new RichPresence { State = ((_state != null) ? (_state.Clone() as string) : null), StateUrl = ((_stateUrl != null) ? (_stateUrl.Clone() as string) : null), Details = ((_details != null) ? (_details.Clone() as string) : null), DetailsUrl = ((_detailsUrl != null) ? (_detailsUrl.Clone() as string) : null), Type = base.Type, StatusDisplay = base.StatusDisplay, Buttons = ((!HasButtons()) ? null : (Buttons.Clone() as Button[])), Secrets = ((!HasSecrets()) ? null : new Secrets { JoinSecret = ((base.Secrets.JoinSecret != null) ? (base.Secrets.JoinSecret.Clone() as string) : null), SpectateSecret = ((base.Secrets.SpectateSecret != null) ? (base.Secrets.SpectateSecret.Clone() as string) : null) }), Timestamps = ((!HasTimestamps()) ? null : new Timestamps { Start = base.Timestamps.Start, End = base.Timestamps.End }), Assets = ((!HasAssets()) ? null : new Assets { LargeImageKey = ((base.Assets.LargeImageKey != null) ? (base.Assets.LargeImageKey.Clone() as string) : null), LargeImageText = ((base.Assets.LargeImageText != null) ? (base.Assets.LargeImageText.Clone() as string) : null), LargeImageUrl = ((base.Assets.LargeImageUrl != null) ? (base.Assets.LargeImageUrl.Clone() as string) : null), SmallImageKey = ((base.Assets.SmallImageKey != null) ? (base.Assets.SmallImageKey.Clone() as string) : null), SmallImageText = ((base.Assets.SmallImageText != null) ? (base.Assets.SmallImageText.Clone() as string) : null), SmallImageUrl = ((base.Assets.SmallImageUrl != null) ? (base.Assets.SmallImageUrl.Clone() as string) : null) }), Party = ((!HasParty()) ? null : new Party { ID = base.Party.ID, Size = base.Party.Size, Max = base.Party.Max, Privacy = base.Party.Privacy }) }; } internal RichPresence Merge(BaseRichPresence presence) { _state = presence.State; _stateUrl = presence.StateUrl; _details = presence.Details; _detailsUrl = presence.DetailsUrl; base.Type = presence.Type; base.StatusDisplay = presence.StatusDisplay; base.Party = presence.Party; base.Timestamps = presence.Timestamps; base.Secrets = presence.Secrets; if (presence.HasAssets()) { if (!HasAssets()) { base.Assets = presence.Assets; } else { base.Assets.Merge(presence.Assets); } } else { base.Assets = null; } return this; } internal override bool Matches(RichPresence other) { if (!base.Matches(other)) { return false; } if ((Buttons == null) ^ (other.Buttons == null)) { return false; } if (Buttons != null) { if (Buttons.Length != other.Buttons.Length) { return false; } for (int i = 0; i < Buttons.Length; i++) { Button button = Buttons[i]; Button button2 = other.Buttons[i]; if (button.Label != button2.Label || button.Url != button2.Url) { return false; } } } return true; } public static implicit operator bool(RichPresence presesnce) { return presesnce != null; } } internal sealed class RichPresenceResponse : BaseRichPresence { [JsonProperty("application_id")] public string ClientID { get; private set; } [JsonProperty("name")] public string Name { get; private set; } } [Serializable] public class Secrets { [Obsolete("This feature has been deprecated my Mason in issue #152 on the offical library. Was originally used as a Notify Me feature, it has been replaced with Join / Spectate.", true)] public string MatchSecret; private string _joinSecret; private string _spectateSecret; [JsonProperty(/*Could not decode attribute arguments.*/)] public string JoinSecret { get { return _joinSecret; } set { if (!BaseRichPresence.ValidateString(value, out _joinSecret, useBytes: false, 128)) { throw new StringOutOfRangeException(128); } } } [JsonProperty(/*Could not decode attribute arguments.*/)] public string SpectateSecret { get { return _spectateSecret; } set { if (!BaseRichPresence.ValidateString(value, out _spectateSecret, useBytes: false, 128)) { throw new StringOutOfRangeException(128); } } } public static Encoding Encoding => Encoding.UTF8; public static int SecretLength => 128; public static string CreateSecret(Random random) { byte[] array = new byte[SecretLength]; random.NextBytes(array); return Encoding.GetString(array); } public static string CreateFriendlySecret(Random random) { string text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < SecretLength; i++) { stringBuilder.Append(text[random.Next(text.Length)]); } return stringBuilder.ToString(); } } public enum StatusDisplayType { Name, State, Details } [Serializable] public class Timestamps { public static Timestamps Now => new Timestamps(DateTime.UtcNow); [JsonIgnore] public DateTime? Start { get; set; } [JsonIgnore] public DateTime? End { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public ulong? StartUnixMilliseconds { get { if (!Start.HasValue) { return null; } return ToUnixMilliseconds(Start.Value); } set { Start = (value.HasValue ? new DateTime?(FromUnixMilliseconds(value.Value)) : null); } } [JsonProperty(/*Could not decode attribute arguments.*/)] public ulong? EndUnixMilliseconds { get { if (!End.HasValue) { return null; } return ToUnixMilliseconds(End.Value); } set { End = (value.HasValue ? new DateTime?(FromUnixMilliseconds(value.Value)) : null); } } public static Timestamps FromTimeSpan(double seconds) { return FromTimeSpan(TimeSpan.FromSeconds(seconds)); } public static Timestamps FromTimeSpan(TimeSpan timespan) { return new Timestamps { Start = DateTime.UtcNow, End = DateTime.UtcNow + timespan }; } public Timestamps() { Start = null; End = null; } public Timestamps(DateTime start) { Start = start; End = null; } public Timestamps(DateTime start, DateTime end) { Start = start; End = end; } public static DateTime FromUnixMilliseconds(ulong unixTime) { return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(Convert.ToDouble(unixTime)); } public static ulong ToUnixMilliseconds(DateTime date) { DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); return Convert.ToUInt64((date - dateTime).TotalMilliseconds); } } public class User { public enum AvatarFormat { PNG, JPEG, WebP, GIF } public enum AvatarSize { x16 = 0x10, x32 = 0x20, x64 = 0x40, x128 = 0x80, x256 = 0x100, x512 = 0x200, x1024 = 0x400, x2048 = 0x800 } public struct AvatarDecorationData { [JsonProperty("asset")] public string Asset { get; private set; } [JsonProperty("skuId")] public string SKU { get; private set; } } [Flags] public enum Flag { None = 0, Employee = 1, Partner = 2, HypeSquad = 4, BugHunter = 8, HouseBravery = 0x40, HouseBrilliance = 0x80, HouseBalance = 0x100, EarlySupporter = 0x200, TeamUser = 0x400, BugHunterLevel2 = 0x4000, VerifiedBot = 0x10000, VerifiedDeveloper = 0x20000, CertifiedModerator = 0x40000, BotHttpInteractions = 0x80000, ActiveDeveloper = 0x400000 } public enum PremiumType { None, NitroClassic, Nitro, NitroBasic } [JsonProperty("id")] public ulong ID { get; private set; } [JsonProperty("username")] public string Username { get; private set; } [JsonProperty("discriminator")] [Obsolete("Discord no longer uses discriminators.")] public int Discriminator { get; private set; } [JsonProperty("global_name")] public string DisplayName { get; private set; } [JsonProperty("avatar")] public string Avatar { get; private set; } public bool IsAvatarAnimated { get { if (Avatar != null) { return Avatar.StartsWith("a_"); } return false; } } [JsonProperty("avatar_decoration_data")] public AvatarDecorationData? AvatarDecoration { get; private set; } [JsonProperty("bot")] public bool Bot { get; private set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public Flag Flags { get; private set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public PremiumType Premium { get; private set; } public string CdnEndpoint { get; private set; } internal User() { CdnEndpoint = "cdn.discordapp.com"; } internal void SetConfiguration(Configuration configuration) { CdnEndpoint = configuration.CdnHost; } public string GetAvatarURL() { return GetAvatarURL(AvatarFormat.PNG, AvatarSize.x128); } public string GetAvatarURL(AvatarFormat format) { return GetAvatarURL(format, AvatarSize.x128); } public string GetAvatarURL(AvatarFormat format, AvatarSize size) { string text = $"/avatars/{ID}/{Avatar}"; if (string.IsNullOrEmpty(Avatar)) { if (format != 0) { throw new BadImageFormatException("The user has no avatar and the requested format " + format.ToString() + " is not supported. (Only supports PNG)."); } int num = (int)((ID >> 22) % 6); if (Discriminator > 0) { num = Discriminator % 5; } text = $"/embed/avatars/{num}"; } return $"https://{CdnEndpoint}{text}{GetAvatarExtension(format)}?size={(int)size}&animated=true"; } public string GetAvatarDecorationURL() { return GetAvatarDecorationURL(AvatarFormat.PNG); } public string GetAvatarDecorationURL(AvatarFormat format) { if (!AvatarDecoration.HasValue) { return null; } string arg = "/avatar-decoration-presets/" + AvatarDecoration.Value.Asset; return $"https://{CdnEndpoint}{arg}{GetAvatarExtension(format)}"; } public string GetAvatarExtension(AvatarFormat format) { return "." + format.ToString().ToLowerInvariant(); } public override string ToString() { if (!string.IsNullOrEmpty(DisplayName)) { return DisplayName; } if (Discriminator != 0) { return Username + "#" + Discriminator.ToString("D4"); } return Username; } } [Flags] public enum EventType { None = 0, Spectate = 1, Join = 2, JoinRequest = 4 } } namespace DiscordRPC.RPC { internal class RpcConnection : IDisposable { public static readonly int VERSION = 1; public static readonly int POLL_RATE = 1000; private static readonly bool CLEAR_ON_SHUTDOWN = true; private static readonly bool LOCK_STEP = false; private ILogger _logger; private RpcState _state; private readonly object l_states = new object(); private Configuration _configuration; private readonly object l_config = new object(); private volatile bool aborting; private volatile bool shutdown; private string applicationID; private int processID; private long nonce; private Thread thread; private INamedPipeClient namedPipe; private int targetPipe; private readonly object l_rtqueue = new object(); private readonly uint _maxRtQueueSize; private Queue<ICommand> _rtqueue; private readonly object l_rxqueue = new object(); private readonly uint _maxRxQueueSize; private Queue<IMessage> _rxqueue; private AutoResetEvent queueUpdatedEvent = new AutoResetEvent(initialState: false); private BackoffDelay delay; public ILogger Logger { get { return _logger; } set { _logger = value; if (namedPipe != null) { namedPipe.Logger = value; } } } public RpcState State { get { lock (l_states) { return _state; } } } public Configuration Configuration { get { Configuration configuration = null; lock (l_config) { return _configuration; } } } public bool IsRunning => thread != null; public bool ShutdownOnly { get; set; } public event OnRpcMessageEvent OnRpcMessage; public RpcConnection(string applicationID, int processID, int targetPipe, INamedPipeClient client, uint maxRxQueueSize = 128u, uint maxRtQueueSize = 512u) { this.applicationID = applicationID; this.processID = processID; this.targetPipe = targetPipe; namedPipe = client; ShutdownOnly = true; Logger = new ConsoleLogger(); delay = new BackoffDelay(500, 60000); _maxRtQueueSize = maxRtQueueSize; _rtqueue = new Queue<ICommand>((int)(_maxRtQueueSize + 1)); _maxRxQueueSize = maxRxQueueSize; _rxqueue = new Queue<IMessage>((int)(_maxRxQueueSize + 1)); nonce = 0L; } private long GetNextNonce() { nonce++; return nonce; } internal void EnqueueCommand(ICommand command) { Logger.Trace("Enqueue Command: {0}", command.GetType().FullName); if (aborting || shutdown) { return; } lock (l_rtqueue) { if (_rtqueue.Count == _maxRtQueueSize) { Logger.Error("Too many enqueued commands, dropping oldest one. Maybe you are pushing new presences to fast?"); _rtqueue.Dequeue(); } _rtqueue.Enqueue(command); } } private void EnqueueMessage(IMessage message) { try { if (this.OnRpcMessage != null) { this.OnRpcMessage(this, message); } } catch (Exception ex) { Logger.Error("Unhandled Exception while processing event: {0}", ex.GetType().FullName); Logger.Error(ex.Message); Logger.Error(ex.StackTrace); } if (_maxRxQueueSize == 0) { Logger.Trace("Enqueued Message, but queue size is 0."); return; } Logger.Trace("Enqueue Message: {0}", message.Type); lock (l_rxqueue) { if (_rxqueue.Count == _maxRxQueueSize) { Logger.Warning("Too many enqueued messages, dropping oldest one."); _rxqueue.Dequeue(); } _rxqueue.Enqueue(message); } } internal IMessage DequeueMessage() { lock (l_rxqueue) { if (_rxqueue.Count == 0) { return null; } return _rxqueue.Dequeue(); } } internal IMessage[] DequeueMessages() { lock (l_rxqueue) { IMessage[] result = _rxqueue.ToArray(); _rxqueue.Clear(); return result; } } private void MainLoop() { Logger.Info("RPC Connection Started"); if (Logger.Level <= LogLevel.Trace) { Logger.Trace("============================"); Logger.Trace("Assembly: " + Assembly.GetAssembly(typeof(RichPresence)).FullName); Logger.Trace("Pipe: " + namedPipe.GetType().FullName); Logger.Trace("Platform: " + Environment.OSVersion.ToString()); Logger.Trace("DotNet: " + Environment.Version.ToString()); Logger.Trace("applicationID: " + applicationID); Logger.Trace("targetPipe: " + targetPipe); ILogger logger = Logger; int pOLL_RATE = POLL_RATE; logger.Trace("POLL_RATE: " + pOLL_RATE); ILogger logger2 = Logger; uint maxRtQueueSize = _maxRtQueueSize; logger2.Trace("_maxRtQueueSize: " + maxRtQueueSize); ILogger logger3 = Logger; maxRtQueueSize = _maxRxQueueSize; logger3.Trace("_maxRxQueueSize: " + maxRtQueueSize); Logger.Trace("============================"); } while (!aborting && !shutdown) { try { if (namedPipe == null) { Logger.Error("Something bad has happened with our pipe client!"); aborting = true; return; } Logger.Trace("Connecting to the pipe through the {0}", namedPipe.GetType().FullName); if (namedPipe.Connect(targetPipe)) { Logger.Trace("Connected to the pipe. Attempting to establish handshake..."); EnqueueMessage(new ConnectionEstablishedMessage { ConnectedPipe = namedPipe.ConnectedPipe }); EstablishHandshake(); Logger.Trace("Connection Established. Starting reading loop..."); bool flag = true; while (flag && !aborting && !shutdown && namedPipe.IsConnected) { if (namedPipe.ReadFrame(out var frame)) { Logger.Trace("Read Payload: {0}", frame.Opcode); switch (frame.Opcode) { case Opcode.Close: { ClosePayload @object = frame.GetObject<ClosePayload>(); Logger.Warning("We have been told to terminate by discord: ({0}) {1}", @object.Code, @object.Reason); EnqueueMessage(new CloseMessage { Code = @object.Code, Reason = @object.Reason }); flag = false; break; } case Opcode.Ping: Logger.Trace("PING"); frame.Opcode = Opcode.Pong; namedPipe.WriteFrame(frame); break; case Opcode.Pong: Logger.Trace("PONG"); break; case Opcode.Frame: { if (shutdown) { Logger.Warning("Skipping frame because we are shutting down."); break; } if (frame.Data == null) { Logger.Error("We received no data from the frame so we cannot get the event payload!"); break; } EventPayload eventPayload = null; try { eventPayload = frame.GetObject<EventPayload>(); } catch (Exception ex) { Logger.Error("Failed to parse event! {0}", ex.Message); Logger.Error("Data: {0}", frame.Message); } try { if (eventPayload != null) { ProcessFrame(eventPayload); } } catch (Exception ex2) { Logger.Error("Failed to process event! {0}", ex2.Message); Logger.Error("Data: {0}", frame.Message); } break; } default: Logger.Error("Invalid opcode: {0}", frame.Opcode); flag = false; break; } } if (!aborting && namedPipe.IsConnected) { ProcessCommandQueue(); queueUpdatedEvent.WaitOne(POLL_RATE); } } Logger.Trace("Left main read loop for some reason. Aborting: {0}, Shutting Down: {1}", aborting, shutdown); } else { Logger.Error("Failed to connect for some reason."); EnqueueMessage(new ConnectionFailedMessage { FailedPipe = targetPipe }); } if (!aborting && !shutdown) { long num = delay.NextDelay(); Logger.Trace("Waiting {0}ms before attempting to connect again", num); Thread.Sleep(delay.NextDelay()); } } catch (Exception ex3) { Logger.Error("Unhandled Exception: {0}", ex3.GetType().FullName); Logger.Error(ex3.Message); Logger.Error(ex3.StackTrace); } finally { if (namedPipe.IsConnected) { Logger.Trace("Closing the named pipe."); namedPipe.Close(); } SetConnectionState(RpcState.Disconnected); } } Logger.Trace("Left Main Loop"); if (namedPipe != null) { namedPipe.Dispose(); } Logger.Info("Thread Terminated, no longer performing RPC connection."); } private void ProcessFrame(EventPayload response) { //IL_01c6: Unknown result type (might be due to invalid IL or missing references) Logger.Info("Handling Response. Cmd: {0}, Event: {1}", response.Command, response.Event); if (response.Event.HasValue && response.Event.Value == ServerEvent.Error) { Logger.Error("Error received from the RPC"); ErrorMessage @object = response.GetObject<ErrorMessage>(); Logger.Error("Server responded with an error message: ({0}) {1}", @object.Code.ToString(), @object.Message); EnqueueMessage(@object); } else if (State == RpcState.Connecting && response.Command == Command.Dispatch && response.Event.HasValue && response.Event.Value == ServerEvent.Ready) { Logger.Info("Connection established with the RPC"); SetConnectionState(RpcState.Connected); delay.Reset(); ReadyMessage object2 = response.GetObject<ReadyMessage>(); lock (l_config) { _configuration = object2.Configuration; object2.User.SetConfiguration(_configuration); } EnqueueMessage(object2); } else if (State == RpcState.Connected) { switch (response.Command) { case Command.Dispatch: ProcessDispatch(response); break; case Command.SetActivity: { if (response.Data == null) { EnqueueMessage(new PresenceMessage()); break; } RichPresenceResponse object3 = response.GetObject<RichPresenceResponse>(); EnqueueMessage(new PresenceMessage(object3)); break; } case Command.Subscribe: case Command.Unsubscribe: { ((Collection<JsonConverter>)(object)new JsonSerializer().Converters).Add((JsonConverter)(object)new EnumSnakeCaseConverter()); ServerEvent value = response.GetObject<EventPayload>().Event.Value; if (response.Command == Command.Subscribe) { EnqueueMessage(new SubscribeMessage(value)); } else { EnqueueMessage(new UnsubscribeMessage(value)); } break; } case Command.SendActivityJoinInvite: Logger.Trace("Got invite response ack."); break; case Command.CloseActivityJoinRequest: Logger.Trace("Got invite response reject ack."); break; default: Logger.Error("Unknown frame was received! {0}", response.Command); break; } } else { Logger.Trace("Received a frame while we are disconnected. Ignoring. Cmd: {0}, Event: {1}", response.Command, response.Event); } } private void ProcessDispatch(EventPayload response) { if (response.Command == Command.Dispatch && response.Event.HasValue) { switch (response.Event.Value) { case ServerEvent.ActivitySpectate: { SpectateMessage object3 = response.GetObject<SpectateMessage>(); EnqueueMessage(object3); break; } case ServerEvent.ActivityJoin: { JoinMessage object2 = response.GetObject<JoinMessage>(); EnqueueMessage(object2); break; } case ServerEvent.ActivityJoinRequest: { JoinRequestMessage @object = response.GetObject<JoinRequestMessage>(); EnqueueMessage(@object); break; } default: Logger.Warning("Ignoring {0}", response.Event.Value); break; } } } private void ProcessCommandQueue() { if (State != RpcState.Connected) { return; } if (aborting) { Logger.Warning("We have been told to write a queue but we have also been aborted."); } bool flag = true; ICommand command = null; while (flag && namedPipe.IsConnected) { lock (l_rtqueue) { flag = _rtqueue.Count > 0; if (!flag) { break; } command = _rtqueue.Peek(); } if (shutdown || (!aborting && LOCK_STEP)) { flag = false; } IPayload payload = command.PreparePayload(GetNextNonce()); Logger.Trace("Attempting to send payload: {0}", payload.Command); PipeFrame frame = default(PipeFrame); if (command is CloseCommand) { SendHandwave(); Logger.Trace("Handwave sent, ending queue processing."); lock (l_rtqueue) { _rtqueue.Dequeue(); break; } } if (aborting) { Logger.Warning("- skipping frame because of abort."); lock (l_rtqueue) { _rtqueue.Dequeue(); } continue; } frame.SetObject(Opcode.Frame, payload); Logger.Trace("Sending payload: {0}", payload.Command); if (namedPipe.WriteFrame(frame)) { Logger.Trace("Sent Successfully."); lock (l_rtqueue) { _rtqueue.Dequeue(); } continue; } Logger.Warning("Something went wrong during writing!"); break; } } private void EstablishHandshake() { Logger.Trace("Attempting to establish a handshake..."); if (State != 0) { Logger.Error("State must be disconnected in order to start a handshake!"); return; } Logger.Trace("Sending Handshake..."); if (!namedPipe.WriteFrame(new PipeFrame(Opcode.Handshake, new Handshake { Version = VERSION, ClientID = applicationID }))) { Logger.Error("Failed to write a handshake."); } else { SetConnectionState(RpcState.Connecting); } } private void SendHandwave() { Logger.Info("Attempting to wave goodbye..."); if (State == RpcState.Disconnected) { Logger.Error("State must NOT be disconnected in order to send a handwave!"); } else if (!namedPipe.WriteFrame(new PipeFrame(Opcode.Close, new Handshake { Version = VERSION, ClientID = applicationID }))) { Logger.Error("failed to write a handwave."); } } public bool AttemptConnection() { Logger.Info("Attempting a new connection"); if (thread != null) { Logger.Error("Cannot attempt a new connection as the previous connection thread is not null!"); return false; } if (State != 0) { Logger.Warning("Cannot attempt a new connection as the previous connection hasn't changed state yet."); return false; } if (aborting) { Logger.Error("Cannot attempt a new connection while aborting!"); return false; } thread = new Thread(MainLoop); thread.Name = "Discord IPC Thread"; thread.IsBackground = true; thread.Start(); return true; } private void SetConnectionState(RpcState state) { Logger.Trace("Setting the connection state to {0}", state.ToString().ToSnakeCase().ToUpperInvariant()); lock (l_states) { _state = state; } } public void Shutdown() { Logger.Trace("Initiated shutdown procedure"); shutdown = true; lock (l_rtqueue) { _rtqueue.Clear(); if (CLEAR_ON_SHUTDOWN) { _rtqueue.Enqueue(new PresenceCommand { PID = processID, Presence = null }); } _rtqueue.Enqueue(new CloseCommand()); } queueUpdatedEvent.Set(); } public void Close() { if (thread == null) { Logger.Error("Cannot close as it is not available!"); return; } if (aborting) { Logger.Error("Cannot abort as it has already been aborted"); return; } if (ShutdownOnly) { Shutdown(); return; } Logger.Trace("Updating Abort State..."); aborting = true; queueUpdatedEvent.Set(); } public void Dispose() { ShutdownOnly = false; Close(); } } internal enum RpcState { Disconnected, Connecting, Connected } } namespace DiscordRPC.RPC.Payload { internal class ClosePayload : IPayload { [JsonProperty("code")] public int Code { get; set; } [JsonProperty("message")] public string Reason { get; set; } [JsonConstructor] public ClosePayload() { Code = -1; Reason = ""; } } internal enum Command { [EnumValue("DISPATCH")] Dispatch, [EnumValue("SET_ACTIVITY")] SetActivity, [EnumValue("SUBSCRIBE")] Subscribe, [EnumValue("UNSUBSCRIBE")] Unsubscribe, [EnumValue("SEND_ACTIVITY_JOIN_INVITE")] SendActivityJoinInvite, [EnumValue("CLOSE_ACTIVITY_JOIN_REQUEST")] CloseActivityJoinRequest, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] Authorize, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] Authenticate, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetGuild, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetGuilds, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetChannel, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetChannels, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] SetUserVoiceSettings, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] SelectVoiceChannel, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetSelectedVoiceChannel, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] SelectTextChannel, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] GetVoiceSettings, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] SetVoiceSettings, [Obsolete("This value is appart of the RPC API and is not supported by this library.", true)] CaptureShortcut } internal abstract class IPayload { [JsonProperty("cmd")] [JsonConverter(typeof(EnumSnakeCaseConverter))] public Command Command { get; set; } [JsonProperty("nonce")] public string Nonce { get; set; } protected IPayload() { } protected IPayload(long nonce) { Nonce = nonce.ToString(); } public override string ToString() { return $"Payload || Command: {Command}, Nonce: {Nonce}"; } } internal class ArgumentPayload : IPayload { [JsonProperty(/*Could not decode attribute arguments.*/)] public JObject Arguments { get; set; } public ArgumentPayload() { Arguments = null; } public ArgumentPayload(long nonce) : base(nonce) { Arguments = null; } public ArgumentPayload(object args, long nonce) : base(nonce) { SetObject(args); } public void SetObject(object obj) { Arguments = JObject.FromObject(obj); } public T GetObject<T>() { return ((JToken)Arguments).ToObject<T>(); } public override string ToString() { return "Argument " + base.ToString(); } } internal class EventPayload : IPayload { [JsonProperty(/*Could not decode attribute arguments.*/)] public JObject Data { get; set; } [JsonProperty("evt")] [JsonConverter(typeof(EnumSnakeCaseConverter))] public ServerEvent? Event { get; set; } public EventPayload() { Data = null; } public EventPayload(long nonce) : base(nonce) { Data = null; } public T GetObject<T>() { if (Data == null) { return default(T); } return ((JToken)Data).ToObject<T>(); } public override string ToString() { return "Event " + base.ToString() + ", Event: " + (Event.HasValue ? Event.ToString() : "N/A"); } } internal enum ServerEvent { [EnumValue("READY")] Ready, [EnumValue("ERROR")] Error, [EnumValue("ACTIVITY_JOIN")] ActivityJoin, [EnumValue("ACTIVITY_SPECTATE")] ActivitySpectate, [EnumValue("ACTIVITY_JOIN_REQUEST")] ActivityJoinRequest } } namespace DiscordRPC.RPC.Commands { internal class CloseCommand : ICommand { [JsonProperty("close_reason")] public string value = "Unity 5.5 doesn't handle thread aborts. Can you please close me discord?"; [JsonProperty("pid")] public int PID { get; set; } public IPayload PreparePayload(long nonce) { return new ArgumentPayload { Command = Command.Dispatch, Nonce = null, Arguments = null }; } } internal interface ICommand { IPayload PreparePayload(long nonce); } internal class PresenceCommand : ICommand { [JsonProperty("pid")] public int PID { get; set; } [JsonProperty("activity")] public RichPresence Presence { get; set; } public IPayload PreparePayload(long nonce) { return new ArgumentPayload(this, nonce) { Command = Command.SetActivity }; } } internal class RespondCommand : ICommand { [JsonProperty("user_id")] public string UserID { get; set; } [JsonIgnore] public bool Accept { get; set; } public IPayload PreparePayload(long nonce) { return new ArgumentPayload(this, nonce) { Command = (Accept ? Command.SendActivityJoinInvite : Command.CloseActivityJoinRequest) }; } } internal class SubscribeCommand : ICommand { public ServerEvent Event { get; set; } public bool IsUnsubscribe { get; set; } public IPayload PreparePayload(long nonce) { return new EventPayload(nonce) { Command = (IsUnsubscribe ? Command.Unsubscribe : Command.Subscribe), Event = Event }; } } } namespace DiscordRPC.Registry { public interface IRegisterUriScheme { bool Register(SchemeInfo info); } public sealed class MacUriScheme : IRegisterUriScheme { private ILogger logger; public MacUriScheme(ILogger logger) { this.logger = logger; } public bool Register(SchemeInfo info) { string executablePath = info.ExecutablePath; if (string.IsNullOrEmpty(executablePath)) { logger.Error("Failed to register because the application could not be located."); return false; } logger.Trace("Registering Steam Command"); string text = executablePath; if (info.UsingSteamApp) { text = "steam://rungameid/" + info.SteamAppID; } else { logger.Warning("This library does not fully support MacOS URI Scheme Registration."); } string text2 = "~/Library/Application Support/discord/games"; if (!Directory.CreateDirectory(text2).Exists) { logger.Error("Failed to register because {0} does not exist", text2); return false; } string text3 = text2 + "/" + info.ApplicationID + ".json"; File.WriteAllText(text3, "{ \"command\": \"" + text + "\" }"); logger.Trace("Registered {0}, {1}", text3, text); return true; } } public sealed class UnixUriScheme : IRegisterUriScheme { private ILogger logger; public UnixUriScheme(ILogger logger) { this.logger = logger; } public bool Register(SchemeInfo info) { string environmentVariable = Environment.GetEnvironmentVariable("HOME"); if (string.IsNullOrEmpty(environmentVariable)) { logger.Error("Failed to register because the HOME variable was not set."); return false; } string executablePath = info.ExecutablePath; if (string.IsNullOrEmpty(executablePath)) { logger.Error("Failed to register because the application was not located."); return false; } string text = null; text = ((!info.UsingSteamApp) ? executablePath : ("xdg-open steam://rungameid/" + info.SteamAppID)); string text2 = $"[Desktop Entry]\nName=Game {info.ApplicationID}\nExec={text} %u\nType=Application\nNoDisplay=true\nCategories=Discord;Games;\nMimeType=x-scheme-handler/discord-{info.ApplicationID}"; string text3 = "/discord-" + info.ApplicationID + ".desktop"; string text4 = environmentVariable + "/.local/share/applications"; if (!Directory.CreateDirectory(text4).Exists) { logger.Error("Failed to register because {0} does not exist", text4); return false; } File.WriteAllText(text4 + text3, text2); if (!RegisterMime(info.ApplicationID)) { logger.Error("Failed to register because the Mime failed."); return false; } logger.Trace("Registered {0}, {1}, {2}", text4 + text3, text2, text); return true; } private bool RegisterMime(string appid) { string arguments = string.Format("default discord-{0}.desktop x-scheme-handler/discord-{0}", appid); Process process = Process.Start("xdg-mime", arguments); process.WaitForExit(); return process.ExitCode >= 0; } } public struct SchemeInfo { public string ApplicationID { get; set; } public string SteamAppID { get; set; } public string ExecutablePath { get; set; } public bool UsingSteamApp => !string.IsNullOrEmpty(SteamAppID); } public static class UriScheme { public static bool Register(SchemeInfo info, ILogger logger = null) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return new WindowsUriScheme(logger).Register(info); } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { return new UnixUriScheme(logger).Register(info); } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { return new MacUriScheme(logger).Register(info); } logger?.Error("Unknown Platform: {0}", RuntimeInformation.OSDescription); throw new PlatformNotSupportedException("Platform does not support registration."); } } public sealed class WindowsUriScheme : IRegisterUriScheme { private ILogger logger; public WindowsUriScheme(ILogger logger) { this.logger = logger; } public bool Register(SchemeInfo info) { string executablePath = info.ExecutablePath; if (executablePath == null) { logger.Error("Failed to register application because the location was null."); return false; } string scheme = "discord-" + info.ApplicationID; string friendlyName = "Run game " + info.ApplicationID + " protocol"; string defaultIcon = executablePath; string command = executablePath; if (info.UsingSteamApp) { string steamLocation = GetSteamLocation(); if (steamLocation != null) { command = $"\"{steamLocation}\" steam://rungameid/{info.SteamAppID}"; } } CreateUriScheme(scheme, friendlyName, defaultIcon, command); return true; } private void CreateUriScheme(string scheme, string friendlyName, string defaultIcon, string command) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { throw new PlatformNotSupportedException("Requires Windows to use the Registry"); } using (RegistryKey registryKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\" + scheme)) { registryKey.SetValue("", "URL:" + friendlyName); registryKey.SetValue("URL Protocol", ""); using (RegistryKey registryKey2 = registryKey.CreateSubKey("DefaultIcon")) { registryKey2.SetValue("", defaultIcon); } using RegistryKey registryKey3 = registryKey.CreateSubKey("shell\\open\\command"); registryKey3.SetValue("", command); } logger.Trace("Registered {0}, {1}, {2}", scheme, friendlyName, command); } public string GetSteamLocation() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { throw new PlatformNotSupportedException("Requires Windows to use the Registry"); } using RegistryKey registryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Valve\\Steam"); if (registryKey == null) { return null; } return registryKey.GetValue("SteamExe") as string; } } } namespace DiscordRPC.Message { public class CloseMessage : IMessage { public override MessageType Type => MessageType.Close; public string Reason { get; internal set; } public int Code { get; internal set; } internal CloseMessage() { } internal CloseMessage(string reason) { Reason = reason; } } public class ConnectionEstablishedMessage : IMessage { public override MessageType Type => MessageType.ConnectionEstablished; [Obsolete("The connected pipe is not neccessary information.")] public int ConnectedPipe { get; internal set; } } public class ConnectionFailedMessage : IMessage { public override MessageType Type => MessageType.ConnectionFailed; public int FailedPipe { get; internal set; } } public class ErrorMessage : IMessage { public override MessageType Type => MessageType.Error; [JsonProperty("code")] public ErrorCode Code { get; internal set; } [JsonProperty("message")] public string Message { get; internal set; } } public enum ErrorCode { Success = 0, PipeException = 1, ReadCorrupt = 2, NotImplemented = 10, UnknownError = 1000, InvalidPayload = 4000, InvalidCommand = 4002, InvalidEvent = 4004 } public abstract class IMessage { private DateTime _timecreated; public abstract MessageType Type { get; } public DateTime TimeCreated => _timecreated; public IMessage() { _timecreated = DateTime.Now; } } public class JoinMessage : IMessage { public override MessageType Type => MessageType.Join; [JsonProperty("secret")] public string Secret { get; internal set; } } public class JoinRequestMessage : IMessage { public override MessageType Type => MessageType.JoinRequest; [JsonProperty("user")] public User User { get; internal set; } } public enum MessageType { Ready, Close, Error, PresenceUpdate, Subscribe, Unsubscribe, Join, Spectate, JoinRequest, ConnectionEstablished, ConnectionFailed } public class PresenceMessage : IMessage { public override MessageType Type => MessageType.PresenceUpdate; public BaseRichPresence Presence { get; internal set; } public string Name { get; internal set; } public string ApplicationID { get; internal set; } internal PresenceMessage() : this(null) { } internal PresenceMessage(RichPresenceResponse rpr) { if (rpr == null) { Presence = null; Name = "No Rich Presence"; ApplicationID = ""; } else { Presence = rpr; Name = rpr.Name; ApplicationID = rpr.ClientID; } } } public class ReadyMessage : IMessage { public override MessageType Type => MessageType.Ready; [JsonProperty("config")] public Configuration Configuration { get; set; } [JsonProperty("user")] public User User { get; set; } [JsonProperty("v")] public int Version { get; set; } } public class SpectateMessage : JoinMessage { public override MessageType Type => MessageType.Spectate; } public class SubscribeMessage : IMessage { public override MessageType Type => MessageType.Subscribe; public EventType Event { get; internal set; } internal SubscribeMessage(ServerEvent evt) { switch (evt) { default: Event = EventType.Join; break; case ServerEvent.ActivityJoinRequest: Event = EventType.JoinRequest; break; case ServerEvent.ActivitySpectate: Event = EventType.Spectate; break; } } } public class UnsubscribeMessage : IMessage { public override MessageType Type => MessageType.Unsubscribe; public EventType Event { get; internal set; } internal UnsubscribeMessage(ServerEvent evt) { switch (evt) { default: Event = EventType.Join; break; case ServerEvent.ActivityJoinRequest: Event = EventType.JoinRequest; break; case ServerEvent.ActivitySpectate: Event = EventType.Spectate; break; } } } } namespace DiscordRPC.Logging { public class ConsoleLogger : ILogger { public LogLevel Level { get; set; } public bool Coloured { get; set; } public bool Colored { get { return Coloured; } set { Coloured = value; } } public ConsoleLogger() { Level = LogLevel.Info; Coloured = false; } public ConsoleLogger(LogLevel level) : this() { Level = level; } public ConsoleLogger(LogLevel level, bool coloured) { Level = level; Coloured = coloured; } public void Trace(string message, params object[] args) { if (Level <= LogLevel.Trace) { if (Coloured) { Console.ForegroundColor = ConsoleColor.Gray; } string text = "TRACE: " + message; if (args.Length != 0) { Console.WriteLine(text, args); } else { Console.WriteLine(text); } } } public void Info(string message, params object[] args) { if (Level <= LogLevel.Info) { if (Coloured) { Console.ForegroundColor = ConsoleColor.White; } string text = "INFO: " + message; if (args.Length != 0) { Console.WriteLine(text, args); } else { Console.WriteLine(text); } } } public void Warning(string message, params object[] args) { if (Level <= LogLevel.Warning) { if (Coloured) { Console.ForegroundColor = ConsoleColor.Yellow; } string text = "WARN: " + message; if (args.Length != 0) { Console.WriteLine(text, args); } else { Console.WriteLine(text); } } } public void Error(string message, params object[] args) { if (Level <= LogLevel.Error) { if (Coloured) { Console.ForegroundColor = ConsoleColor.Red; } string text = "ERR : " + message; if (args.Length != 0) { Console.WriteLine(text, args); } else { Console.WriteLine(text); } } } } public class FileLogger : ILogger { private object filelock; public LogLevel Level { get; set; } public string File { get; set; } public FileLogger(string path) : this(path, LogLevel.Info) { } public FileLogger(string path, LogLevel level) { Level = level; File = path; filelock = new object(); } public void Trace(string message, params object[] args) { if (Level > LogLevel.Trace) { return; } lock (filelock) { System.IO.File.AppendAllText(File, "\r\nTRCE: " + ((args.Length != 0) ? string.Format(message, args) : message)); } } public void Info(string message, params object[] args) { if (Level > LogLevel.Info) { return; } lock (filelock) { System.IO.File.AppendAllText(File, "\r\nINFO: " + ((args.Length != 0) ? string.Format(message, args) : message)); } } public void Warning(string message, params object[] args) { if (Level > LogLevel.Warning) { return; } lock (filelock) { System.IO.File.AppendAllText(File, "\r\nWARN: " + ((args.Length != 0) ? string.Format(message, args) : message)); } } public void Error(string message, params object[] args) { if (Level > LogLevel.Error) { return; } lock (filelock) { System.IO.File.AppendAllText(File, "\r\nERR : " + ((args.Length != 0) ? string.Format(message, args) : message)); } } } public interface ILogger { LogLevel Level { get; set; } void Trace(string message, params object[] args); void Info(string message, params object[] args); void Warning(string message, params object[] args); void Error(string message, params object[] args); } public enum LogLevel { Trace = 1, Info = 2, Warning = 3, Error = 4, None = 256 } public class NullLogger : ILogger { public LogLevel Level { get; set; } public void Trace(string message, params object[] args) { } public void Info(string message, params object[] args) { } public void Warning(string message, params object[] args) { } public void Error(string message, params object[] args) { } } } namespace DiscordRPC.IO { internal class Handshake { [JsonProperty("v")] public int Version { get; set; } [JsonProperty("client_id")] public string ClientID { get; set; } } public interface INamedPipeClient : IDisposable { ILogger Logger { get; set; } bool IsConnected { get; } [Obsolete("The connected pipe is not neccessary information.")] int ConnectedPipe { get; } bool Connect(int pipe); bool ReadFrame(out PipeFrame frame); bool WriteFrame(PipeFrame frame); void Close(); } public sealed class ManagedNamedPipeClient : INamedPipeClient, IDisposable { private NamedPipeClientStream _stream; private byte[] _buffer = new byte[PipeFrame.MAX_SIZE]; private Queue<PipeFrame> _framequeue = new Queue<PipeFrame>(); private object _framequeuelock = new object(); private volatile bool _isDisposed; private volatile bool _isClosed = true; private object l_stream = new object(); public ILogger Logger { get; set; } public bool IsConnected { get { if (_isClosed) { return false; } lock (l_stream) { return _stream != null && _stream.IsConnected; } } } public int ConnectedPipe { get; private set; } public ManagedNamedPipeClient() { _buffer = new byte[PipeFrame.MAX_SIZE]; Logger = new NullLogger(); _stream = null; } public bool Connect(int pipe) { Logger.Trace("ManagedNamedPipeClient.Connection({0})", pipe); if (_isDisposed) { throw new ObjectDisposedException("NamedPipe"); } if (pipe > 9) { throw new ArgumentOutOfRangeException("pipe", "Argument cannot be greater than 9"); } int startPipe = 0; if (pipe >= 0) { startPipe = pipe; } foreach (string pipe2 in PipeLocation.GetPipes(startPipe)) { if (AttemptConnection(pipe2)) { BeginReadStream(); return true; } } return false; } private bool AttemptConnection(string pipename) { if (_isDisposed) { throw new ObjectDisposedException("_stream"); } try { lock (l_stream) { Logger.Info("Attempting to connect to '{0}'", pipename); _stream = new NamedPipeClientStream(".", pipename, PipeDirection.InOut, PipeOptions.Asynchronous); _stream.Connect(0); Logger.Trace("Waiting for connection..."); do { Thread.Sleep(10); } while (!_stream.IsConnected); } Logger.Info("Connected to '{0}'", pipename); ConnectedPipe = int.Parse(pipename.Substring(pipename.LastIndexOf('-'))); _isClosed = false; } catch (Exception ex) { Logger.Error("Failed connection to {0}. {1}", pipename, ex.Message); Close(); } Logger.Trace("Done. Result: {0}", _isClosed); return !_isClosed; } private void BeginReadStream() { if (_isClosed) { return; } try { lock (l_stream) { if (_stream != null && _stream.IsConnected) { Logger.Trace("Begining Read of {0} bytes", _buffer.Length); _stream.BeginRead(_buffer, 0, _buffer.Length, EndReadStream, _stream.IsConnected); } } } catch (ObjectDisposedException) { Logger.Warning("Attempted to start reading from a disposed pipe"); } catch (InvalidOperationException) { Logger.Warning("Attempted to start reading from a closed pipe"); } catch (Exception ex3) { Logger.Error("An exception occured while starting to read a stream: {0}", ex3.Message); Logger.Error(ex3.StackTrace); } } private void EndReadStream(IAsyncResult callback) { Logger.Trace("Ending Read"); int num = 0; try { lock (l_stream) { if (_stream == null || !_stream.IsConnected) { return; } num = _stream.EndRead(callback); } } catch (IOException) { Logger.Warning("Attempted to end reading from a closed pipe"); return; } catch (NullReferenceException) { Logger.Warning("Attempted to read from a null pipe"); return; } catch (ObjectDisposedException) { Logger.Warning("Attemped to end reading from a disposed pipe"); return; } catch (Exception ex4) { Logger.Error("An exception occured while ending a read of a stream: {0}", ex4.Message); Logger.Error(ex4.StackTrace); return; } Logger.Trace("Read {0} bytes", num); if (num > 0) { using MemoryStream stream = new MemoryStream(_buffer, 0, num); try { PipeFrame item = default(PipeFrame); if (item.ReadStream(stream)) { Logger.Trace("Read a frame: {0}", item.Opcode); lock (_framequeuelock) { _framequeue.Enqueue(item); } } else { Logger.Error("Pipe failed to read from the data received by the stream."); Close(); } } catch (Exception ex5) { Logger.Error("A exception has occured while trying to parse the pipe data: {0}", ex5.Message); Close(); } } else { Logger.Error("Empty frame was read on {0}, aborting.", Environment.OSVersion); Close(); } if (!_isClosed && IsConnected) { Logger.Trace("Starting another read"); BeginReadStream(); } } public bool ReadFrame(out PipeFrame frame) { if (_isDisposed) { throw new ObjectDisposedException("_stream"); } lock (_framequeuelock) { if (_framequeue.Count == 0) { frame = default(PipeFrame); return false; } frame = _framequeue.Dequeue(); return true; } } public bool WriteFrame(PipeFrame frame) { if (_isDisposed) { throw new ObjectDisposedException("_stream"); } if (_isClosed || !IsConnected) { Logger.Error("Failed to write frame because the stream is closed"); return false; } try { frame.WriteStream(_stream); return true; } catch (IOException ex) { Logger.Error("Failed to write frame because of a IO Exception: {0}", ex.Message); } catch (ObjectDisposedException) { Logger.Warning("Failed to write frame as the stream was already disposed"); } catch (InvalidOperationException) { Logger.Warning("Failed to write frame because of a invalid operation"); } return false; } public void Close() { if (_isClosed) { Logger.Warning("Tried to close a already closed pipe."); return; } try { lock (l_stream) { if (_stream != null) { try { _stream.Flush(); _stream.Dispose(); } catch (Exception) { } _stream = null; _isClosed = true; } else { Logger.Warning("Stream was closed, but no stream was available to begin with!"); } } } catch (ObjectDisposedException) { Logger.Warning("Tried to dispose already disposed stream"); } finally { _isClosed = true; } } public void Dispose() { if (_isDisposed) { return; } if (!_isClosed) { Close(); } lock (l_stream) { if (_stream != null) { _stream.Dispose(); _stream = null; } } _isDisposed = true; } [Obsolete("Use PipePermutation.GetPipes instead", true)] public static string GetPipeName(int pipe) { return string.Empty; } [Obsolete("Use PipePermutation.GetPipes instead", true)] public static string GetPipeName(int pipe, string sandbox) { return string.Empty; } [Obsolete("Use PipePermutation.GetPipes instead", true)] public static string GetPipeSandbox() { return string.Empty; } [Obsolete("Use PipePermutation.GetPipes instead", true)] public static bool IsUnix() { return true; } } public enum Opcode : uint { Handshake, Frame, Close, Ping, Pong } public struct PipeFrame : IEquatable<PipeFrame> { public static readonly int MAX_SIZE = 16384; public Opcode Opcode { get; set; } public uint Length => (uint)Data.Length; public byte[] Data { get; set; } public string Message { get { return GetMessage(); } set { SetMessage(value); } } public Encoding MessageEncoding => Encoding.UTF8; public PipeFrame(Opcode opcode, object data) { Opcode = opcode; Data = null; SetObject(data); } private void SetMessage(string str) { Data = MessageEncoding.GetBytes(str); } private string GetMessage() { return MessageEncoding.GetString(Data); } public void SetObject(object obj) { string message = JsonConvert.SerializeObject(obj); SetMessage(message); } public void SetObject(Opcode opcode, object obj) { Opcode = opcode; SetObject(obj); } public T GetObject<T>() { return JsonConvert.DeserializeObject<T>(GetMessage()); } public bool ReadStream(Stream stream) { if (!TryReadUInt32(stream, out var value)) { return false; } if (!TryReadUInt32(stream, out var value2)) { return false; } uint num = value2; using MemoryStream memoryStream = new MemoryStream(); uint num2 = (uint)Min(2048, value2); byte[] array = new byte[num2]; int count; while ((count = stream.Read(array, 0, Min(array.Length, num))) > 0) { num -= num2; memoryStream.Write(array, 0, count); } byte[] array2 = memoryStream.ToArray(); if (array2.LongLength != value2) { return false; } Opcode = (Opcode)value; Data = array2; return true; } private int Min(int a, uint b) { if (b >= a) { return a; } return (int)b; } private bool TryReadUInt32(Stream stream, out uint value) { byte[] array = new byte[4]; if (stream.Read(array, 0, array.Length) != 4) { value = 0u; return false; } value = BitConverter.ToUInt32(array, 0); return true; } public void WriteStream(Stream stream) { byte[] bytes = BitConverter.GetBytes((uint)Opcode); byte[] bytes2 = BitConverter.GetBytes(Length); byte[] array = new byte[bytes.Length + bytes2.Length + Data.Length]; bytes.CopyTo(array, 0); bytes2.CopyTo(array, bytes.Length); Data.CopyTo(array, bytes.Length + bytes2.Length); stream.Write(array, 0, array.Length); } public bool Equals(PipeFrame other) { if (Opcode == other.Opcode && Length == other.Length) { return Data == other.Data; } return false; } } public static class PipeLocation { [CompilerGenerated] private sealed class <GetUnixPipes>d__5 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; private int index; public int <>3__index; private IEnumerator<string> <>7__wrap1; private string <tempDir>5__3; private string[] <>7__wrap3; private int <>7__wrap4; string IEnumerator<string>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetUnixPipes>d__5(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 1u) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; <tempDir>5__3 = null; <>7__wrap3 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = TemporaryDirectories().GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; <>7__wrap3 = LinuxPackageManagers; <>7__wrap4 = 0; goto IL_0105; case 2: { <>1__state = -3; <>7__wrap4++; goto IL_0105; } IL_0105: if (<>7__wrap4 < <>7__wrap3.Length) { string path = <>7__wrap3[<>7__wrap4]; <>2__current = Path.Combine(<tempDir>5__3, path, string.Format("{0}{1}", "discord-ipc-", index)); <>1__state = 2; return true; } <>7__wrap3 = null; <tempDir>5__3 = null; break; } if (<>7__wrap1.MoveNext()) { <tempDir>5__3 = <>7__wrap1.Current; <>2__current = Path.Combine(<tempDir>5__3, string.Format("{0}{1}", "discord-ipc-", index)); <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<string> IEnumerable<string>.GetEnumerator() { <GetUnixPipes>d__5 <GetUnixPipes>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <GetUnixPipes>d__ = this; } else { <GetUnixPipes>d__ = new <GetUnixPipes>d__5(0); } <GetUnixPipes>d__.index = <>3__index; return <GetUnixPipes>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<string>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <GetWindowsPipes>d__4 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; private int index; public int <>3__index; string IEnumerator<string>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetWindowsPipes>d__4(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = string.Format("{0}{1}", "discord-ipc-", index); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<string> IEnumerable<string>.GetEnumerator() { <GetWindowsPipes>d__4 <GetWindowsPipes>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <GetWindowsPipes>d__ = this; } else { <GetWindowsPipes>d__ = new <GetWindowsPipes>d__4(0); } <GetWindowsPipes>d__.index = <>3__index; return <GetWindowsPipes>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<string>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <TemporaryDirectories>d__6 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; string IEnumerator<string>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <TemporaryDirectories>d__6(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { string environmentVariable; switch (<>1__state) { default: return false; case 0: <>1__state = -1; environmentVariable = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR"); if (environmentVariable != null) { <>2__current = environmentVariable; <>1__state = 1; return true; } goto IL_0053; case 1: <>1__state = -1; goto IL_0053; case 2: <>1__state = -1; goto IL_0078; case 3: <>1__state = -1; goto IL_009d; case 4: <>1__state = -1; goto IL_00c2; case 5: { <>1__state = -1; return false; } IL_009d: environmentVariable = Environment.GetEnvironmentVariable("TEMP"); if (environmentVariable != null) { <>2__current = environmentVariable; <>1__state = 4; return true; } goto IL_00c2; IL_0053: environmentVariable = Environment.GetEnvironmentVariable("TMPDIR"); if (environmentVariable != null) { <>2__current = environmentVariable; <>1__state = 2; return true; } goto IL_0078; IL_0078: environmentVariable = Environment.GetEnvironmentVariable("TMP"); if (environmentVariable != null) { <>2__current = environmentVariable; <>1__state = 3; return true; } goto IL_009d; IL_00c2: <>2__current = "/temp"; <>1__state = 5; return true; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<string> IEnumerable<string>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <TemporaryDirectories>d__6(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<string>)this).GetEnumerator(); } } private const string DiscordPipePrefix = "discord-ipc-"; private const int MaximumPipeVariations = 10; private static readonly string[] LinuxPackageManagers = new string[2] { "app/com.discordapp.Discord/", "snap.discord/" }; public static IEnumerable<string> GetPipes(int startPipe = 0) { IsOSUnix(); if (IsOSUnix()) { return Enumerable.Range(startPipe, 10).SelectMany(GetUnixPipes); } return Enumerable.Range(startPipe, 10).SelectMany(GetWindowsPipes); } [IteratorStateMachine(typeof(<GetWindowsPipes>d__4))] private static IEnumerable<string> GetWindowsPipes(int index) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetWindowsPipes>d__4(-2) { <>3__index = index }; } [IteratorStateMachine(typeof(<GetUnixPipes>d__5))] private static IEnumerable<string> GetUnixPipes(int index) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetUnixPipes>d__5(-2) { <>3__index = index }; } [IteratorStateMachine(typeof(<TemporaryDirectories>d__6))] private static IEnumerable<string> TemporaryDirectories() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <TemporaryDirectories>d__6(-2); } private static bool IsOSUnix() { if (Environment.OSVersion.Platform == PlatformID.Unix) { return true; } return false; } } } namespace DiscordRPC.Helper { internal class BackoffDelay { private int _current; private int _fails; public int Maximum { get; private set; } public int Minimum { get; private set; } public int Current => _current; public int Fails => _fails; public Random Random { get; set; } private BackoffDelay() { } public BackoffDelay(int min, int max) : this(min, max, new Random()) { } public BackoffDelay(int min, int max, Random random) { Minimum = min; Maximum = max; _current = min; _fails = 0; Random = random; } public void Reset() { _fails = 0; _current = Minimum; } public int NextDelay() { _fails++; double num = (float)(Maximum - Minimum) / 100f; _current = (int)Math.Floor(num * (double)_fails) + Minimum; return Math.Min(Math.Max(_current, Minimum), Maximum); } } public static class StringTools { public static string GetNullOrString(this string str) { if (str.Length != 0 && !string.IsNullOrEmpty(str.Trim())) { return str; } return null; } public static bool WithinLength(this string str, int bytes) { return str.WithinLength(bytes, Encoding.UTF8); } public static bool WithinLength(this string str, int bytes, Encoding encoding) { return encoding.GetByteCount(str) <= bytes; } public static string ToCamelCase(this string str) { return (from s in str?.ToLowerInvariant().Split(new string[2] { "_", " " }, StringSplitOptions.RemoveEmptyEntries) select char.ToUpper(s[0]) + s.Substring(1, s.Length - 1)).Aggregate(string.Empty, (string s1, string s2) => s1 + s2); } public static string ToSnakeCase(this string str) { if (str == null) { return null; } return string.Concat(str.Select((char x, int i) => (i <= 0 || !char.IsUpper(x)) ? x.ToString() : ("_" + x)).ToArray()).ToUpperInvariant(); } } } namespace DiscordRPC.Exceptions { public class BadPresenceException : Exception { internal BadPresenceException(string message) : base(message) { } } public class InvalidConfigurationException : Exception { internal InvalidConfigurationException(string message) : base(message) { } } [Obsolete("Not actually used anywhere")] public class InvalidPipeException : Exception { internal InvalidPipeException(string message) : base(message) { } } public class StringOutOfRangeException : Exception { public int MaximumLength { get; private set; } public int MinimumLength { get; private set; } internal StringOutOfRangeException(string message, int min, int max) : base(message) { MinimumLength = min; MaximumLength = max; } internal StringOutOfRangeException(int minumum, int max) : this($"Length of string is out of range. Expected a value between {minumum} and {max}", minumum, max) { } internal StringOutOfRangeException(int max) : this($"Length of string is out of range. Expected a value with a maximum length of {max}", 0, max) { } } public class UninitializedException : Exception { internal UninitializedException(string message) : base(message) { } internal UninitializedException() : this("Cannot perform action because the client has not been initialized yet or has been deinitialized.") { } } } namespace DiscordRPC.Events { public delegate void OnReadyEvent(object sender, ReadyMessage args); public delegate void OnCloseEvent(object sender, CloseMessage args); public delegate void OnErrorEvent(object sender, ErrorMessage args); public delegate void OnPresenceUpdateEvent(object sender, PresenceMessage args); public delegate void OnSubscribeEvent(object sender, SubscribeMessage args); public delegate void OnUnsubscribeEvent(object sender, UnsubscribeMessage args); public delegate void OnJoinEvent(object sender, JoinMessage args); public delegate void OnSpectateEvent(object sender, SpectateMessage args); public delegate void OnJoinRequestedEvent(object sender, JoinRequestMessage args); public delegate void OnConnectionEstablishedEvent(object sender, ConnectionEstablishedMessage args); public delegate void OnConnectionFailedEvent(object sender, ConnectionFailedMessage args); public delegate void OnRpcMessageEvent(object sender, IMessage msg); } namespace DiscordRPC.Converters { internal class EnumSnakeCaseConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType.IsEnum; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.Value == null) { return null; } object obj = null; if (TryParseEnum(objectType, (string)reader.Value, out obj)) { return obj; } return existingValue; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { Type type = value.GetType(); string text = Enum.GetName(type, value); MemberInfo[] members = type.GetMembers(BindingFlags.Static | BindingFlags.Public); foreach (MemberInfo memberInfo in members) { if (memberInfo.Name.Equals(text)) { object[] customAttributes = memberInfo.GetCustomAttributes(typeof(EnumValueAttribute), inherit: true); if (customAttributes.Length != 0) { text = ((EnumValueAttribute)customAttributes[0]).Value; } } } writer.WriteValue(text); } public bool TryParseEnum(Type enumType, string str, out object obj
UserLibs/Scriban.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Numerics; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Scriban.Functions; using Scriban.Helpers; using Scriban.Parsing; using Scriban.Runtime; using Scriban.Runtime.Accessors; using Scriban.Syntax; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("Scriban.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Alexandre Mutel")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Alexandre Mutel")] [assembly: AssemblyDescription("Scriban is a fast, powerful, safe and lightweight scripting language and engine for .NET, which was primarily developed for text templating with a compatibility mode for parsing liquid templates.")] [assembly: AssemblyFileVersion("0.0.0.0")] [assembly: AssemblyInformationalVersion("0.0.0-alpha.0.856+b79ba35830fb5260f45b6c06620726ddddc7863a")] [assembly: AssemblyProduct("Scriban")] [assembly: AssemblyTitle("Scriban")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/HAHOOS/scriban.git")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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 System { [ExcludeFromCodeCoverage] internal readonly struct Index : IEquatable<Index> { private static class ThrowHelper { [DoesNotReturn] public static void ThrowValueArgumentOutOfRange_NeedNonNegNumException() { throw new ArgumentOutOfRangeException("value", "Non-negative number required."); } } private readonly int _value; public static Index Start => new Index(0); public static Index End => new Index(-1); public int Value { get { if (_value < 0) { return ~_value; } return _value; } } public bool IsFromEnd => _value < 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Index(int value, bool fromEnd = false) { if (value < 0) { ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } if (fromEnd) { _value = ~value; } else { _value = value; } } private Index(int value) { _value = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Index FromStart(int value) { if (value < 0) { ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } return new Index(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Index FromEnd(int value) { if (value < 0) { ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); } return new Index(~value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetOffset(int length) { int num = _value; if (IsFromEnd) { num += length + 1; } return num; } public override bool Equals([NotNullWhen(true)] object? value) { if (value is Index) { return _value == ((Index)value)._value; } return false; } public bool Equals(Index other) { return _value == other._value; } public override int GetHashCode() { return _value; } public static implicit operator Index(int value) { return FromStart(value); } public override string ToString() { if (IsFromEnd) { return ToStringFromEnd(); } return ((uint)Value).ToString(); } private string ToStringFromEnd() { return "^" + Value; } } [ExcludeFromCodeCoverage] internal readonly struct Range : IEquatable<Range> { private static class HashHelpers { public static int Combine(int h1, int h2) { return (((h1 << 5) | (h1 >>> 27)) + h1) ^ h2; } } private static class ThrowHelper { [DoesNotReturn] public static void ThrowArgumentOutOfRangeException() { throw new ArgumentOutOfRangeException("length"); } } public Index Start { get; } public Index End { get; } public static Range All => Index.Start..Index.End; public Range(Index start, Index end) { Start = start; End = end; } public override bool Equals([NotNullWhen(true)] object? value) { if (value is Range { Start: var start } range && start.Equals(Start)) { return range.End.Equals(End); } return false; } public bool Equals(Range other) { if (other.Start.Equals(Start)) { return other.End.Equals(End); } return false; } public override int GetHashCode() { return HashHelpers.Combine(Start.GetHashCode(), End.GetHashCode()); } public override string ToString() { return Start.ToString() + ".." + End; } public static Range StartAt(Index start) { return start..Index.End; } public static Range EndAt(Index end) { return Index.Start..end; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public (int Offset, int Length) GetOffsetAndLength(int length) { Index start = Start; int num = ((!start.IsFromEnd) ? start.Value : (length - start.Value)); Index end = End; int num2 = ((!end.IsFromEnd) ? end.Value : (length - end.Value)); if ((uint)num2 > (uint)length || (uint)num > (uint)num2) { ThrowHelper.ThrowArgumentOutOfRangeException(); } return (Offset: num, Length: num2 - num); } } } namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class SuppressGCTransitionAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class UnmanagedCallersOnlyAttribute : Attribute { public Type[]? CallConvs; public string? EntryPoint; } [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class WasmImportLinkageAttribute : Attribute { } } namespace System.Runtime.CompilerServices2 { [AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class InlineArrayAttribute : Attribute { public int Length { get; } public InlineArrayAttribute(int length) { Length = length; } } } namespace System.Runtime.Versioning { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class RequiresPreviewFeaturesAttribute : Attribute { public string? Message { get; } public string? Url { get; set; } public RequiresPreviewFeaturesAttribute() { } public RequiresPreviewFeaturesAttribute(string? message) { Message = message; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class ObsoletedOSPlatformAttribute : Attribute { public string? Message { get; } public string? Url { get; set; } public ObsoletedOSPlatformAttribute(string platformName) { } public ObsoletedOSPlatformAttribute(string platformName, string? message) { Message = message; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class SupportedOSPlatformAttribute : Attribute { public SupportedOSPlatformAttribute(string platformName) { } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class SupportedOSPlatformGuardAttribute : Attribute { public SupportedOSPlatformGuardAttribute(string platformName) { } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class TargetPlatformAttribute : Attribute { public TargetPlatformAttribute(string platformName) { } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class UnsupportedOSPlatformAttribute : Attribute { public string? Message { get; } public UnsupportedOSPlatformAttribute(string platformName) { } public UnsupportedOSPlatformAttribute(string platformName, string? message) { Message = message; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class UnsupportedOSPlatformGuardAttribute : Attribute { public UnsupportedOSPlatformGuardAttribute(string platformName) { } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CallerArgumentExpressionAttribute : Attribute { public string ParameterName { get; } public CallerArgumentExpressionAttribute(string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CollectionBuilderAttribute : Attribute { public Type BuilderType { get; } public string MethodName { get; } public CollectionBuilderAttribute(Type builderType, string methodName) { BuilderType = builderType; MethodName = methodName; } } [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CompilerFeatureRequiredAttribute : Attribute { public const string RefStructs = "RefStructs"; public const string RequiredMembers = "RequiredMembers"; public string FeatureName { get; } public bool IsOptional { get; set; } public CompilerFeatureRequiredAttribute(string featureName) { FeatureName = featureName; } } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute { public string[] Arguments { get; } public InterpolatedStringHandlerArgumentAttribute(string argument) { Arguments = new string[1] { argument }; } public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) { Arguments = arguments; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class InterpolatedStringHandlerAttribute : Attribute { } [EditorBrowsable(EditorBrowsableState.Never)] [ExcludeFromCodeCoverage] internal static class IsExternalInit { } [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ModuleInitializerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class OverloadResolutionPriorityAttribute : Attribute { public int Priority { get; } public OverloadResolutionPriorityAttribute(int priority) { Priority = priority; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] [ExcludeFromCodeCoverage] internal sealed class ParamCollectionAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class RequiredMemberAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [EditorBrowsable(EditorBrowsableState.Never)] [ExcludeFromCodeCoverage] internal sealed class RequiresLocationAttribute : Attribute { } [AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class SkipLocalsInitAttribute : Attribute { } [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class DisableRuntimeMarshallingAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class UnsafeAccessorAttribute : Attribute { public UnsafeAccessorKind Kind { get; } public string? Name { get; set; } public UnsafeAccessorAttribute(UnsafeAccessorKind kind) { Kind = kind; } } internal enum UnsafeAccessorKind { Constructor, Method, StaticMethod, Field, StaticField } } namespace System.Diagnostics { [AttributeUsage(AttributeTargets.Method)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class DebuggerDisableUserUnhandledExceptionsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class StackTraceHiddenAttribute : Attribute { } } namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class AllowNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ConstantExpectedAttribute : Attribute { public object? Min { get; set; } public object? Max { get; set; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class DisallowNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class DoesNotReturnAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class DoesNotReturnIfAttribute : Attribute { public bool ParameterValue { get; } public DoesNotReturnIfAttribute(bool parameterValue) { ParameterValue = parameterValue; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ExperimentalAttribute : Attribute { public string DiagnosticId { get; } public string? UrlFormat { get; set; } public ExperimentalAttribute(string diagnosticId) { DiagnosticId = diagnosticId; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class MaybeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class MaybeNullWhenAttribute : Attribute { public bool ReturnValue { get; } public MaybeNullWhenAttribute(bool returnValue) { ReturnValue = returnValue; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] internal sealed class MemberNotNullAttribute : Attribute { public string[] Members { get; } public MemberNotNullAttribute(string member) { Members = new string[1] { member }; } public MemberNotNullAttribute(params string[] members) { Members = members; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] internal sealed class MemberNotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public string[] Members { get; } public MemberNotNullWhenAttribute(bool returnValue, string member) { ReturnValue = returnValue; Members = new string[1] { member }; } public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { ReturnValue = returnValue; Members = members; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class NotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class NotNullIfNotNullAttribute : Attribute { public string ParameterName { get; } public NotNullIfNotNullAttribute(string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class NotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public NotNullWhenAttribute(bool returnValue) { ReturnValue = returnValue; } } [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class SetsRequiredMembersAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class StringSyntaxAttribute : Attribute { public const string CompositeFormat = "CompositeFormat"; public const string DateOnlyFormat = "DateOnlyFormat"; public const string DateTimeFormat = "DateTimeFormat"; public const string EnumFormat = "EnumFormat"; public const string GuidFormat = "GuidFormat"; public const string Json = "Json"; public const string NumericFormat = "NumericFormat"; public const string Regex = "Regex"; public const string TimeOnlyFormat = "TimeOnlyFormat"; public const string TimeSpanFormat = "TimeSpanFormat"; public const string Uri = "Uri"; public const string Xml = "Xml"; public string Syntax { get; } public object?[] Arguments { get; } public StringSyntaxAttribute(string syntax) { Syntax = syntax; Arguments = new object[0]; } public StringSyntaxAttribute(string syntax, params object?[] arguments) { Syntax = syntax; Arguments = arguments; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class UnscopedRefAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class DynamicallyAccessedMembersAttribute : Attribute { public DynamicallyAccessedMemberTypes MemberTypes { get; } public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) { MemberTypes = memberTypes; } } [Flags] internal enum DynamicallyAccessedMemberTypes { None = 0, PublicParameterlessConstructor = 1, PublicConstructors = 3, NonPublicConstructors = 4, PublicMethods = 8, NonPublicMethods = 0x10, PublicFields = 0x20, NonPublicFields = 0x40, PublicNestedTypes = 0x80, NonPublicNestedTypes = 0x100, PublicProperties = 0x200, NonPublicProperties = 0x400, PublicEvents = 0x800, NonPublicEvents = 0x1000, Interfaces = 0x2000, All = -1 } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Field, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class DynamicDependencyAttribute : Attribute { public string? MemberSignature { get; } public DynamicallyAccessedMemberTypes MemberTypes { get; } public Type? Type { get; } public string? TypeName { get; } public string? AssemblyName { get; } public string? Condition { get; set; } public DynamicDependencyAttribute(string memberSignature) { MemberSignature = memberSignature; } public DynamicDependencyAttribute(string memberSignature, Type type) { MemberSignature = memberSignature; Type = type; } public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName) { MemberSignature = memberSignature; TypeName = typeName; AssemblyName = assemblyName; } public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type) { MemberTypes = memberTypes; Type = type; } public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName) { MemberTypes = memberTypes; TypeName = typeName; AssemblyName = assemblyName; } } [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class FeatureGuardAttribute : Attribute { public Type FeatureType { get; } public FeatureGuardAttribute(Type featureType) { FeatureType = featureType; } } [AttributeUsage(AttributeTargets.Property, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class FeatureSwitchDefinitionAttribute : Attribute { public string SwitchName { get; } public FeatureSwitchDefinitionAttribute(string switchName) { SwitchName = switchName; } } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class RequiresAssemblyFilesAttribute : Attribute { public string? Message { get; } public string? Url { get; set; } public RequiresAssemblyFilesAttribute() { } public RequiresAssemblyFilesAttribute(string message) { Message = message; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class RequiresDynamicCodeAttribute : Attribute { public string Message { get; } public string? Url { get; set; } public RequiresDynamicCodeAttribute(string message) { Message = message; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class RequiresUnreferencedCodeAttribute : Attribute { public string Message { get; } public string? Url { get; set; } public RequiresUnreferencedCodeAttribute(string message) { Message = message; } } [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] [Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] internal sealed class UnconditionalSuppressMessageAttribute : Attribute { public string Category { get; } public string CheckId { get; } public string? Scope { get; set; } public string? Target { get; set; } public string? MessageId { get; set; } public string? Justification { get; set; } public UnconditionalSuppressMessageAttribute(string category, string checkId) { Category = category; CheckId = checkId; } } } namespace Scriban { [DebuggerDisplay("Count: {Count}")] public class LogMessageBag : IReadOnlyList<LogMessage>, IEnumerable<LogMessage>, IEnumerable, IReadOnlyCollection<LogMessage> { private readonly List<LogMessage> _messages; public int Count => _messages.Count; public LogMessage this[int index] => _messages[index]; public bool HasErrors { get; private set; } public LogMessageBag() { _messages = new List<LogMessage>(); } public void Add(LogMessage message) { if (message == null) { throw new ArgumentNullException("message"); } if (message.Type == ParserMessageType.Error) { HasErrors = true; } _messages.Add(message); } public void AddRange(IEnumerable<LogMessage> messages) { if (messages == null) { throw new ArgumentNullException("messages"); } foreach (LogMessage message in messages) { Add(message); } } public IEnumerator<LogMessage> GetEnumerator() { return _messages.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_messages).GetEnumerator(); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); foreach (LogMessage message in _messages) { stringBuilder.AppendLine(message.ToString()); } return stringBuilder.ToString(); } } public class Template { public string? SourceFilePath { get; } public ScriptPage? Page { get; private set; } public bool HasErrors { get; private set; } public LogMessageBag Messages { get; } public ParserOptions ParserOptions { get; } public LexerOptions LexerOptions { get; } private async ValueTask<object?> EvaluateAndRenderAsync(TemplateContext context, bool render) { if (context == null) { throw new ArgumentNullException("context"); } CheckErrors(); if (SourceFilePath != null) { context.PushSourceFile(SourceFilePath); } try { context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific; if (Page == null) { return null; } object result = await context.EvaluateAsync(Page).ConfigureAwait(continueOnCapturedContext: false); if (render && context.EnableOutput && result != null) { await context.WriteAsync(Page.Span, result).ConfigureAwait(continueOnCapturedContext: false); } return result; } finally { if (SourceFilePath != null) { context.PopSourceFile(); } } } public async ValueTask<object?> EvaluateAsync(TemplateContext context) { bool previousOutput = context.EnableOutput; try { context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific; context.EnableOutput = false; return await EvaluateAndRenderAsync(context, render: false).ConfigureAwait(continueOnCapturedContext: false); } finally { context.EnableOutput = previousOutput; } } public static async ValueTask<object?> EvaluateAsync(string expression, TemplateContext context) { if (expression == null) { throw new ArgumentNullException("expression"); } LexerOptions lexerOptions = new LexerOptions { Mode = ScriptMode.ScriptOnly }; return await Parse(expression, null, null, lexerOptions).EvaluateAsync(context).ConfigureAwait(continueOnCapturedContext: false); } public async ValueTask<object?> EvaluateAsync(object? model = null, MemberRenamerDelegate? memberRenamer = null, MemberFilterDelegate? memberFilter = null) { ScriptObject scriptObject = new ScriptObject(); if (model != null) { scriptObject.Import(model, memberFilter, memberRenamer); } TemplateContext context = ((LexerOptions.Lang == ScriptLang.Liquid) ? new LiquidTemplateContext() : new TemplateContext()); context.EnableOutput = false; context.MemberRenamer = memberRenamer ?? StandardMemberRenamer.Default; context.MemberFilter = memberFilter; context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific; context.PushGlobal(scriptObject); object result = await EvaluateAsync(context).ConfigureAwait(continueOnCapturedContext: false); context.PopGlobal(); return result; } public static async ValueTask<object?> EvaluateAsync(string expression, object model, MemberRenamerDelegate? memberRenamer = null, MemberFilterDelegate? memberFilter = null) { if (expression == null) { throw new ArgumentNullException("expression"); } LexerOptions lexerOptions = new LexerOptions { Mode = ScriptMode.ScriptOnly }; return await Parse(expression, null, null, lexerOptions).EvaluateAsync(model, memberRenamer, memberFilter).ConfigureAwait(continueOnCapturedContext: false); } public async ValueTask<string> RenderAsync(TemplateContext context) { await EvaluateAndRenderAsync(context, render: true).ConfigureAwait(continueOnCapturedContext: false); string text = context.Output.ToString(); if (context.Output is StringBuilderOutput stringBuilderOutput) { stringBuilderOutput.Builder.Length = 0; } if (text == null) { text = string.Empty; } return text; } public async ValueTask<string> RenderAsync(object? model = null, MemberRenamerDelegate? memberRenamer = null, MemberFilterDelegate? memberFilter = null) { ScriptObject scriptObject = new ScriptObject(); if (model != null) { scriptObject.Import(model, memberFilter, memberRenamer); } TemplateContext templateContext = ((LexerOptions.Lang == ScriptLang.Liquid) ? new LiquidTemplateContext() : new TemplateContext()); templateContext.MemberRenamer = memberRenamer ?? StandardMemberRenamer.Default; templateContext.MemberFilter = memberFilter; templateContext.PushGlobal(scriptObject); return await RenderAsync(templateContext).ConfigureAwait(continueOnCapturedContext: false); } private Template(ParserOptions? parserOptions, LexerOptions? lexerOptions, string? sourceFilePath) { ParserOptions = parserOptions ?? Scriban.Parsing.ParserOptions.Default; LexerOptions = lexerOptions ?? Scriban.Parsing.LexerOptions.Default; Messages = new LogMessageBag(); SourceFilePath = sourceFilePath; } public static Template Parse(string text, string? sourceFilePath = null, ParserOptions? parserOptions = null, LexerOptions? lexerOptions = null) { Template template = new Template(parserOptions, lexerOptions, sourceFilePath); template.ParseInternal(text, sourceFilePath); return template; } public static Template ParseLiquid(string text, string? sourceFilePath = null, ParserOptions? parserOptions = null, LexerOptions? lexerOptions = null) { LexerOptions lexerOptions2 = lexerOptions ?? Scriban.Parsing.LexerOptions.Default; lexerOptions2 = lexerOptions2 with { Lang = ScriptLang.Liquid }; return Parse(text, sourceFilePath, parserOptions, lexerOptions2); } public static object? Evaluate(string expression, TemplateContext context) { if (expression == null) { throw new ArgumentNullException("expression"); } LexerOptions lexerOptions = new LexerOptions { Mode = ScriptMode.ScriptOnly }; return Parse(expression, null, null, lexerOptions).Evaluate(context); } public static object? Evaluate(string expression, object model, MemberRenamerDelegate? memberRenamer = null, MemberFilterDelegate? memberFilter = null) { if (expression == null) { throw new ArgumentNullException("expression"); } LexerOptions lexerOptions = new LexerOptions { Mode = ScriptMode.ScriptOnly }; return Parse(expression, null, null, lexerOptions).Evaluate(model, memberRenamer, memberFilter); } public object? Evaluate(TemplateContext context) { bool enableOutput = context.EnableOutput; try { context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific; context.EnableOutput = false; return EvaluateAndRender(context, render: false); } finally { context.EnableOutput = enableOutput; } } public object? Evaluate(object? model = null, MemberRenamerDelegate? memberRenamer = null, MemberFilterDelegate? memberFilter = null) { ScriptObject scriptObject = new ScriptObject(); if (model != null) { scriptObject.Import(model, memberFilter, memberRenamer); } TemplateContext templateContext = ((LexerOptions.Lang == ScriptLang.Liquid) ? new LiquidTemplateContext() : new TemplateContext()); templateContext.EnableOutput = false; templateContext.MemberRenamer = memberRenamer ?? StandardMemberRenamer.Default; templateContext.MemberFilter = memberFilter; templateContext.UseScientific = LexerOptions.Lang == ScriptLang.Scientific; templateContext.PushGlobal(scriptObject); object? result = Evaluate(templateContext); templateContext.PopGlobal(); return result; } public string Render(TemplateContext context) { EvaluateAndRender(context, render: true); string text = context.Output.ToString(); if (context.Output is StringBuilderOutput stringBuilderOutput) { stringBuilderOutput.Builder.Length = 0; } if (text == null) { text = string.Empty; } return text; } public string Render(object? model = null, MemberRenamerDelegate? memberRenamer = null, MemberFilterDelegate? memberFilter = null) { ScriptObject scriptObject = new ScriptObject(); if (model != null) { scriptObject.Import(model, memberFilter, memberRenamer); } TemplateContext templateContext = ((LexerOptions.Lang == ScriptLang.Liquid) ? new LiquidTemplateContext() : new TemplateContext()); templateContext.MemberRenamer = memberRenamer ?? StandardMemberRenamer.Default; templateContext.MemberFilter = memberFilter; templateContext.PushGlobal(scriptObject); return Render(templateContext); } public string ToText(ScriptPrinterOptions options = default(ScriptPrinterOptions)) { CheckErrors(); TextWriterOutput textWriterOutput = new TextWriterOutput(); ScriptPrinter scriptPrinter = new ScriptPrinter(textWriterOutput, options); if (Page != null) { scriptPrinter.Write(Page); } return textWriterOutput.ToString(); } private object? EvaluateAndRender(TemplateContext context, bool render) { if (context == null) { throw new ArgumentNullException("context"); } CheckErrors(); if (SourceFilePath != null) { context.PushSourceFile(SourceFilePath); } try { context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific; if (Page == null) { return null; } object obj = context.Evaluate(Page); if (render && context.EnableOutput && obj != null) { context.Write(Page.Span, obj); } return obj; } finally { if (SourceFilePath != null) { context.PopSourceFile(); } } } private void CheckErrors() { if (HasErrors) { throw new InvalidOperationException("This template has errors. Check the <Template.HasError> and <Template.Messages> before evaluating a template. Messages:\n" + string.Join("\n", Messages)); } } private void ParseInternal(string text, string? sourceFilePath) { if (string.IsNullOrEmpty(text)) { HasErrors = false; Page = new ScriptPage { Span = new SourceSpan(sourceFilePath ?? string.Empty, default(TextPosition), TextPosition.Eof) }; } else { Parser parser = new Parser(new Lexer(text, sourceFilePath, LexerOptions), ParserOptions); Page = parser.Run(); HasErrors = parser.HasErrors; Messages.AddRange(parser.Messages); } } } public class TemplateContext : IFormatProvider { public delegate bool TryGetMemberDelegate(TemplateContext context, SourceSpan span, object target, string member, out object? value); public delegate bool TryGetVariableDelegate(TemplateContext context, SourceSpan span, ScriptVariable variable, out object? value); public delegate string RenderRuntimeExceptionDelegate(ScriptRuntimeException exception); internal enum LoopType { Default, Queryable } private class VariableContext { public IScriptObject? LocalObject; public FastStack<ScriptObject> Loops; public bool HideParentFunctionScopes; public VariableContext(IScriptObject? localObject) { LocalObject = localObject; Loops = new FastStack<ScriptObject>(4); } } private enum VariableScope { Local, Loop } private FastStack<ScriptObject> _availableStores; internal FastStack<ScriptBlockStatement> BlockDelegates; private FastStack<VariableContext> _globalContexts; private FastStack<VariableContext> _functionContexts; private FastStack<CultureInfo> _cultures; private readonly Dictionary<Type, IListAccessor> _listAccessors; private FastStack<ScriptLoopStatementBase> _loops; private readonly Dictionary<Type, IObjectAccessor> _memberAccessors; private FastStack<IScriptOutput> _outputs; private FastStack<VariableContext> _localContexts; private VariableContext? _currentLocalContext; private IScriptOutput _output; private FastStack<string> _sourceFiles; private FastStack<object?> _caseValues; private int _callDepth; private bool _isFunctionCallDisabled; private int _loopStep; private int _getOrSetValueLevel; private FastStack<VariableContext> _availableGlobalContexts; private FastStack<VariableContext> _availableFunctionContexts; private FastStack<VariableContext> _availableLocalContexts; private FastStack<ScriptPipeArguments> _availablePipeArguments; private FastStack<ScriptPipeArguments> _pipeArguments; private FastStack<List<ScriptExpression>> _availableScriptExpressionLists; private object?[]?[] _availableReflectionArguments; private ScriptPipeArguments? _currentPipeArguments; private bool _previousTextWasNewLine; private readonly IEqualityComparer<string>? _keyComparer; public static RenderRuntimeExceptionDelegate RenderRuntimeExceptionDefault = (ScriptRuntimeException ex) => $"[{ex.OriginalMessage}]"; private static readonly object TrueObject = true; private static readonly object FalseObject = false; private int _objectToStringLevel; private int _currentToStringLength; private int _currentOutputLength; private bool _hasOutputLimitEllipsis; internal bool AllowPipeArguments => _getOrSetValueLevel <= 1; public CultureInfo CurrentCulture { get { if (_cultures.Count != 0) { return _cultures.Peek(); } return CultureInfo.InvariantCulture; } } public ITemplateLoader? TemplateLoader { get; set; } public bool IsLiquid { get; protected set; } public bool AutoIndent { get; set; } public bool IndentOnEmptyLines { get; set; } [Obsolete("Use AutoIndent instead. Note that AutoIndent is true by default.")] public bool IndentWithInclude { get { return AutoIndent; } set { AutoIndent = value; } } public int LimitToString { get; set; } public int ObjectRecursionLimit { get; set; } public string NewLine { get; set; } public ScriptLang Language { get; set; } public CancellationToken CancellationToken { get; set; } public ParserOptions TemplateLoaderParserOptions { get; set; } public LexerOptions TemplateLoaderLexerOptions { get; set; } public MemberRenamerDelegate MemberRenamer { get; set; } public MemberFilterDelegate? MemberFilter { get; set; } public int LoopLimit { get; set; } public int? LoopLimitQueryable { get; set; } public int RecursiveLimit { get; set; } public bool EnableOutput { get; set; } public IScriptOutput Output => _output; public bool UseScientific { get; set; } public bool ErrorForStatementFunctionAsExpression { get; set; } public ScriptObject BuiltinObject { get; } public IScriptObject? CurrentGlobal => _globalContexts.Peek()?.LocalObject; public Dictionary<string, Template> CachedTemplates { get; } public string? CurrentSourceFile => _sourceFiles.Peek(); public TryGetVariableDelegate? TryGetVariable { get; set; } public RenderRuntimeExceptionDelegate? RenderRuntimeException { get; set; } public TryGetMemberDelegate? TryGetMember { get; set; } public Dictionary<object, object> Tags { get; } internal ScriptPipeArguments? CurrentPipeArguments => _currentPipeArguments; public int GlobalCount => _globalContexts.Count; public int OutputCount => _outputs.Count; public int CultureCount => _cultures.Count; public int SourceFileCount => _sourceFiles.Count; internal ScriptFlowState FlowState { get; set; } public TimeSpan RegexTimeOut { get; set; } public bool StrictVariables { get; set; } public bool EnableBreakAndContinueAsReturnOutsideLoop { get; set; } public bool EnableRelaxedTargetAccess { get; set; } public bool EnableRelaxedMemberAccess { get; set; } public bool EnableRelaxedFunctionAccess { get; set; } public bool EnableRelaxedIndexerAccess { get; set; } public bool EnableNullIndexer { get; set; } public ScriptNode? CurrentNode { get; private set; } public SourceSpan CurrentSpan => CurrentNode?.Span ?? default(SourceSpan); public string? CurrentIndent { get; set; } internal bool IsInLoop => _loops.Count > 0; internal bool IgnoreExceptionsWhileRewritingScientific { get; set; } protected virtual async ValueTask<Template> CreateTemplateAsync(string templatePath, ScriptNode? callerContext) { SourceSpan callerSpan = callerContext?.Span ?? CurrentSpan; ITemplateLoader templateLoader = TemplateLoader ?? throw new ScriptRuntimeException(callerSpan, "No TemplateLoader registered in TemplateContext.TemplateLoader"); string text; try { text = await templateLoader.LoadAsync(this, callerSpan, templatePath).ConfigureAwait(continueOnCapturedContext: false); } catch (Exception ex) when (!(ex is ScriptRuntimeException)) { throw new ScriptRuntimeException(callerSpan, "Unexpected exception while creating template from path `" + templatePath + "`", ex); } if (text == null) { throw new ScriptRuntimeException(callerSpan, "The result of including `" + templatePath + "` cannot be null"); } Template template = Template.Parse(text, templatePath, TemplateLoaderParserOptions, TemplateLoaderLexerOptions); if (template.HasErrors) { throw new ScriptParserRuntimeException(callerSpan, "Error while parsing template `" + templatePath + "`", template.Messages); } CachedTemplates.Add(templatePath, template); return template; } public virtual async ValueTask<object?> EvaluateAsync(ScriptNode? scriptNode, bool aliasReturnedFunction) { if (scriptNode == null) { return null; } bool previousFunctionCallState = _isFunctionCallDisabled; int previousLevel = _getOrSetValueLevel; ScriptNode previousNode = CurrentNode; try { CurrentNode = scriptNode; _getOrSetValueLevel = 0; _isFunctionCallDisabled = aliasReturnedFunction; return await scriptNode.EvaluateAsync(this).ConfigureAwait(continueOnCapturedContext: false); } catch (ScriptRuntimeException exception) when (RenderRuntimeException != null) { return RenderRuntimeException(exception); } catch (Exception ex) when (!(ex is ScriptRuntimeException)) { ScriptRuntimeException ex2 = new ScriptRuntimeException(scriptNode.Span, ex.Message, ex); if (RenderRuntimeException != null) { return RenderRuntimeException(ex2); } throw ex2; } finally { CurrentNode = previousNode; _getOrSetValueLevel = previousLevel; _isFunctionCallDisabled = previousFunctionCallState; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public async ValueTask<object?> EvaluateAsync(ScriptNode scriptNode) { return await EvaluateAsync(scriptNode, aliasReturnedFunction: false).ConfigureAwait(continueOnCapturedContext: false); } private async ValueTask<Dictionary<string, object?>?> FetchNamedArgumentsAsync(ScriptNode? callerContext) { if (!(callerContext is ScriptFunctionCall scriptFunctionCall) || scriptFunctionCall.Arguments.Count == 0) { return null; } Dictionary<string, object?> namedArgumentsValues = new Dictionary<string, object>(); foreach (ScriptExpression argument in scriptFunctionCall.Arguments) { if (argument is ScriptNamedArgument scriptNamedArgument) { string name = scriptNamedArgument.Name?.Name; if (name != null && scriptNamedArgument.Value != null) { namedArgumentsValues[name] = await scriptNamedArgument.Value.EvaluateAsync(this).ConfigureAwait(continueOnCapturedContext: false); } } } return namedArgumentsValues; } public async ValueTask<Template> GetOrCreateTemplateAsync(string templatePath, ScriptNode? callerContext) { if (!CachedTemplates.TryGetValue(templatePath, out Template value)) { value = await CreateTemplateAsync(templatePath, callerContext).ConfigureAwait(continueOnCapturedContext: false); CachedTemplates[templatePath] = value; } return value; } private async ValueTask<object?> GetOrSetValueAsync(ScriptExpression targetExpression, object? valueToSet, bool setter) { object value = null; try { if (targetExpression is IScriptVariablePath scriptVariablePath) { if (setter) { await scriptVariablePath.SetValueAsync(this, valueToSet).ConfigureAwait(continueOnCapturedContext: false); } else { value = await scriptVariablePath.GetValueAsync(this).ConfigureAwait(continueOnCapturedContext: false); } } else { if (setter) { throw new ScriptRuntimeException(targetExpression.Span, "Unsupported target expression for assignment."); } value = await EvaluateAsync(targetExpression).ConfigureAwait(continueOnCapturedContext: false); } } catch (Exception ex) when (_getOrSetValueLevel == 1 && !(ex is ScriptRuntimeException)) { throw new ScriptRuntimeException(targetExpression.Span, "Unexpected exception while accessing target expression: " + ex.Message, ex); } value = await AwaitIfNeededAsync(value).ConfigureAwait(continueOnCapturedContext: false); if (((_isFunctionCallDisabled && _getOrSetValueLevel > 1) || !_isFunctionCallDisabled) && ScriptFunctionCall.IsFunction(value)) { value = await ScriptFunctionCall.CallAsync(this, targetExpression, value, _getOrSetValueLevel == 1, null).ConfigureAwait(continueOnCapturedContext: false); } return value; } public async ValueTask<object?> GetValueAsync(ScriptExpression? target) { if (target == null) { return null; } ScriptNode previousNode = CurrentNode; _getOrSetValueLevel++; try { CurrentNode = target; return await GetOrSetValueAsync(target, null, setter: false).ConfigureAwait(continueOnCapturedContext: false); } finally { CurrentNode = previousNode; _getOrSetValueLevel--; } } public async ValueTask<string> RenderTemplateAsync(Template template, ScriptArray arguments, ScriptNode? callerContext) { _ = string.Empty; EnterRecursive(callerContext); string previousIndent = CurrentIndent; bool previousTextWasNewLine = _previousTextWasNewLine; CurrentIndent = null; PushOutput(); Dictionary<string, object> dictionary = await FetchNamedArgumentsAsync(callerContext).ConfigureAwait(continueOnCapturedContext: false); PushLocal(); try { SetValue(ScriptVariable.Arguments, arguments, asReadOnly: true, force: true); if (dictionary != null) { foreach (KeyValuePair<string, object> item in dictionary) { ScriptVariable variable = ScriptVariable.Create(item.Key, ScriptVariableScope.Local); SetValue(variable, item.Value, asReadOnly: false, force: true); } } if (previousIndent != null) { ResetPreviousNewLine(); } return await template.RenderAsync(this).ConfigureAwait(continueOnCapturedContext: false); } finally { PopLocal(); PopOutput(); CurrentIndent = previousIndent; _previousTextWasNewLine = previousTextWasNewLine; ExitRecursive(callerContext); } } public async ValueTask SetValueAsync(ScriptExpression? target, object? value) { if (target == null) { return; } _getOrSetValueLevel++; try { await GetOrSetValueAsync(target, value, setter: true).ConfigureAwait(continueOnCapturedContext: false); } finally { _getOrSetValueLevel--; } } public async ValueTask<TemplateContext> WriteAsync(string? text, int startIndex, int count) { if (text != null) { if (CurrentIndent != null) { int index = startIndex; int indexEnd = startIndex + count; while (index < indexEnd) { int newLineIndex = text.IndexOf('\n', index); if (newLineIndex < 0 || newLineIndex >= indexEnd) { if (_previousTextWasNewLine) { if (!(await WriteOutputChunkAsync(CurrentIndent, 0, CurrentIndent.Length).ConfigureAwait(continueOnCapturedContext: false))) { return this; } _previousTextWasNewLine = false; } await WriteOutputChunkAsync(text, index, indexEnd - index).ConfigureAwait(continueOnCapturedContext: false); return this; } int length = newLineIndex - index; if (_previousTextWasNewLine && (IndentOnEmptyLines || (length != 0 && (length != 1 || text[index] != '\r')))) { if (!(await WriteOutputChunkAsync(CurrentIndent, 0, CurrentIndent.Length).ConfigureAwait(continueOnCapturedContext: false))) { return this; } _previousTextWasNewLine = false; } if (!(await WriteOutputChunkAsync(text, index, length + 1).ConfigureAwait(continueOnCapturedContext: false))) { return this; } index = newLineIndex + 1; _previousTextWasNewLine = true; } } else { if (count > 0) { _previousTextWasNewLine = text[startIndex + count - 1] == '\n'; } await WriteOutputChunkAsync(text, startIndex, count).ConfigureAwait(continueOnCapturedContext: false); } } return this; } public async ValueTask<TemplateContext> WriteAsync(string? text) { if (text != null) { await WriteAsync(text, 0, text.Length).ConfigureAwait(continueOnCapturedContext: false); } return this; } public async ValueTask<TemplateContext> WriteAsync(ScriptStringSlice slice) { await WriteAsync(slice.FullText ?? string.Empty, slice.Index, slice.Length).ConfigureAwait(continueOnCapturedContext: false); return this; } public virtual async ValueTask<TemplateContext> WriteAsync(SourceSpan span, object? textAsObject) { if (textAsObject != null) { textAsObject = await AwaitIfNeededAsync(textAsObject).ConfigureAwait(continueOnCapturedContext: false); string text = ObjectToString(textAsObject); if (text != null) { await WriteAsync(text).ConfigureAwait(continueOnCapturedContext: false); } } return this; } public async ValueTask<TemplateContext> WriteLineAsync() { await WriteAsync(NewLine).ConfigureAwait(continueOnCapturedContext: false); return this; } private async ValueTask<bool> WriteOutputChunkAsync(string text, int startIndex, int count) { if (count <= 0) { return true; } int allowedCount = GetAllowedOutputCount(count); if (allowedCount > 0) { await Output.WriteAsync(text, startIndex, allowedCount, CancellationToken).ConfigureAwait(continueOnCapturedContext: false); _currentOutputLength += allowedCount; } if (allowedCount < count) { await WriteOutputLimitEllipsisAsync().ConfigureAwait(continueOnCapturedContext: false); return false; } return true; } private async ValueTask WriteOutputLimitEllipsisAsync() { if (!_hasOutputLimitEllipsis) { await Output.WriteAsync("...", 0, 3, CancellationToken).ConfigureAwait(continueOnCapturedContext: false); _hasOutputLimitEllipsis = true; } } public TemplateContext() : this(null, null) { } public TemplateContext(ScriptObject? builtin) : this(builtin, null) { } public TemplateContext(IEqualityComparer<string>? keyComparer) : this(null, keyComparer) { } public TemplateContext(ScriptObject? builtin, IEqualityComparer<string>? keyComparer) { BuiltinObject = builtin ?? GetDefaultBuiltinObject(); EnableOutput = true; EnableBreakAndContinueAsReturnOutsideLoop = false; EnableRelaxedTargetAccess = false; EnableRelaxedMemberAccess = true; EnableRelaxedFunctionAccess = false; EnableRelaxedIndexerAccess = true; AutoIndent = true; IndentOnEmptyLines = true; LoopLimit = 1000; RecursiveLimit = 100; LimitToString = 1048576; ObjectRecursionLimit = 20; MemberRenamer = StandardMemberRenamer.Default; RegexTimeOut = TimeSpan.FromSeconds(10.0); TemplateLoaderParserOptions = new ParserOptions(); TemplateLoaderLexerOptions = LexerOptions.Default; NewLine = Environment.NewLine; Language = ScriptLang.Default; _outputs = new FastStack<IScriptOutput>(4); _output = new StringBuilderOutput(); _outputs.Push(_output); _globalContexts = new FastStack<VariableContext>(4); _functionContexts = new FastStack<VariableContext>(4); _availableGlobalContexts = new FastStack<VariableContext>(4); _availableFunctionContexts = new FastStack<VariableContext>(4); _availableLocalContexts = new FastStack<VariableContext>(4); _localContexts = new FastStack<VariableContext>(4); _availableStores = new FastStack<ScriptObject>(4); _cultures = new FastStack<CultureInfo>(4); _caseValues = new FastStack<object>(4); _sourceFiles = new FastStack<string>(4); _memberAccessors = new Dictionary<Type, IObjectAccessor>(); _listAccessors = new Dictionary<Type, IListAccessor>(); _loops = new FastStack<ScriptLoopStatementBase>(4); BlockDelegates = new FastStack<ScriptBlockStatement>(4); _availablePipeArguments = new FastStack<ScriptPipeArguments>(4); _pipeArguments = new FastStack<ScriptPipeArguments>(4); _availableScriptExpressionLists = new FastStack<List<ScriptExpression>>(4); _availableReflectionArguments = new object[65][]; _keyComparer = keyComparer; for (int i = 0; i < _availableReflectionArguments.Length; i++) { _availableReflectionArguments[i] = new object[i]; } _isFunctionCallDisabled = false; CachedTemplates = new Dictionary<string, Template>(); Tags = new Dictionary<object, object>(); PushGlobal(BuiltinObject); } public void CheckAbort() { RuntimeHelpers.EnsureSufficientExecutionStack(); CancellationToken cancellationToken = CancellationToken; if (cancellationToken.IsCancellationRequested) { throw new ScriptAbortException(CurrentNode?.Span ?? default(SourceSpan), cancellationToken); } } public void PushCulture(CultureInfo culture) { if (culture == null) { throw new ArgumentNullException("culture"); } _cultures.Push(culture); } public CultureInfo PopCulture() { if (_cultures.Count == 0) { throw new InvalidOperationException("Cannot PopCulture more than PushCulture"); } return _cultures.Pop(); } internal void PushPipeArguments() { ScriptPipeArguments scriptPipeArguments = ((_availablePipeArguments.Count > 0) ? _availablePipeArguments.Pop() : new ScriptPipeArguments(1)); _pipeArguments.Push(scriptPipeArguments); _currentPipeArguments = scriptPipeArguments; } internal void ClearPipeArguments() { while (_pipeArguments.Count > 0) { PopPipeArguments(); } } internal List<ScriptExpression> GetOrCreateListOfScriptExpressions(int capacity) { List<ScriptExpression> list = ((_availableScriptExpressionLists.Count > 0) ? _availableScriptExpressionLists.Pop() : new List<ScriptExpression>()); if (capacity > list.Capacity) { list.Capacity = capacity; } return list; } internal void ReleaseListOfScriptExpressions(List<ScriptExpression> list) { _availableScriptExpressionLists.Push(list); list.Clear(); } internal object?[] GetOrCreateReflectionArguments(int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length"); } if (length >= _availableReflectionArguments.Length) { return new object[length]; } object[] array = _availableReflectionArguments[length] ?? new object[length]; if (length > 0) { _availableReflectionArguments[length] = array[0] as object[]; array[0] = null; } return array; } internal void ReleaseReflectionArguments(object?[] reflectionArguments) { if (reflectionArguments != null && reflectionArguments.Length < _availableReflectionArguments.Length) { Array.Clear(reflectionArguments, 0, reflectionArguments.Length); object[] array = _availableReflectionArguments[reflectionArguments.Length]; _availableReflectionArguments[reflectionArguments.Length] = reflectionArguments; if (reflectionArguments.Length != 0) { reflectionArguments[0] = array; } } } internal void PopPipeArguments() { if (_pipeArguments.Count == 0) { throw new InvalidOperationException("Cannot PopPipeArguments more than PushPipeArguments"); } ScriptPipeArguments scriptPipeArguments = _pipeArguments.Pop(); scriptPipeArguments.Clear(); _currentPipeArguments = ((_pipeArguments.Count > 0) ? _pipeArguments.Peek() : null); _availablePipeArguments.Push(scriptPipeArguments); } public void PushSourceFile(string sourceFile) { if (sourceFile == null) { throw new ArgumentNullException("sourceFile"); } _sourceFiles.Push(sourceFile); } public string PopSourceFile() { if (_sourceFiles.Count == 0) { throw new InvalidOperationException("Cannot PopSourceFile more than PushSourceFile"); } return _sourceFiles.Pop(); } public object? GetValue(ScriptExpression? target) { if (target == null) { return null; } ScriptNode currentNode = CurrentNode; _getOrSetValueLevel++; try { CurrentNode = target; return GetOrSetValue(target, null, setter: false); } finally { CurrentNode = currentNode; _getOrSetValueLevel--; } } public void SetValue(ScriptVariable variable, bool value) { SetValue(variable, value ? TrueObject : FalseObject); } public virtual void Import(SourceSpan span, object objectToImport) { if (!(objectToImport is ScriptObject)) { throw new ScriptRuntimeException(span, "Unexpected value `" + GetTypeName(objectToImport) + "` for import. Expecting an plain script object."); } (CurrentGlobal ?? throw new ScriptRuntimeException(span, "No current global object is available.")).Import(objectToImport); } public void SetValue(ScriptExpression? target, object? value) { if (target == null) { return; } _getOrSetValueLevel++; try { GetOrSetValue(target, value, setter: true); } finally { _getOrSetValueLevel--; } } public void PushOutput() { PushOutput(new StringBuilderOutput()); } public void PushOutput(IScriptOutput output) { _output = output ?? throw new ArgumentNullException("output"); _outputs.Push(_output); } public IScriptOutput PopOutput() { if (_outputs.Count == 1) { throw new InvalidOperationException("Unexpected PopOutput for top level writer"); } IScriptOutput result = _outputs.Pop(); _output = _outputs.Peek(); return result; } public virtual TemplateContext Write(SourceSpan span, object? textAsObject) { if (textAsObject != null) { textAsObject = AwaitIfNeeded(textAsObject); string text = ObjectToString(textAsObject); if (text != null) { Write(text); } } return this; } private void ResetOutputLimitTracking() { _currentOutputLength = 0; _hasOutputLimitEllipsis = false; } private bool WriteOutputChunk(string text, int startIndex, int count) { if (count <= 0) { return true; } int allowedOutputCount = GetAllowedOutputCount(count); if (allowedOutputCount > 0) { Output.Write(text, startIndex, allowedOutputCount); _currentOutputLength += allowedOutputCount; } if (allowedOutputCount < count) { WriteOutputLimitEllipsis(); return false; } return true; } private int GetAllowedOutputCount(int requestedCount) { if (LimitToString <= 0) { return requestedCount; } int num = LimitToString - _currentOutputLength; if (num <= 0) { return 0; } return Math.Min(requestedCount, num); } private void WriteOutputLimitEllipsis() { if (!_hasOutputLimitEllipsis) { Output.Write("...", 0, 3); _hasOutputLimitEllipsis = true; } } public TemplateContext Write(string? text) { if (text != null) { Write(text, 0, text.Length); } return this; } public TemplateContext WriteLine() { Write(NewLine); return this; } public TemplateContext Write(ScriptStringSlice slice) { Write(slice.FullText ?? string.Empty, slice.Index, slice.Length); return this; } public TemplateContext Write(string? text, int startIndex, int count) { if (text != null) { if (CurrentIndent != null) { int num = startIndex; int num2 = startIndex + count; while (num < num2) { int num3 = text.IndexOf('\n', num); if (num3 < 0 || num3 >= num2) { if (_previousTextWasNewLine) { if (!WriteOutputChunk(CurrentIndent, 0, CurrentIndent.Length)) { return this; } _previousTextWasNewLine = false; } WriteOutputChunk(text, num, num2 - num); return this; } int num4 = num3 - num; if (_previousTextWasNewLine && (IndentOnEmptyLines || (num4 != 0 && (num4 != 1 || text[num] != '\r')))) { if (!WriteOutputChunk(CurrentIndent, 0, CurrentIndent.Length)) { return this; } _previousTextWasNewLine = false; } if (!WriteOutputChunk(text, num, num4 + 1)) { return this; } num = num3 + 1; _previousTextWasNewLine = true; } } else { if (count > 0) { _previousTextWasNewLine = text[startIndex + count - 1] == '\n'; } WriteOutputChunk(text, startIndex, count); } } return this; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public object? Evaluate(ScriptNode scriptNode) { return Evaluate(scriptNode, aliasReturnedFunction: false); } public virtual object? Evaluate(ScriptNode? scriptNode, bool aliasReturnedFunction) { if (scriptNode == null) { return null; } bool isFunctionCallDisabled = _isFunctionCallDisabled; int getOrSetValueLevel = _getOrSetValueLevel; ScriptNode currentNode = CurrentNode; try { CurrentNode = scriptNode; _getOrSetValueLevel = 0; _isFunctionCallDisabled = aliasReturnedFunction; return scriptNode.Evaluate(this); } catch (ScriptRuntimeException exception) when (RenderRuntimeException != null) { return RenderRuntimeException(exception); } catch (Exception ex) when (!(ex is ScriptRuntimeException)) { ScriptRuntimeException ex2 = new ScriptRuntimeException(scriptNode.Span, ex.Message, ex); if (RenderRuntimeException != null) { return RenderRuntimeException(ex2); } throw ex2; } finally { CurrentNode = currentNode; _getOrSetValueLevel = getOrSetValueLevel; _isFunctionCallDisabled = isFunctionCallDisabled; } } public IObjectAccessor GetMemberAccessor(object target) { if (target == null) { return NullAccessor.Default; } Type type = target.GetType(); if (!_memberAccessors.TryGetValue(type, out IObjectAccessor value)) { value = GetMemberAccessorImpl(target) ?? NullAccessor.Default; _memberAccessors.Add(type, value); } return value; } public virtual void Reset() { while (OutputCount > 1) { PopOutput(); } ((StringBuilderOutput)Output).Builder.Length = 0; while (GlobalCount > 1) { PopGlobal(); } while (CultureCount > 0) { PopCulture(); } while (SourceFileCount > 0) { PopSourceFile(); } CachedTemplates.Clear(); _memberAccessors.Clear(); ResetOutputLimitTracking(); } protected virtual IObjectAccessor? GetMemberAccessorImpl(object target) { Type type = target.GetType(); if (target is IScriptObject) { return ScriptObjectAccessor.Default; } if (target is string) { return StringAccessor.Default; } if (type.IsPrimitiveOrDecimal()) { return PrimitiveAccessor.Default; } if (DictionaryAccessor.TryGet(target, out IObjectAccessor accessor) && accessor != null) { return accessor; } if (type.IsArray) { return ArrayAccessor.Default; } if (target is IList) { return ListAccessor.Default; } return new TypedObjectAccessor(type, _keyComparer, MemberFilter, MemberRenamer); } public static ScriptObject GetDefaultBuiltinObject() { return new BuiltinFunctions(); } public void EnterRecursive(ScriptNode? node) { SourceSpan span = node?.Span ?? CurrentSpan; try { RuntimeHelpers.EnsureSufficientExecutionStack(); } catch (InsufficientExecutionStackException) { throw new ScriptRuntimeException(span, "Exceeding recursive depth limit, near to stack overflow"); } _callDepth++; if (RecursiveLimit != 0 && _callDepth > RecursiveLimit) { throw new ScriptRuntimeException(span, $"Exceeding number of recursive depth limit `{RecursiveLimit}` for node: `{node}`"); } } public void ExitRecursive(ScriptNode? node) { _callDepth--; if (_callDepth < 0) { throw new ScriptRuntimeException(node?.Span ?? CurrentSpan, $"unexpected ExitRecursive not matching EnterRecursive for `{node}`"); } } internal void EnterFunction(ScriptNode? caller) { EnterRecursive(caller); } internal void ExitFunction(ScriptNode? caller) { ExitRecursive(caller); } internal void EnterLoop(ScriptLoopStatementBase loop) { if (loop == null) { throw new ArgumentNullException("loop"); } _loops.Push(loop); PushVariableScope(VariableScope.Loop); OnEnterLoop(loop); } protected virtual void OnEnterLoop(ScriptLoopStatementBase loop) { } internal void ExitLoop(ScriptLoopStatementBase loop) { try { OnExitLoop(loop); } finally { PopVariableScope(VariableScope.Loop); _loops.Pop(); if (!IsInLoop) { _loopStep = 0; } } } protected virtual void OnExitLoop(ScriptLoopStatementBase loop) { } internal void StepLoop(SourceSpan span, ref int loopStep, LoopType loopType = LoopType.Default) { CheckAbort(); loopStep++; int num = ((loopType == LoopType.Queryable) ? LoopLimitQueryable.GetValueOrDefault(LoopLimit) : LoopLimit); if (num != 0 && loopStep > num) { throw new ScriptRuntimeException(span, $"Exceeding number of iteration limit `{num}` for internal iteration."); } } internal bool StepLoop(ScriptLoopStatementBase loop, LoopType loopType = LoopType.Default) { _loopStep++; int num = ((loopType != LoopType.Queryable) ? LoopLimit : LoopLimitQueryable.GetValueOrDefault(LoopLimit)); if (num != 0 && _loopStep > num) { throw new ScriptRuntimeException(_loops.Peek().Span, $"Exceeding number of iteration limit `{num}` for loop statement."); } return OnStepLoop(loop); } protected virtual bool OnStepLoop(ScriptLoopStatementBase loop) { return true; } internal void PushCase(object? caseValue) { _caseValues.Push(caseValue); } internal object? PeekCase() { return _caseValues.Peek(); } internal object? PopCase() { if (_caseValues.Count == 0) { throw new InvalidOperationException("Cannot PopCase more than PushCase"); } return _caseValues.Pop(); } private object? GetOrSetValue(ScriptExpression targetExpression, object? valueToSet, bool setter) { object value = null; try { if (targetExpression is IScriptVariablePath scriptVariablePath) { if (setter) { scriptVariablePath.SetValue(this, valueToSet); } else { value = scriptVariablePath.GetValue(this); } } else { if (setter) { throw new ScriptRuntimeException(targetExpression.Span, "Unsupported target expression for assignment."); } value = Evaluate(targetExpression); } } catch (Exception ex) when (_getOrSetValueLevel == 1 && !(ex is ScriptRuntimeException)) { throw new ScriptRuntimeException(targetExpression.Span, "Unexpected exception while accessing target expression: " + ex.Message, ex); } value = AwaitIfNeeded(value); if (((_isFunctionCallDisabled && _getOrSetValueLevel > 1) || !_isFunctionCallDisabled) && ScriptFunctionCall.IsFunction(value)) { value = ScriptFunctionCall.Call(this, targetExpression, value, _getOrSetValueLevel == 1, null); } return value; } public IListAccessor GetListAccessor(object target) { return TryGetListAccessor(target) ?? throw new ScriptRuntimeException(CurrentSpan, "Unable to find a list accessor for `" + GetTypeName(target) + "`."); } internal IListAccessor? TryGetListAccessor(object target) { Type type = target.GetType(); if (!_listAccessors.TryGetValue(type, out IListAccessor value)) { value = GetListAccessorImpl(target, type); if (value != null) { _listAccessors.Add(type, value); } } return value; } protected virtual IListAccessor? GetListAccessorImpl(object target, Type type) { if (type.IsArray) { return ArrayAccessor.Default; } if (type == typeof(string)) { return StringAccessor.Default; } if (type.IsPrimitiveOrDecimal()) { return PrimitiveAccessor.Default; } if (target is IList) { return ListAccessor.Default; } return null; } public void ResetPreviousNewLine() { _previousTextWasNewLine = false; } public virtual string? GetTemplatePathFromName(string? templateName, ScriptNode? callerContext) { SourceSpan span = callerContext?.Span ?? CurrentSpan; if (string.IsNullOrEmpty(templateName)) { throw new ScriptRuntimeException(span, "Include template name cannot be null or empty"); } string templateName2 = templateName ?? throw new ScriptRuntimeException(span, "Include template name cannot be null or empty"); return ConvertTemplateNameToPath(templateName2, callerContext); } protected string ConvertTemplateNameToPath(string templateName, ScriptNode? callerContext) { SourceSpan sourceSpan = callerContext?.Span ?? CurrentSpan; if (TemplateLoader == null) { throw new ScriptRuntimeException(sourceSpan, "Unable to include <" + templateName + ">. No TemplateLoader registered in TemplateContext.TemplateLoader"); } ITemplateLoader templateLoader = TemplateLoader; string path; try { path = templateLoader.GetPath(this, sourceSpan, templateName); } catch (Exception ex) when (!(ex is ScriptRuntimeException)) { throw new ScriptRuntimeException(sourceSpan, "Unexpected exception while getting the path for the include name `" + templateName + "`", ex); } if (path == null) { throw new ScriptRuntimeException(sourceSpan, "Include template path is null for `" + templateName); } return path; } public Template GetOrCreateTemplate(string templatePath, ScriptNode? callerContext) { if (!CachedTemplates.TryGetValue(templatePath, out Template value)) { value = CreateTemplate(templatePath, callerContext); CachedTemplates[templatePath] = value; } return value; } protected virtual Template CreateTemplate(string templatePath, ScriptNode? callerContext) { SourceSpan sourceSpan = callerContext?.Span ?? CurrentSpan; ITemplateLoader templateLoader = TemplateLoader ?? throw new ScriptRuntimeException(sourceSpan, "No TemplateLoader registered in TemplateContext.TemplateLoader"); string text; try { text = templateLoader.Load(this, sourceSpan, templatePath); } catch (Exception ex) when (!(ex is ScriptRuntimeException)) { throw new ScriptRuntimeException(sourceSpan, "Unexpected exception while creating template from path `" + templatePath + "`", ex); } if (text == null) { throw new ScriptRuntimeException(sourceSpan, "The result of including `" + templatePath + "` cannot be null"); } Template template = Template.Parse(text, templatePath, TemplateLoaderParserOptions, TemplateLoaderLexerOptions); if (template.HasErrors) { throw new ScriptParserRuntimeException(sourceSpan, "Error while parsing template `" + templatePath + "`", template.Messages); } CachedTemplates.Add(templatePath, template); return template; } private Dictionary<string, object?>? FetchNamedArguments(ScriptNode? callerContext) { if (!(callerContext is ScriptFunctionCall scriptFunctionCall) || scriptFunctionCall.Arguments.Count == 0) { return null; } Dictionary<string, object> dictionary = new Dictionary<string, object>(); foreach (ScriptExpression argument in scriptFunctionCall.Arguments) { if (argument is ScriptNamedArgument scriptNamedArgument) { string text = scriptNamedArgument.Name?.Name; if (text != null && scriptNamedArgument.Value != null) { object value = scriptNamedArgument.Value.Evaluate(this); dictionary[text] = value; } } } return dictionary; } public string RenderTemplate(Template template, ScriptArray arguments, ScriptNode? callerContext) { string empty = string.Empty; EnterRecursive(callerContext); string currentIndent = CurrentIndent; bool previousTextWasNewLine = _previousTextWasNewLine; CurrentIndent = null; PushOutput(); Dictionary<string, object> dictionary = FetchNamedArguments(callerContext); PushLocal(); try { SetValue(ScriptVariable.Arguments, arguments, asReadOnly: true, force: true); if (dictionary != null) { foreach (KeyValuePair<string, object> item in dictionary) { ScriptVariable variable = ScriptVariable.Create(item.Key, ScriptVariableScope.Local); SetValue(variable, item.Value, asReadOnly: false, force: true); } } if (currentIndent != null) { ResetPreviousNewLine(); } return template.Render(this); } finally { PopLocal(); PopOutput(); CurrentIndent = currentIndent; _previousTextWasNewLine = previousTextWasNewLine; ExitRecursive(callerContext); } } public object? GetFormat(Type? formatType) { return CurrentCulture.GetFormat(formatType); } public virtual object? IsEmpty(SourceSpan span, object? against) { if (against == null) { return null; } if (against is IList) { return ((IList)against).Count == 0; } if (against is IEnumerable) { return !((IEnumerable)against).GetEnumerator().MoveNext(); } if (against.GetType().IsPrimitiveOrDecimal()) { return false; } return GetMemberAccessor(against).GetMemberCount(this, span, against) > 0; } public virtual IList? ToList(SourceSpan span, object? value) { if (value == null) { return null; } if (value is IList) { return (IList)value; } return new ScriptArray((value as IEnumerable) ?? throw new ScriptRuntimeException(span, "Unexpected list value. Expecting an array, list or iterator. Unable to convert to a list.")); } public virtual string? ObjectToString(object? value, bool nested = false) { if (_objectToStringLevel == 0) { _currentToStringLength = 0; } try { _objectToStringLevel++; if (ObjectRecursionLimit != 0 && _objectToStringLevel > ObjectRecursionLimit) { throw new InvalidOperationException("Structure is too deeply nested or contains reference loops."); } string text = ObjectToStringImpl(value, nested); if (LimitToString > 0 && _objectToStringLevel == 1 && text != null && text.Length >= LimitToString) { return text + "..."; } return text; } finally { _objectToStringLevel--; } } private string? ObjectToStringImpl(object? value, bool nested) { if (LimitToString > 0 && _currentToStringLength >= LimitToString) { return string.Empty; } if (value is string text) { if (LimitToString > 0 && _currentToStringLength + text.Length >= LimitToString) { int num = LimitToString - _currentToStringLength; if (num <= 0) { return string.Empty; } string text2 = text.Substring(0, num); if (!nested) { return text2; } return "\"" + StringFunctions.Escape(text2); } if (!nested) { return text; } return "\"" + StringFunctions.Escape(text) + "\""; } if (value == null || value == EmptyScriptObject.Default) { if (!nested) { return null; } return "null"; } if (value is bool) { if (!(bool)value) { return "false"; } return "true"; } if (value is DateTime value2) { bool strictVariables = StrictVariables; try { StrictVariables = false; if (GetValue(DateTimeFunctions.DateVariable) is DateTimeFunctions dateTimeFunctions) { return dateTimeFunctions.ToString(value2, dateTimeFunctions.Format, CurrentCulture); } } finally { StrictVariables = strictVariables; } } if (value is IFormattable formattable) { return formattable.ToString(null, this); } if (value is IConvertible convertible) { return convertible.ToString(this); } if (value is IEnumerable enumerable) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("["); _currentToStringLength++; bool flag = true; foreach (object item in enumerable) { if (!flag) { stringBuilder.Append(", "); _currentToStringLength += 2; } string text3 = ObjectToString(item, nested: true); stringBuilder.Append(text3); if (text3 != null) { _currentToStringLength += text3.Length; } if (LimitToString > 0 && _currentToStringLength >= LimitToString) { return stringBuilder.ToString(); } flag = false; } stringBuilder.Append("]"); _currentToStringLength++; return stringBuilder.ToString(); } string fullName = value.GetType().FullName; if (fullName != null && fullName.StartsWith("System.Collections.Generic.KeyValuePair")) { ScriptObject scriptObject = new ScriptObject(2); scriptObject.Import(value, null, MemberRenamer); return ObjectToString(scriptObject, nested: true); } if (value is IScriptCustomFunction && !(value is ScriptFunction)) { return "<function>"; } return value.ToString(); } public virtual bool ToBool(SourceSpan span, object? value) { if (value == null || value == EmptyScriptObject.Default) { return false; } if (value is bool) { return (bool)value; } if (UseScientific) { Type type = value.GetType(); if (type.IsPrimitive || type == typeof(decimal)) { return Convert.ToBoolean(value); } if (value is BigInteger bigInteger) { return bigInteger != BigInteger.Zero; } } return true; } public virtual int ToInt(SourceSpan span, object? value) { if (value == null) { return 0; } if (value is int) { return (int)value; } checked { try { if (value is BigInteger bigInteger) { return (int)bigInteger; } if (value is IScriptConvertibleTo scriptConvertibleTo && scriptConvertibleTo.TryConvertTo(this, span, typeof(int), out object value2)) { return (int)value2; } if (value is uint num) { return (int)num; } if (value is ulong num2) { return (int)num2; } return Convert.ToInt32(value, CurrentCulture); } catch (Exception innerException) { throw new ScriptRuntimeException(span, "Unable to convert type `" + GetTypeName(value) + "` to int", innerException); } } } public virtual string GetTypeName(object? value) { if (value == null) { return "null"; } if (value is Type type) { return type.ScriptPrettyName(); } if (value is IScriptCustomTypeInfo scriptCustomTypeInfo) { return scriptCustomTypeInfo.TypeName; } return value.GetType().ScriptPrettyName(); } [return: MaybeNull] public T ToObject<T>(SourceSpan span, object? value) { object obj = ToObject(span, value, typeof(T)); if (obj is T) { return (T)obj; } if (obj == null && default(T) == null) { return default(T); } throw new ScriptRuntimeException(span, "Unable to convert type `" + GetTypeName(value) + "` to `" + GetTypeName(typeof(T)) + "`"); } public virtual object? ToObject(SourceSpan span, object? value, Type destinationType) { if ((object)destinationType == null) { throw new ArgumentNullException("destinationType"); } bool flag; (flag, destinationType) = GetNullableInfo(destinationType); if (flag && value == null) { return null; } Type type = value?.GetType(); if (destinationType == type) { return value; } if (destinationType == typeof(string)) { return ObjectToString(value); } if (destinationType == typeof(int)) { return ToInt(span, value); } if (destinationType == typeof(bool)) { return ToBool(span, value); } if (value == null) { if (destinationType == typeof(double)) { return 0.0; } if (destinationType == typeof(float)) { return 0f; } if (destinationType == typeof(long)) { return 0L; } if (destinationType == typeof(decimal)) { return 0m; } if (destinationType == typeof(BigInteger)) { return new BigInteger(0); } return null; } if (destinationType.IsEnum) { try { if (value is string value2) { return Enum.Parse(destinationType, value2); } return Enum.ToObject(destinationType, value); } catch (Exception innerException) { throw new ScriptRuntimeException(span, "Unable to convert type `" + GetTypeName(value) + "` to `" + GetTypeName(destinationType) + "`", innerException); } } if (value is IScriptConvertibleTo scriptConvertibleTo && scriptConvertibleTo.TryConvertTo(this, span, destinationType, out object value3)) { return value3; } if (typeof(IScriptConvertibleFrom).IsAssignableFrom(destinationType)) { if (!(Activator.CreateInstance(destinationType) is IScriptConvertibleFrom scriptConvertibleFrom)) { throw new ScriptRuntimeException(span, "Unable to create an instance of `" + GetTypeName(destinationType) + "`"); } if (scriptConvertibleFrom.TryConvertFrom(this, span, value)) { return scriptConvertibleFrom; } } Type c = type; if ((object)type != null && type.IsPrimitiveOrDecimal() && destinationType.IsPrimitiveOrDecimal()) { try { if (destinationType == typeof(BigInteger)) { if (type == typeof(char)) { return new BigInteger((char)value); } if (type == typeof(bool)) { return new BigInteger(((bool)value) ? 1 : 0); } if (type == typeof(float)) { return new BigInteger((float)value); } if (type == typeof(double)) { return new BigInteger((double)value); } if (type == typeof(int)) { return new BigInteger((int)value); } if (type == typeof(uint)) { return new BigInteger((uint)value); } if (type == typeof(long)) { return new BigInteger((long)value); } if (type == typeof(ulong)) { return new BigInteger((ulong)value); } } else if (type == typeof(BigInteger)) { if (destinationType == typeof(char)) { return (char)(int)(BigInteger)value; } if (destinationType == typeof(float)) { return (float)(BigInteger)value; } if (destinationType == typeof(double)) { return (double)(BigInteger)value; } if (destinationType == typeof(uint)) { return (uint)(BigInteger)value; } if (destinationType == typeof(long)) { return (long)(BigInteger)value; } if (destinationType == typeof(ulong)) { return (ulong)(BigInteger)value; } } return Convert.ChangeType(value, destinationType, CurrentCulture); } catch (Exception innerException2) { throw new ScriptRuntimeException(span, "Unable to convert type `" + GetTypeName(value) + "` to `" + GetTypeName(destinationType) + "`", innerException2); } } if (destinationType == typeof(IList)) { return ToList(span, value); } if (destinationType.IsAssignableFrom(c)) { return value; } throw new ScriptRuntimeException(span, "Unable to convert type `" + GetTypeName(value) + "` to `" + GetTypeName(destinationType) + "`"); static (bool isNullable, Type DestinationType) GetNullableInfo(Type type2) { type2 = type2 ?? throw new ArgumentNullException("destinationType"); Type underlyingType = Nullable.GetUnderlyingType(type2); if ((object)underlyingType != null) { return (isNullable: true, DestinationType: underlyingType); } return (isNullable: false, DestinationType: type2); } } public void PushGlobal(IScriptObject scriptObject) { PushGlobalOnly(scriptObject); PushLocal(); } internal void PushGlobalOnly(IScriptObject scriptObject) { if (scriptObject == null) { throw new ArgumentNullException("scriptObject"); } _globalContexts.Push(GetOrCreateGlobalContext(scriptObject)); } private VariableContext GetOrCreateGlobalContext(IScriptObject globalObject) { if (_availableGlobalContexts.Count == 0) { return new VariableContext(globalObject); } VariableContext variableContext = _availableGlobalContexts.Pop(); variableContext.LocalObject = globalObject; return variableContext; } internal IScriptObject PopGlobalOnly() { if (_globalContexts.Count == 1) { throw new InvalidOperationException("Unexpected PopGlobal() not matching a PushGlobal"); } VariableContext variableContext = _globalContexts.Pop(); IScriptObject? localObject = variableContext.LocalObject; variableContext.LocalObject = null; _availableGlobalContexts.Push(variableContext); if (localObject == null) { throw new InvalidOperationException("Unexpected null global store."); } return localObject; } public IScriptObject PopGlobal() { IScriptObject result = PopGlobalOnly(); PopLocal(); return result; } public void PushLocal() { PushVariableScope(VariableScope.Local); } public void PopLocal() { PopVariableScope(VariableScope.Local); } internal void PushFunction(ScriptObject? functionVariables, bool hideParentFunctionScopes) { VariableContext variableContext = ((_availableFunctionContexts.Count > 0) ? _availableFunctionContexts.Pop() : new VariableContext(null)); variableContext.LocalObject = functionVariables; variableContext.HideParentFunctionScopes = hideParentFunctionScopes; _functionContexts.Push(variableContext); PushLocal(); } internal void PopFunction() { VariableContext variableContext = _functionContexts.Pop(); PopLocal(); if (variableContext.LocalObject is ScriptObject scriptObject) { scriptObject.Clear(); } variableContext.LocalObject = null; variableContext.HideParentFunctionScopes = false; _availableFunctionContexts.Push(variableContext); } public void SetValue(ScriptVariable variable, object? value, bool asReadOnly = false) { if ((object)variable == null) { throw new ArgumentNullException("variable"); } if (!GetStoreForWrite(variable).TrySetValue(this, variable.Span, variable.Name, value, asReadOnly)) { throw new ScriptRuntimeException(variable.Span, $"Cannot set value on the readonly variable `{variable}`"); } } public void SetValue(ScriptVariable variable, object? value, bool asReadOnly, bool force) { if ((object)variable == null) { throw new ArgumentNullException("variable"); } IScriptObject storeForWrite = GetStoreForWrite(variable); if (force) { storeForWrite.Remove(variable.Name); storeForWrite.TrySetValue(this, variable.Span, variable.Name, value, asReadOnly); } else if (!storeForWrite.TrySetValue(this, variable.Span, variable.Name, value, asReadOnly)) { throw new ScriptRuntimeException(variable.Span, $"Cannot set value on the readonly variable `{variable}`"); } } public void DeleteValue(ScriptVariable variable) { if ((object)variable == null) { throw new ArgumentNullException("variable"); } GetStoreForWrite(variable).Remove(variable.Name); } public void SetReadOnly(ScriptVariable variable, bool isReadOnly = true) { if ((object)variable == null) { throw new ArgumentNullException("variable"); } GetStoreForWrite(variable).SetReadOnly(variable.Name, isReadOnly); } public virtual void SetLoopVariable(ScriptVariable variable, object? value) { if ((object)variable == null) { throw new ArgumentNullException("variable"); } VariableContext obj = ((variable.Scope == ScriptVariableScope.Global) ? GetCurrentGlobalVariableContext() : (_currentLocalContext ?? throw new InvalidOperationException("No current local context is available."))); if (obj.Loops.Count == 0) { throw new InvalidOperationException("Cannot set a loop global variable without a loop variable store."); } if (!obj.Loops.Peek().TrySetValue(this, variable.Span, variable.Name, value, readOnly: false)) { throw new ScriptRuntimeException(variable.Span, $"Cannot set value on the variable `{variable}`"); } } private void PushLocalContext(ScriptObject? locals = null) { VariableContext variableContext = ((_availableLocalContexts.Count > 0) ? _availableLocalContexts.Pop() : new VariableContext(null)); variableContext.LocalObject = locals; _localContexts.Push(variableContext); _currentLocalContext = variableContext; } private ScriptObject? PopLocalContext() { VariableContext variableContext = _localContexts.Pop(); ScriptObject result = variableContext.LocalObject as ScriptObject; variableContext.LocalObject = null; _availableLocalContexts.Push(variableContext); _currentLocalContext = _localContexts.Peek(); return result; } public object? GetValue(ScriptVariable variable) { if ((object)variable == null) { throw new ArgumentNullException("variable"); } IEnumerable<IScriptObject> storeForRead = GetStoreForRead(variable); object value = null; foreach (IScriptObject item in storeForRead) { if (item.TryGetValue(this, variable.Span, variable.Name, out value)) { return value; } } bool found = false; if (TryGetVariable != null && TryGetVariable(this, variable.Span, variable, out value)) { found = true; } CheckVariableFound(variable, found); return value; } public object? GetValue(ScriptVariableGlobal variable) { if ((object)variable == null) { throw new ArgumentNullException("variable"); } object value = null; foreach (VariableContext visibleFunctionContext in GetVisibleFunctionContexts()) { if (TryGetValue(visibleFunctionContext, variable, out value)) { return value; } } int count = _globalContexts.Count; VariableContext[] items = _globalContexts.Items; for (int num = count - 1; num >= 0; num--) { VariableContext context = items[num]; if (TryGetValue(context, variable, out value)) { return value; } } bool found = false; if (TryGetVariable != null && TryGetVariable(this, variable.Span, variable, out value)) { found = true; } CheckVariableFound(variable, found); return value; } internal object? AwaitIfNeeded(object? value) { return value; } internal async ValueTask<object?> AwaitIfNeededAsync(object? value) { if (value == null) { return null; } if (value is Task task) { await task.ConfigureAwait(continueOnCapturedContext: false); return task.GetType().GetProperty("Result")?.GetValue(task); } if (value is ValueTask valueTask) { await valueTask.ConfigureAwait(continueOnCapturedContext: false); return null; } Type type = value.GetType(); if (type.IsValueType && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ValueTask<>)) { MethodInfo method = type.GetMethod("AsTask", Type.EmptyTypes); if ((object)method == null) { return value; } if (!(method.Invoke(value, null) is Task awaitedTask)) { return value; } await awaitedTask.ConfigureAwait(continueOnCapturedContext: false); return awaitedTask.GetType().GetProperty("Result")?.GetValue(awaitedTask); } return value; } public ValueTask<object?> GetValueAsync(ScriptVariableGlobal variable) { return new ValueTask<object>(GetValue(variable)); } public ValueTask<object?> GetValueAsync(ScriptVariable variable) { return new ValueTask<object>(GetValue(variable)); } public ValueTask SetValueAsync(ScriptVariable variable, object? value, bool asReadOnly = false) { SetValue(variable, value, asReadOnly); return default(ValueTask); } private IScriptObject GetStoreForWrite(ScriptVariable variable) { ScriptVariableScope scope = variable.Scope; IScriptObject scriptObject = null; switch (scope) { case ScriptVariableScope.Global: scriptObject = GetGlobalStoreForWrite(variable); break; case ScriptVariableScope.Local: { VariableContext variableContext = _currentLocalContext ?? throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the local variable `{variable}` in the current context"); if (variableContext.LocalObject != null) { scriptObject = variableContext.LocalObject; break; } if (_globalContexts.Count > 0) { scriptObject = _globalContexts.Peek().LocalObject; break; } throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the local variable `{variable}` in the current context"); } } if (scriptObject == null) { throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the variable `{variable}` in the current context"); } return scriptObject; } private IEnumerable<IScriptObject> GetStoreForRead(ScriptVariable variable) { ScriptVariableScope scope = variable.Scope; switch (scope) { case ScriptVariableScope.Global: { foreach (VariableContext visibleFunctionContext in GetVisibleFunctionContexts()) { foreach (IScriptObject item in GetStoresForRead(visibleFunctionContext)) { yield return item; } } for (int i = _globalContexts.Count - 1; i >= 0; i--) { VariableContext context = _globalContexts.Items[i]; foreach (IScriptObject item2 in GetStoresForRead(context)) { yield return item2; } } break; } case ScriptVariableScope.Local: { VariableContext currentLocalContext = _currentLocalContext ?? throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the local variable `{variable}` in the current context"); ScriptObject[] loopItems = currentLocalContext.Loops.Items; for (int i = currentLocalContext.Loops.Count - 1; i >= 0; i--) { yield return loopItems[i]; } if (currentLocalContext.LocalObject != null) { yield return currentLocalContext.LocalObject; break; } if (_globalContexts.Count > 0) { IScriptObject localObject = _globalContexts.Peek().LocalObject; if (localObject == null) { throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the local variable `{variable}` in the current context"); } yield return localObject; break; } throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the local variable `{variable}` in the current context"); } default: throw new NotImplementedException($"Variable scope `{scope}` is not implemented"); } } private IEnumerable<VariableContext> GetVisibleFunctionContexts() { for (int i = _functionContexts.Count - 1; i >= 0; i--) { VariableContext functionContext = _functionContexts.Items[i]; yield return functionContext; if (functionContext.HideParentFunctionScopes) { break; } } } private VariableContext GetCurrentGlobalVariableContext() { foreach (VariableContext visibleFunctionContext in GetVisibleFunctionContexts()) { if (visibleFunctionContext.LocalObject != null) { return visibleFunctionContext; } } return _globalContexts.Peek(); } private IScriptObject GetGlobalStoreForWrite(ScriptVariable variable) { string name = variable.Name; IScriptObject localObject = _globalContexts.Peek().LocalObject; if (localObject == null) { throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the global variable `{variable}` in the current context"); } IScriptObject scriptObject = null; foreach (VariableContext visibleFunctionContext in GetVisibleFunctionContexts()) { IScriptObject localObject2 = visibleFunctionContext.LocalObject; if (localObject2 != null) { if (scriptObject == null) { scriptObject = localObject2; } if (localObject2.Contains(name)) { CheckStoreCanWrite(variable, localObject2); return localObject2; } } } if (scriptObject == null) { CheckStoreCanWrite(variable, localObject); return localObject; } if (ContainsInGlobalContexts(name)) { CheckStoreCanWrite(variable, localObject); return localObject; } CheckStoreCanWrite(variable, scriptObject); return scriptObject; } private bo