Decompiled source of LabPresence v1.3.0
UserLibs/DiscordRPC.dll
Decompiled 2 months 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 2 months 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.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.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("6.5.7.0")] [assembly: AssemblyInformationalVersion("6.5.7+44876ae9de7b2855f1eb51d041c7b3ce5e1fe224")] [assembly: AssemblyProduct("Scriban")] [assembly: AssemblyTitle("Scriban")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/scriban/scriban")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: AssemblyVersion("6.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } 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; object result = await context.EvaluateAsync(Page).ConfigureAwait(continueOnCapturedContext: false); if (render && Page != null && 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 = default(LexerOptions); lexerOptions.Mode = ScriptMode.ScriptOnly; LexerOptions value = lexerOptions; LexerOptions? lexerOptions2 = value; return await Parse(expression, null, null, lexerOptions2).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; 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 = default(LexerOptions); lexerOptions.Mode = ScriptMode.ScriptOnly; LexerOptions value = lexerOptions; LexerOptions? lexerOptions2 = value; return await Parse(expression, null, null, lexerOptions2).EvaluateAsync(model, memberRenamer, memberFilter).ConfigureAwait(continueOnCapturedContext: false); } public async ValueTask<string> RenderAsync(TemplateContext context) { await EvaluateAndRenderAsync(context, render: true).ConfigureAwait(continueOnCapturedContext: false); string? result = context.Output.ToString(); if (context.Output is StringBuilderOutput stringBuilderOutput) { stringBuilderOutput.Builder.Length = 0; } return result; } 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; templateContext.MemberFilter = memberFilter; templateContext.PushGlobal(scriptObject); return await RenderAsync(templateContext).ConfigureAwait(continueOnCapturedContext: false); } private Template(ParserOptions? parserOptions, LexerOptions? lexerOptions, string sourceFilePath) { ParserOptions = parserOptions.GetValueOrDefault(); LexerOptions = lexerOptions.GetValueOrDefault(); 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 valueOrDefault = lexerOptions.GetValueOrDefault(); valueOrDefault.Lang = ScriptLang.Liquid; return Parse(text, sourceFilePath, parserOptions, valueOrDefault); } public static object Evaluate(string expression, TemplateContext context) { if (expression == null) { throw new ArgumentNullException("expression"); } LexerOptions lexerOptions = default(LexerOptions); lexerOptions.Mode = ScriptMode.ScriptOnly; LexerOptions value = lexerOptions; LexerOptions? lexerOptions2 = value; return Parse(expression, null, null, lexerOptions2).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 = default(LexerOptions); lexerOptions.Mode = ScriptMode.ScriptOnly; LexerOptions value = lexerOptions; LexerOptions? lexerOptions2 = value; return Parse(expression, null, null, lexerOptions2).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; 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? result = context.Output.ToString(); if (context.Output is StringBuilderOutput stringBuilderOutput) { stringBuilderOutput.Builder.Length = 0; } return result; } 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; templateContext.MemberFilter = memberFilter; templateContext.PushGlobal(scriptObject); return Render(templateContext); } public string ToText(ScriptPrinterOptions options = default(ScriptPrinterOptions)) { CheckErrors(); TextWriterOutput textWriterOutput = new TextWriterOutput(); new ScriptPrinter(textWriterOutput, options).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; object obj = context.Evaluate(Page); if (render && Page != null && 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, 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 VariableContext(IScriptObject localObject) { LocalObject = localObject; Loops = new FastStack<ScriptObject>(4); } } private enum VariableScope { Local, Loop } [CompilerGenerated] private sealed class <GetStoreForRead>d__306 : IEnumerable<IScriptObject>, IEnumerable, IEnumerator<IScriptObject>, IEnumerator, IDisposable { private int <>1__state; private IScriptObject <>2__current; private int <>l__initialThreadId; private ScriptVariable variable; public ScriptVariable <>3__variable; public TemplateContext <>4__this; private bool <isInLoop>5__2; private int <i>5__3; private VariableContext <context>5__4; private ScriptObject[] <loopItems>5__5; private int <j>5__6; IScriptObject IEnumerator<IScriptObject>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetStoreForRead>d__306(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <context>5__4 = null; <loopItems>5__5 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; TemplateContext templateContext = <>4__this; switch (num) { default: return false; case 0: { <>1__state = -1; ScriptVariableScope scope = variable.Scope; if (scope != 0) { if (scope == ScriptVariableScope.Local) { <loopItems>5__5 = templateContext._currentLocalContext.Loops.Items; <i>5__3 = templateContext._currentLocalContext.Loops.Count - 1; goto IL_01bf; } throw new NotImplementedException($"Variable scope `{scope}` is not implemented"); } <isInLoop>5__2 = templateContext.IsInLoop; <i>5__3 = templateContext._globalContexts.Count - 1; goto IL_0149; } case 1: <>1__state = -1; <j>5__6--; goto IL_00ff; case 2: <>1__state = -1; <context>5__4 = null; <i>5__3--; goto IL_0149; case 3: <>1__state = -1; <i>5__3--; goto IL_01bf; case 4: <>1__state = -1; break; case 5: { <>1__state = -1; break; } IL_0149: if (<i>5__3 < 0) { break; } <context>5__4 = templateContext._globalContexts.Items[<i>5__3]; if (<isInLoop>5__2) { int count = <context>5__4.Loops.Count; if (count > 0) { <loopItems>5__5 = <context>5__4.Loops.Items; <j>5__6 = count - 1; goto IL_00ff; } } goto IL_010f; IL_010f: <>2__current = <context>5__4.LocalObject; <>1__state = 2; return true; IL_01bf: if (<i>5__3 >= 0) { <>2__current = <loopItems>5__5[<i>5__3]; <>1__state = 3; return true; } if (templateContext._currentLocalContext.LocalObject != null) { <>2__current = templateContext._currentLocalContext.LocalObject; <>1__state = 4; return true; } if (templateContext._globalContexts.Count > 0) { <>2__current = templateContext._globalContexts.Peek().LocalObject; <>1__state = 5; return true; } throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the local variable `{variable}` in the current context"); IL_00ff: if (<j>5__6 >= 0) { <>2__current = <loopItems>5__5[<j>5__6]; <>1__state = 1; return true; } <loopItems>5__5 = null; goto IL_010f; } 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<IScriptObject> IEnumerable<IScriptObject>.GetEnumerator() { <GetStoreForRead>d__306 <GetStoreForRead>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <GetStoreForRead>d__ = this; } else { <GetStoreForRead>d__ = new <GetStoreForRead>d__306(0) { <>4__this = <>4__this }; } <GetStoreForRead>d__.variable = <>3__variable; return <GetStoreForRead>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<IScriptObject>)this).GetEnumerator(); } } private FastStack<ScriptObject> _availableStores; internal FastStack<ScriptBlockStatement> BlockDelegates; private FastStack<VariableContext> _globalContexts; 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> _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; 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) { string text; try { text = await TemplateLoader.LoadAsync(this, callerContext.Span, templatePath).ConfigureAwait(continueOnCapturedContext: false); } catch (Exception ex) when (!(ex is ScriptRuntimeException)) { throw new ScriptRuntimeException(callerContext.Span, "Unexpected exception while creating template from path `" + templatePath + "`", ex); } if (text == null) { throw new ScriptRuntimeException(callerContext.Span, "The result of including `" + templatePath + "` cannot be null"); } Template template = Template.Parse(text, templatePath, TemplateLoaderParserOptions, TemplateLoaderLexerOptions); if (template.HasErrors) { throw new ScriptParserRuntimeException(callerContext.Span, "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); } public async ValueTask<Template> GetOrCreateTemplateAsync(string templatePath, ScriptNode callerContext) { if (!CachedTemplates.TryGetValue(templatePath, out var 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); } 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) { 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 result = null; EnterRecursive(callerContext); string previousIndent = CurrentIndent; CurrentIndent = null; PushOutput(); PushLocal(); try { SetValue(ScriptVariable.Arguments, arguments, asReadOnly: true, force: true); IEnumerable<ScriptNamedArgument> enumerable = (callerContext as ScriptFunctionCall)?.Arguments.OfType<ScriptNamedArgument>(); if (enumerable != null) { foreach (ScriptNamedArgument item in enumerable) { string name = item.Name.Name; object value = await item.Value.EvaluateAsync(this).ConfigureAwait(continueOnCapturedContext: false); ScriptVariable variable = ScriptVariable.Create(name, ScriptVariableScope.Local); SetValue(variable, value, asReadOnly: false, force: true); } } if (previousIndent != null) { ResetPreviousNewLine(); } result = await template.RenderAsync(this).ConfigureAwait(continueOnCapturedContext: false); if (previousIndent != null) { ResetPreviousNewLine(); } } finally { PopLocal(); PopOutput(); CurrentIndent = previousIndent; ExitRecursive(callerContext); } return result; } public async ValueTask SetValueAsync(ScriptExpression target, object value) { if (target == null) { throw new ArgumentNullException("target"); } _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) { await Output.WriteAsync(CurrentIndent, 0, CurrentIndent.Length, CancellationToken).ConfigureAwait(continueOnCapturedContext: false); _previousTextWasNewLine = false; } await Output.WriteAsync(text, index, indexEnd - index, CancellationToken).ConfigureAwait(continueOnCapturedContext: false); break; } int length = newLineIndex - index; if (_previousTextWasNewLine && (IndentOnEmptyLines || (length != 0 && (length != 1 || text[index] != '\r')))) { await Output.WriteAsync(CurrentIndent, 0, CurrentIndent.Length, CancellationToken).ConfigureAwait(continueOnCapturedContext: false); _previousTextWasNewLine = false; } await Output.WriteAsync(text, index, length + 1, CancellationToken).ConfigureAwait(continueOnCapturedContext: false); index = newLineIndex + 1; _previousTextWasNewLine = true; } } else { if (count > 0) { _previousTextWasNewLine = text[startIndex + count - 1] == '\n'; } await Output.WriteAsync(text, startIndex, count, CancellationToken).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, slice.Index, slice.Length).ConfigureAwait(continueOnCapturedContext: false); return this; } public virtual async ValueTask<TemplateContext> WriteAsync(SourceSpan span, object textAsObject) { if (textAsObject != null) { string text = ObjectToString(textAsObject); await WriteAsync(text).ConfigureAwait(continueOnCapturedContext: false); } return this; } public async ValueTask<TemplateContext> WriteLineAsync() { await WriteAsync(NewLine).ConfigureAwait(continueOnCapturedContext: false); return this; } 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 = 0; ObjectRecursionLimit = 0; MemberRenamer = StandardMemberRenamer.Default; RegexTimeOut = TimeSpan.FromSeconds(10.0); TemplateLoaderParserOptions = default(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); _availableGlobalContexts = 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] = (object[])array[0]; 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) { 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.Import(objectToImport); } public void SetValue(ScriptExpression target, object value) { if (target == null) { throw new ArgumentNullException("target"); } _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) { string text = ObjectToString(textAsObject); Write(text); } return this; } 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, 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) { Output.Write(CurrentIndent, 0, CurrentIndent.Length); _previousTextWasNewLine = false; } Output.Write(text, num, num2 - num); break; } int num4 = num3 - num; if (_previousTextWasNewLine && (IndentOnEmptyLines || (num4 != 0 && (num4 != 1 || text[num] != '\r')))) { Output.Write(CurrentIndent, 0, CurrentIndent.Length); _previousTextWasNewLine = false; } Output.Write(text, num, num4 + 1); num = num3 + 1; _previousTextWasNewLine = true; } } else { if (count > 0) { _previousTextWasNewLine = text[startIndex + count - 1] == '\n'; } Output.Write(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 var 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(); } } 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 var accessor)) { if (type.IsArray) { return ArrayAccessor.Default; } if (target is IList) { return ListAccessor.Default; } return new TypedObjectAccessor(type, _keyComparer, MemberFilter, MemberRenamer); } return accessor; } public static ScriptObject GetDefaultBuiltinObject() { return new BuiltinFunctions(); } public void EnterRecursive(ScriptNode node) { try { RuntimeHelpers.EnsureSufficientExecutionStack(); } catch (InsufficientExecutionStackException) { throw new ScriptRuntimeException(node.Span, "Exceeding recursive depth limit, near to stack overflow"); } _callDepth++; if (RecursiveLimit != 0 && _callDepth > RecursiveLimit) { throw new ScriptRuntimeException(node.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, $"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 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 obj = null; try { if (targetExpression is IScriptVariablePath scriptVariablePath) { if (setter) { scriptVariablePath.SetValue(this, valueToSet); } else { obj = scriptVariablePath.GetValue(this); } } else { if (setter) { throw new ScriptRuntimeException(targetExpression.Span, "Unsupported target expression for assignment."); } obj = 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); } if (((_isFunctionCallDisabled && _getOrSetValueLevel > 1) || !_isFunctionCallDisabled) && ScriptFunctionCall.IsFunction(obj)) { obj = ScriptFunctionCall.Call(this, targetExpression, obj, _getOrSetValueLevel == 1, null); } return obj; } public IListAccessor GetListAccessor(object target) { Type type = target.GetType(); if (!_listAccessors.TryGetValue(type, out var value)) { value = GetListAccessorImpl(target, type); _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) { if (string.IsNullOrEmpty(templateName)) { throw new ScriptRuntimeException(callerContext.Span, "Include template name cannot be null or empty"); } return ConvertTemplateNameToPath(templateName, callerContext); } protected string ConvertTemplateNameToPath(string templateName, ScriptNode callerContext) { if (TemplateLoader == null) { throw new ScriptRuntimeException(callerContext.Span, "Unable to include <" + templateName + ">. No TemplateLoader registered in TemplateContext.TemplateLoader"); } string path; try { path = TemplateLoader.GetPath(this, callerContext.Span, templateName); } catch (Exception ex) when (!(ex is ScriptRuntimeException)) { throw new ScriptRuntimeException(callerContext.Span, "Unexpected exception while getting the path for the include name `" + templateName + "`", ex); } if (path == null) { throw new ScriptRuntimeException(callerContext.Span, "Include template path is null for `" + templateName); } return path; } public Template GetOrCreateTemplate(string templatePath, ScriptNode callerContext) { if (!CachedTemplates.TryGetValue(templatePath, out var value)) { value = CreateTemplate(templatePath, callerContext); CachedTemplates[templatePath] = value; } return value; } protected virtual Template CreateTemplate(string templatePath, ScriptNode callerContext) { string text; try { text = TemplateLoader.Load(this, callerContext.Span, templatePath); } catch (Exception ex) when (!(ex is ScriptRuntimeException)) { throw new ScriptRuntimeException(callerContext.Span, "Unexpected exception while creating template from path `" + templatePath + "`", ex); } if (text == null) { throw new ScriptRuntimeException(callerContext.Span, "The result of including `" + templatePath + "` cannot be null"); } Template template = Template.Parse(text, templatePath, TemplateLoaderParserOptions, TemplateLoaderLexerOptions); if (template.HasErrors) { throw new ScriptParserRuntimeException(callerContext.Span, "Error while parsing template `" + templatePath + "`", template.Messages); } CachedTemplates.Add(templatePath, template); return template; } public string RenderTemplate(Template template, ScriptArray arguments, ScriptNode callerContext) { string result = null; EnterRecursive(callerContext); string currentIndent = CurrentIndent; CurrentIndent = null; PushOutput(); PushLocal(); try { SetValue(ScriptVariable.Arguments, arguments, asReadOnly: true, force: true); IEnumerable<ScriptNamedArgument> enumerable = (callerContext as ScriptFunctionCall)?.Arguments.OfType<ScriptNamedArgument>(); if (enumerable != null) { foreach (ScriptNamedArgument item in enumerable) { string name = item.Name.Name; object value = item.Value.Evaluate(this); ScriptVariable variable = ScriptVariable.Create(name, ScriptVariableScope.Local); SetValue(variable, value, asReadOnly: false, force: true); } } if (currentIndent != null) { ResetPreviousNewLine(); } result = template.Render(this); if (currentIndent != null) { ResetPreviousNewLine(); } } finally { PopLocal(); PopOutput(); CurrentIndent = currentIndent; ExitRecursive(callerContext); } return result; } 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(); } public T ToObject<T>(SourceSpan span, object value) { return (T)ToObject(span, value, typeof(T)); } public virtual object ToObject(SourceSpan span, object value, Type destinationType) { if (destinationType == null) { throw new ArgumentNullException("destinationType"); } (bool IsNullable, Type DestinationType) tuple = GetNullableInfo(destinationType); bool item = tuple.IsNullable; destinationType = tuple.DestinationType; Type type = value?.GetType(); if (destinationType == type) { return value; } if (item && value == null) { return null; } 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)) { IScriptConvertibleFrom scriptConvertibleFrom = (IScriptConvertibleFrom)Activator.CreateInstance(destinationType); if (scriptConvertibleFrom.TryConvertFrom(this, span, value)) { return scriptConvertibleFrom; } } Type c = type; if (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 destinationType) { destinationType = destinationType ?? throw new ArgumentNullException("destinationType"); Type underlyingType = Nullable.GetUnderlyingType(destinationType); if ((object)underlyingType != null) { return (true, underlyingType); } return (false, destinationType); } } 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); return localObject; } public IScriptObject PopGlobal() { IScriptObject result = PopGlobalOnly(); PopLocal(); return result; } public void PushLocal() { PushVariableScope(VariableScope.Local); } public void PopLocal() { PopVariableScope(VariableScope.Local); } public void SetValue(ScriptVariable variable, object value, bool asReadOnly = false) { if (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 (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 (variable == null) { throw new ArgumentNullException("variable"); } GetStoreForWrite(variable).Remove(variable.Name); } public void SetReadOnly(ScriptVariable variable, bool isReadOnly = true) { if (variable == null) { throw new ArgumentNullException("variable"); } GetStoreForWrite(variable).SetReadOnly(variable.Name, isReadOnly); } public virtual void SetLoopVariable(ScriptVariable variable, object value) { if (variable == null) { throw new ArgumentNullException("variable"); } VariableContext obj = ((variable.Scope == ScriptVariableScope.Global) ? _globalContexts.Peek() : _currentLocalContext); 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 = (ScriptObject)variableContext.LocalObject; variableContext.LocalObject = null; _availableLocalContexts.Push(variableContext); _currentLocalContext = _localContexts.Peek(); return result; } public object GetValue(ScriptVariable variable) { if (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 (variable == null) { throw new ArgumentNullException("variable"); } object value = null; int count = _globalContexts.Count; VariableContext[] items = _globalContexts.Items; bool isInLoop = IsInLoop; for (int num = count - 1; num >= 0; num--) { VariableContext variableContext = items[num]; if (isInLoop) { int count2 = variableContext.Loops.Count; if (count2 > 0) { ScriptObject[] items2 = variableContext.Loops.Items; for (int num2 = count2 - 1; num2 >= 0; num2--) { if (items2[num2].TryGetValue(this, variable.Span, variable.Name, out value)) { return value; } } } } if (items[num].LocalObject.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 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: { string name = variable.Name; int num = _globalContexts.Count - 1; scriptObject = _globalContexts.Items[num].LocalObject; if (!scriptObject.CanWrite(name)) { string arg = ((scriptObject == BuiltinObject) ? "builtin " : string.Empty); throw new ScriptRuntimeException(variable.Span, $"Cannot set the {arg}readonly variable `{variable}`"); } break; } case ScriptVariableScope.Local: if (_currentLocalContext.LocalObject != null) { scriptObject = _currentLocalContext.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"); } return scriptObject; } [IteratorStateMachine(typeof(<GetStoreForRead>d__306))] private IEnumerable<IScriptObject> GetStoreForRead(ScriptVariable variable) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetStoreForRead>d__306(-2) { <>4__this = this, <>3__variable = variable }; } private void CheckVariableFound(ScriptVariable variable, bool found) { if (StrictVariables && !found && variable != ScriptVariable.Arguments) { throw new ScriptRuntimeException(variable.Span, $"The variable or function `{variable}` was not found"); } } private void PushVariableScope(VariableScope scope) { if (scope == VariableScope.Local) { ScriptObject locals = ((_availableStores.Count > 0) ? _availableStores.Pop() : new ScriptObject()); PushLocalContext(locals); return; } ScriptObject item = ((_availableStores.Count > 0) ? _availableStores.Pop() : new ScriptObject()); ScriptObject item2 = ((_availableStores.Count > 0) ? _availableStores.Pop() : new ScriptObject()); _globalContexts.Peek().Loops.Push(item2); _currentLocalContext.Loops.Push(item); } private void PopVariableScope(VariableScope scope) { if (scope == VariableScope.Local) { ScriptObject scriptObject = PopLocalContext(); scriptObject.Clear(); _availableStores.Push(scriptObject); return; } if (_currentLocalContext.Loops.Count == 0) { throw new InvalidOperationException("Invalid number of matching push/pop VariableScope."); } ScriptObject scriptObject2 = _globalContexts.Peek().Loops.Pop(); scriptObject2.Clear(); _availableStores.Push(scriptObject2); ScriptObject scriptObject3 = _currentLocalContext.Loops.Pop(); scriptObject3.Clear(); _availableStores.Push(scriptObject3); } } public class ScriptPrinter { private readonly IScriptOutput _output; private readonly bool _isScriptOnly; private bool _isInCode; private bool _expectSpace; private bool _expectEndOfStatement; private bool _previousHasSpace; private bool _hasEndOfStatement; private bool _hasComma; private FastStack<bool> _isWhileLoop; public readonly ScriptPrinterOptions Options; public bool PreviousHasSpace => _previousHasSpace; public bool IsInWhileLoop { get { if (_isWhileLoop.Count > 0) { return _isWhileLoop.Peek(); } return false; } } public ScriptPrinter(IScriptOutput output, ScriptPrinterOptions options = default(ScriptPrinterOptions)) { _isWhileLoop = new FastStack<bool>(4); Options = options; if (options.Mode != 0 && options.Mode != ScriptMode.ScriptOnly) { throw new ArgumentException($"The rendering mode `{options.Mode}` is not supported. Only `ScriptMode.Default` or `ScriptMode.ScriptOnly` are currently supported"); } _isScriptOnly = options.Mode == ScriptMode.ScriptOnly; _isInCode = _isScriptOnly || options.Mode == ScriptMode.FrontMatterOnly || options.Mode == ScriptMode.FrontMatterAndContent; _output = output; _hasEndOfStatement = true; } public ScriptPrinter Write(ScriptNode node) { if (node != null) { bool flag = false; if (node is ScriptLoopStatementBase) { _isWhileLoop.Push(node is ScriptWhileStatement); flag = true; } try { WriteBegin(node); if (node is IScriptTerminal) { _hasComma = false; } node.PrintTo(this); WriteEnd(node); } finally { if (flag) { _isWhileLoop.Pop(); } } } return this; } public ScriptPrinter Write(string text) { _previousHasSpace = text.Length > 0 && char.IsWhiteSpace(text[text.Length - 1]); _output.Write(text); return this; } public ScriptPrinter Write(ScriptStringSlice slice) { _previousHasSpace = slice.Length > 0 && char.IsWhiteSpace(slice[slice.Length - 1]); _output.Write(slice); return this; } public ScriptPrinter ExpectEos() { if (!_hasEndOfStatement) { _expectEndOfStatement = true; } return this; } public ScriptPrinter ExpectSpace() { _expectSpace = true; return this; } public ScriptPrinter WriteListWithCommas<T>(IList<T> list) where T : ScriptNode { if (list == null) { return this; } for (int i = 0; i < list.Count; i++) { T node = list[i]; if (i > 0 && !_hasComma) { Write(","); _hasComma = true; } Write(node); } return this; } public ScriptPrinter WriteEnterCode(int escape = 0) { Write("{"); for (int i = 0; i < escape; i++) { Write("%"); } Write("{"); _expectEndOfStatement = false; _expectSpace = false; _hasEndOfStatement = true; _isInCode = true; return this; } public ScriptPrinter WriteExitCode(int escape = 0) { Write("}"); for (int i = 0; i < escape; i++) { Write("%"); } Write("}"); _expectEndOfStatement = false; _expectSpace = false; _hasEndOfStatement = false; _isInCode = false; return this; } private void WriteBegin(ScriptNode node) { WriteTrivias(node, before: true); HandleEos(node); if (_hasEndOfStatement) { _hasEndOfStatement = false; _expectEndOfStatement = false; } if (node.CanHaveLeadingTrivia()) { if (_expectSpace && !_previousHasSpace) { Write(" "); } _expectSpace = false; } } private void WriteEnd(ScriptNode node) { WriteTrivias(node, before: false); if (node is ScriptPage && _isInCode && !_isScriptOnly) { WriteExitCode(); } } private static bool IsFrontMarker(ScriptNode node) { if (node is ScriptToken scriptToken) { return scriptToken.TokenType == TokenType.FrontMatterMarker; } return false; } private void HandleEos(ScriptNode node) { bool flag = IsFrontMarker(node); if ((node is ScriptStatement || flag) && !IsBlockOrPage(node) && _isInCode && _expectEndOfStatement) { if (!_hasEndOfStatement && !(node is ScriptEscapeStatement)) { Write(flag ? "\n" : "; "); } _expectEndOfStatement = false; _hasEndOfStatement = false; _hasComma = false; } } private static bool IsBlockOrPage(ScriptNode node) { if (!(node is ScriptBlockStatement)) { return node is ScriptPage; } return true; } private void WriteTrivias(ScriptNode node, bool before) { if (!(node is IScriptTerminal scriptTerminal)) { return; } ScriptTrivias trivias = scriptTerminal.Trivias; if (trivias == null) { return; } foreach (ScriptTrivia item in before ? trivias.Before : trivias.After) { item.Write(this); if (item.Type == ScriptTriviaType.NewLine || item.Type == ScriptTriviaType.SemiColon) { _hasEndOfStatement = true; if (item.Type == ScriptTriviaType.SemiColon) { _hasComma = false; } if (_expectSpace) { _expectSpace = false; } } if (item.Type == ScriptTriviaType.Comma) { _hasComma = true; } } } } public struct ScriptPrinterOptions { public ScriptMode Mode; } public class LiquidTemplateContext : TemplateContext { public LiquidTemplateContext() : base(new LiquidBuiltinsFunctions()) { base.Language = ScriptLang.Liquid; base.EnableBreakAndContinueAsReturnOutsideLoop = true; base.EnableRelaxedTargetAccess = true; base.TemplateLoaderLexerOptions = new LexerOptions { Lang = ScriptLang.Liquid }; base.TemplateLoaderParserOptions = new ParserOptions { LiquidFunctionsToScriban = true }; base.IsLiquid = true; } public override string GetTemplatePathFromName(string templateName, ScriptNode callerContext) { if (string.IsNullOrEmpty(templateName)) { return null; } return ConvertTemplateNameToPath(templateName, callerContext); } } } namespace Scriban.Syntax { internal class ScientificFunctionCallRewriter { [DebuggerDisplay("Count = {Count}, Current = {Index} : {Current}")] private class BinaryExpressionIterator : List<BinaryExpressionOrOperator> { public int Index { get; set; } public BinaryExpressionOrOperator Current { get { if (Index >= base.Count) { return default(BinaryExpressionOrOperator); } return base[Index]; } } public bool HasCurrent => Index < base.Count; public bool HasNext => Index + 1 < base.Count; public bool MoveNext() { Index++; return HasCurrent; } public BinaryExpressionOrOperator PeekNext() { return base[Index + 1]; } } [DebuggerDisplay("{ToDebuggerDisplay(),nq}")] private readonly struct BinaryExpressionOrOperator { public readonly ScriptExpression Expression; public readonly ScriptBinaryOperator Operator; public readonly ScriptToken OperatorToken; public readonly FunctionCallKind CallKind; public BinaryExpressionOrOperator(ScriptExpression expression, FunctionCallKind kind) { Expression = expression; Operator = ScriptBinaryOperator.None; OperatorToken = null; CallKind = kind; } public BinaryExpressionOrOperator(ScriptBinaryOperator @operator, ScriptToken operatorToken) { Expression = null; Operator = @operator; OperatorToken = operatorToken; CallKind = FunctionCallKind.None; } private string ToDebuggerDisplay() { object obj; if (Expression == null) { obj = OperatorToken?.ToString(); if (obj == null) { return $"`{Operator.ToText()}` - CallKind = {CallKind}"; } } else { obj = Expression.ToString(); } return (string)obj; } } private enum FunctionCallKind { None, Regular, Expression } private const int ImplicitFunctionCallPrecedence = 101; private static async ValueTask FlattenBinaryExpressionsAsync(TemplateContext context, ScriptExpression expression, List<BinaryExpressionOrOperator> expressions) { while (expression is ScriptBinaryExpression binaryExpression) { ScriptExpression expression2 = (ScriptExpression)binaryExpression.Left.Clone(); ScriptExpression right = (ScriptExpression)binaryExpression.Right.Clone(); ScriptToken token = (ScriptToken)(binaryExpression.OperatorToken?.Clone()); await FlattenBinaryExpressionsAsync(context, expression2, expressions).ConfigureAwait(continueOnCapturedContext: false); expressions.Add(new BinaryExpressionOrOperator(binaryExpression.Operator, token)); expression = right; } ScriptExpression expression3 = expression; expressions.Add(new BinaryExpressionOrOperator(expression3, await GetFunctionCallKindAsync(context, expression).ConfigureAwait(continueOnCapturedContext: false))); } private static async ValueTask<FunctionCallKind> GetFunctionCallKindAsync(TemplateContext context, ScriptExpression expression) { bool restoreStrictVariables = context.StrictVariables; context.StrictVariables = false; object result = null; try { result = await context.EvaluateAsync(expression, aliasReturnedFunction: true).ConfigureAwait(continueOnCapturedContext: false); } catch (ScriptRuntimeException) when (context.IgnoreExceptionsWhileRewritingScientific) { } finally { context.StrictVariables = restoreStrictVariables; } if (result is IScriptCustomFunction scriptCustomFunction && ((scriptCustomFunction.RequiredParameterCount != 0) ? scriptCustomFunction.RequiredParameterCount : ((scriptCustomFunction.ParameterCount > 0) ? 1 : 0)) > 0) { return (!scriptCustomFunction.IsParameterType<ScriptExpression>(0)) ? FunctionCallKind.Regular : FunctionCallKind.Expression; } return FunctionCallKind.None; } public static async ValueTask<ScriptExpression> RewriteAsync(TemplateContext context, ScriptBinaryExpression binaryExpression) { if (!HasImplicitBinaryExpression(binaryExpression)) { return binaryExpression; } BinaryExpressionIterator iterator = new BinaryExpressionIterator(); await FlattenBinaryExpressionsAsync(context, binaryExpression, iterator).ConfigureAwait(continueOnCapturedContext: false); return ParseBinaryExpressionTree(iterator, 0, isExpectingExpression: false); } public static ScriptExpression Rewrite(TemplateContext context, ScriptBinaryExpression binaryExpression) { if (!HasImplicitBinaryExpression(binaryExpression)) { return binaryExpression; } BinaryExpressionIterator binaryExpressionIterator = new BinaryExpressionIterator(); FlattenBinaryExpressions(context, binaryExpression, binaryExpressionIterator); return ParseBinaryExpressionTree(binaryExpressionIterator, 0, isExpectingExpression: false); } private static bool HasImplicitBinaryExpression(ScriptExpression expression) { if (expression is ScriptBinaryExpression scriptBinaryExpression) { if (scriptBinaryExpression.OperatorToken == null && scriptBinaryExpression.Operator == ScriptBinaryOperator.Multiply) { return true; } if (!HasImplicitBinaryExpression(scriptBinaryExpression.Left)) { return HasImplicitBinaryExpression(scriptBinaryExpression.Right); } return true; } return false; } private static void FlattenBinaryExpressions(TemplateContext context, ScriptExpression expression, List<BinaryExpressionOrOperator> expressions) { while (expression is ScriptBinaryExpression scriptBinaryExpression) { ScriptExpression expression2 = (ScriptExpression)scriptBinaryExpression.Left.Clone(); ScriptExpression obj = (ScriptExpression)scriptBinaryExpression.Right.Clone(); ScriptToken operatorToken = (ScriptToken)(scriptBinaryExpression.OperatorToken?.Clone()); FlattenBinaryExpressions(context, expression2, expressions); expressions.Add(new BinaryExpressionOrOperator(scriptBinaryExpression.Operator, operatorToken)); expression = obj; } expressions.Add(new BinaryExpressionOrOperator(expression, GetFunctionCallKind(context, expression))); } private static ScriptExpression ParseBinaryExpressionTree(BinaryExpressionIterator it, int precedence, bool isExpectingExpression) { ScriptExpression scriptExpression = null; while (it.HasCurrent) { BinaryExpressionOrOperator current = it.Current; ScriptExpression expression = current.Expression; if (expression == null) { int num = Parser.GetDefaultBinaryOperatorPrecedence(current.Operator); if (!isExpectingExpression && it.HasNext && it.PeekNext().CallKind != 0) { num = ((num < 101) ? num : 101); } if (num <= precedence) { break; } it.MoveNext(); ScriptBinaryExpression scriptBinaryExpression = new ScriptBinaryExpression { Left = scriptExpression, Operator = current.Operator, OperatorToken = (current.OperatorToken ?? ScriptToken.Star()), Span = { Start = scriptExpression.Span.Start } }; scriptBinaryExpression.Right = ParseBinaryExpressionTree(it, num, isExpectingExpression); scriptBinaryExpression.Span.End = scriptBinaryExpression.Right.Span.End; scriptExpression = scriptBinaryExpression; } else if (!isExpectingExpression && current.CallKind != 0) { ScriptFunctionCall obj = new ScriptFunctionCall { Target = expression, ExplicitCall = true, Span = { Start = expression.Span.Start } }; if (!it.MoveNext()) { throw new ScriptRuntimeException(expression.Span, "The function is expecting at least one argument"); } if (it.Current.Expression == null && (it.Current.Operator != ScriptBinaryOperator.Multiply || it.Current.OperatorToken != null)) { throw new ScriptRuntimeException(expression.Span, "The function expecting one argument cannot be followed by the operator " + (it.Current.OperatorToken?.ToString() ?? it.Current.Operator.ToText())); } if (!it.MoveNext()) { throw new ScriptRuntimeException(expression.Span, "The function is expecting at least one argument"); } ScriptExpression scriptExpression2 = ParseBinaryExpressionTree(it, 101, current.CallKind == FunctionCallKind.Expression); obj.Arguments.Add(scriptExpression2); obj.Span.End = scriptExpression2.Span.End; scriptExpression = obj; } else { scriptExpression = expression; it.MoveNext(); } } return scriptExpression; } private static FunctionCallKind GetFunctionCallKind(TemplateContext context, ScriptExpression expression) { bool strictVariables = context.StrictVariables; context.StrictVariables = false; object obj = null; try { obj = context.Evaluate(expression, aliasReturnedFunction: true); } catch (ScriptRuntimeException) when (context.IgnoreExceptionsWhileRewritingScientific) { } finally { context.StrictVariables = strictVariables; } if (obj is IScriptCustomFunction scriptCustomFunction && ((scriptCustomFunction.RequiredParameterCount != 0) ? scriptCustomFunction.RequiredParameterCount : ((scriptCustomFunction.ParameterCount > 0) ? 1 : 0)) > 0) { if (!scriptCustomFunction.IsParameterType<ScriptExpression>(0)) { return FunctionCallKind.Regular; } return FunctionCallKind.Expression; } return FunctionCallKind.None; } } [ScriptSyntax("array initializer", "[item1, item2,...]")] public class ScriptArrayInitializerExpression : ScriptExpression { private ScriptList<ScriptExpression> _values; private ScriptToken _openBracketToken; private ScriptToken _closeBracketToken; public override int ChildrenCount => 3; public ScriptToken OpenBracketToken { get { return _openBracketToken; } set { ParentToThis(ref _openBracketToken, value); } } public ScriptList<ScriptExpression> Values { get { return _values; } set { ParentToThis(ref _values, value); } } public ScriptToken CloseBracketToken { get { return _closeBracketToken; } set { ParentToThis(ref _closeBracketToken, value); } } public override async ValueTask<object> EvaluateAsync(TemplateContext context) { ScriptArray scriptArray = new ScriptArray(); foreach (ScriptExpression value in Values) { scriptArray.Add(await context.EvaluateAsync(value).ConfigureAwait(continueOnCapturedContext: false)); } return scriptArray; } protected override ScriptNode GetChildrenImpl(int index) { return index switch { 0 => OpenBracketToken, 1 => Values, 2 => CloseBracketToken, _ => null, }; } public override void Accept(ScriptVisitor visitor) { visitor.Visit(this); } public override TResult Accept<TResult>(ScriptVisitor<TResult> visitor) { return visitor.Visit(this); } public ScriptArrayInitializerExpression() { OpenBracketToken = ScriptToken.OpenBracket(); Values = new ScriptList<ScriptExpression>(); CloseBracketToken = ScriptToken.CloseBracket(); } public override object Evaluate(TemplateContext context) { ScriptArray scriptArray = new ScriptArray(); foreach (ScriptExpression value in Values) { object item = context.Evaluate(value); scriptArray.Add(item); } return scriptArray; } public override void PrintTo(ScriptPrinter printer) { printer.Write(OpenBracketToken); printer.WriteListWithCommas(Values); printer.Write(CloseBracketToken); } } [ScriptSyntax("assign expression", "<target_expression> = <value_expression>")] public class ScriptAssignExpression : ScriptExpression { private ScriptExpression _target; private ScriptToken _equalToken; private ScriptExpression _value; public override int ChildrenCount => 3; public ScriptExpression Target { get { return _target; } set { ParentToThis(ref _target, value); } } public ScriptToken EqualToken { get { return _equalToken; } set { ParentToThis(ref _equalToken, value); } } public ScriptExpression Value { get { return _value; } set { ParentToThis(ref _value, value); } } public override async ValueTask<object> EvaluateAsync(TemplateContext context) { object obj = ((EqualToken.TokenType != TokenType.Equal) ? (await GetValueToSetAsync(context).ConfigureAwait(continueOnCapturedContext: false)) : (await context.EvaluateAsync(Value).ConfigureAwait(continueOnCapturedContext: false))); object value = obj; await context.SetValueAsync(Target, value).ConfigureAwait(continueOnCapturedContext: false); return null; } private async ValueTask<object> GetValueToSetAsync(TemplateContext context) { object right = await context.EvaluateAsync(Value).ConfigureAwait(continueOnCapturedContext: false); object leftValue = await context.EvaluateAsync(Target).ConfigureAwait(continueOnCapturedContext: false); ScriptBinaryOperator op = EqualToken.TokenType switch { TokenType.PlusEqual => ScriptBinaryOperator.Add, TokenType.MinusEqual => ScriptBinaryOperator.Subtract, TokenType.AsteriskEqual => ScriptBinaryOperator.Multiply, TokenType.DivideEqual => ScriptBinaryOperator.Divide, TokenType.DoubleDivideEqual => ScriptBinaryOperator.DivideRound, TokenType.PercentEqual => ScriptBinaryOperator.Modulus, _ => throw new ScriptRuntimeException(context.CurrentSpan, $"Operator {EqualToken} is not a valid compound assignment operator"), }; return ScriptBinaryExpression.Evaluate(context, Span, op, leftValue, right); } protected override ScriptNode GetChildrenImpl(int index) { return index switch { 0 => Target, 1 => EqualToken, 2 => Value, _ => null, }; } public override void Accept(ScriptVisitor visitor) { visitor.Visit(this); } public override TResult Accept<TResult>(ScriptVisitor<TResult> visitor) { return visitor.Visit(this); } public ScriptAssignExpression() { EqualToken = ScriptToken.Equal(); } public override object Evaluate(TemplateContext context) { object value = ((EqualToken.TokenType == TokenType.Equal) ? context.Evaluate(Value) : GetValueToSet(context)); context.SetValue(Target, value); return null; } private object GetValueToSet(TemplateContext context) { object rightValue = context.Evaluate(Value); object leftValue = context.Evaluate(Target); ScriptBinaryOperator op = EqualToken.TokenType switch { TokenType.PlusEqual => ScriptBinaryOperator.Add, TokenType.MinusEqual => ScriptBinaryOperator.Subtract, TokenType.AsteriskEqual => ScriptBinaryOperator.Multiply, TokenType.DivideEqual => ScriptBinaryOperator.Divide, TokenType.DoubleDivideEqual => ScriptBinaryOperator.DivideRound, TokenType.PercentEqual => ScriptBinaryOperator.Modulus, _ => throw new ScriptRuntimeException(context.CurrentSpan, $"Operator {EqualToken} is not a valid compound assignment operator"), }; return ScriptBinaryExpression.Evaluate(context, Span, op, leftValue, rightValue); } public override bool CanHaveLeadingTrivia() { return false; } public override void PrintTo(ScriptPrinter printer) { printer.Write(Target); printer.Write(EqualToken); printer.Write(Value); } } [ScriptSyntax("binary expression", "<expression> operator <expression>")] public class ScriptBinaryExpression : ScriptExpression { [CompilerGenerated] private sealed class <RangeExclude>d__32 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private int <>l__initialThreadId; private long left; public long <>3__left; private long right; public long <>3__right; private long <i>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RangeExclude>d__32(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; if (left < right) { <i>5__2 = left; goto IL_0070; } <i>5__2 = left; goto IL_00c0; case 1: <>1__state = -1; <i>5__2++; goto IL_0070; case 2: { <>1__state = -1; <i>5__2--; goto IL_00c0; } IL_00c0: if (<i>5__2 > right) { <>2__current = FitToBestInteger(<i>5__2); <>1__state = 2; return true; } break; IL_0070: if (<i>5__2 < right) { <>2__current = FitToBestInteger(<i>5__2); <>1__state = 1; return true; } break; } 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<object> IEnumerable<object>.GetEnumerator() { <RangeExclude>d__32 <RangeExclude>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <RangeExclude>d__ = this; } else { <RangeExclude>d__ = new <RangeExclude>d__32(0); } <RangeExclude>d__.left = <>3__left; <RangeExclude>d__.right = <>3__right; return <RangeExclude>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<object>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <RangeExclude>d__34 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private int <>l__initialThreadId; private BigInteger left; public BigInteger <>3__left; private BigInteger right; public BigInteger <>3__right; private BigInteger <i>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RangeExclude>d__34(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; if (left < right) { <i>5__2 = left; goto IL_0077; } <i>5__2 = left; goto IL_00ce; case 1: <>1__state = -1; <i>5__2++; goto IL_0077; case 2: { <>1__state = -1; <i>5__2--; goto IL_00ce; } IL_00ce: if (<i>5__2 > right) { <>2__current = FitToBestInteger(<i>5__2); <>1__state = 2; return true; } break; IL_0077: if (<i>5__2 < right) { <>2__current = FitToBestInteger(<i>5__2); <>1__state = 1; return true; } break; } 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<object> IEnumerable<object>.GetEnumerator() { <RangeExclude>d__34 <RangeExclude>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <RangeExclude>d__ = this; } else { <RangeExclude>d__ = new <RangeExclude>d__34(0); } <RangeExclude>d__.left = <>3__left; <RangeExclude>d__.right = <>3__right; return <RangeExclude>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<object>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <RangeInclude>d__31 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private int <>l__initialThreadId; private long left; public long <>3__left; private long right; public long <>3__right; private long <i>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RangeInclude>d__31(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; if (left < right) { <i>5__2 = left; goto IL_0070; } <i>5__2 = left; goto IL_00c0; case 1: <>1__state = -1; <i>5__2++; goto IL_0070; case 2: { <>1__state = -1; <i>5__2--; goto IL_00c0; } IL_00c0: if (<i>5__2 >= right) { <>2__current = FitToBestInteger(<i>5__2); <>1__state = 2; return true; } break; IL_0070: if (<i>5__2 <= right) { <>2__current = FitToBestInteger(<i>5__2); <>1__state = 1; return true; } break; } 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<object> IEnumerable<object>.GetEnumerator() { <RangeInclude>d__31 <RangeInclude>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <RangeInclude>d__ = this; } else { <RangeInclude>d__ = new <RangeInclude>d__31(0); } <RangeInclude>d__.left = <>3__left; <RangeInclude>d__.right = <>3__right; return <RangeInclude>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<object>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <RangeInclude>d__33 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private int <>l__initialThreadId; private BigInteger left; public BigInteger <>3__left; private BigInteger right; public BigInteger <>3__right; private BigInteger <i>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RangeInclude>d__33(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; if (left < right) { <i>5__2 = left; goto IL_0077; } <i>5__2 = left; goto IL_00ce; case 1: <>1__state = -1; <i>5__2++; goto IL_0077; case 2: { <>1__state = -1; <i>5__2--; goto IL_00ce; } IL_00ce: if (<i>5__2 >= right
Mods/LabPresence.dll
Decompiled 2 months 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.Tasks; using BoneLib; using BoneLib.BoneMenu; using BoneLib.Notifications; using DiscordRPC; using DiscordRPC.Events; using DiscordRPC.Exceptions; using DiscordRPC.Helper; using DiscordRPC.IO; using DiscordRPC.Logging; using DiscordRPC.Message; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.Interaction; using Il2CppSLZ.Marrow.Pool; using Il2CppSLZ.Marrow.SceneStreaming; using Il2CppSLZ.Marrow.Utilities; using Il2CppSLZ.Marrow.Warehouse; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using LabFusion.Data; using LabFusion.Entities; using LabFusion.Network; using LabFusion.Player; using LabFusion.SDK.Gamemodes; using LabFusion.SDK.Metadata; using LabFusion.UI.Popups; using LabFusion.Utilities; using LabPresence; using LabPresence.Config; using LabPresence.Managers; using LabPresence.Plugins; using LabPresence.Plugins.Default; using LabPresence.Utilities; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Scriban; using Scriban.Parsing; using Scriban.Runtime; using Semver; using Tomlet.Attributes; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Core), "LabPresence", "1.3.0", "HAHOOS", "https://thunderstore.io/c/bonelab/p/HAHOOS/LabPresence/")] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: MelonPriority(-1000)] [assembly: MelonOptionalDependencies(new string[] { "LabFusion", "DiscordRPC", "Scriban" })] [assembly: MelonPlatform(/*Could not decode attribute arguments.*/)] [assembly: AssemblyTitle("Adds Discord Rich Presence support to BONELAB")] [assembly: AssemblyDescription("Adds Discord Rich Presence support to BONELAB")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("HAHOOS")] [assembly: AssemblyProduct("LabPresence")] [assembly: AssemblyFileVersion("1.3.0")] [assembly: AssemblyInformationalVersion("1.3.0")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("1.3.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LabPresence { public class Core : MelonMod { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static OnJoinEvent <>9__31_4; public static OnJoinRequestedEvent <>9__31_5; public static OnSpectateEvent <>9__31_6; public static Action <>9__31_7; public static Action <>9__31_8; public static Action <>9__31_9; public static Action <>9__31_10; public static Action<LevelCrate> <>9__31_11; public static Action<LevelCrate> <>9__31_12; public static Action<LevelCrate> <>9__31_13; public static Func<ScriptObject> <>9__35_0; internal void <OnInitializeMelon>b__31_4(object _, JoinMessage e) { Overwrites.OnJoin.Run(e); } internal void <OnInitializeMelon>b__31_5(object _, JoinRequestMessage e) { Overwrites.OnJoinRequested.Run(e); } internal void <OnInitializeMelon>b__31_6(object _, SpectateMessage e) { Overwrites.OnSpectate.Run(e); } internal void <OnInitializeMelon>b__31_7() { RichPresenceManager.TrySetRichPresence(Config.LevelLoaded, (ActivityType)0); } internal void <OnInitializeMelon>b__31_8() { RichPresenceManager.TrySetRichPresence(Config.LevelLoading, (ActivityType)0); } internal void <OnInitializeMelon>b__31_9() { RichPresenceManager.TrySetRichPresence(Config.AssetWarehouseLoaded, (ActivityType)0); } internal void <OnInitializeMelon>b__31_10() { RichPresenceManager.TrySetRichPresence(Config.PreGameStarted, (ActivityType)0); } internal void <OnInitializeMelon>b__31_11(LevelCrate _) { if (!FirstLevelLoad) { FirstLevelLoad = true; Thunderstore.BL_SendNotification(); } LevelLaunch = DateTime.Now; ConfigureTimestamp(autoUpdate: true); Overwrites.OnLevelLoaded.Run(); } internal void <OnInitializeMelon>b__31_12(LevelCrate _) { Overwrites.OnLevelLoading.Run(); } internal void <OnInitializeMelon>b__31_13(LevelCrate _) { Overwrites.OnLevelUnloaded.Run(); } internal ScriptObject <AddDefaultPlaceholders>b__35_0() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown ScriptObject val = new ScriptObject((IEqualityComparer<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.0"; private const string ClientID = "1338522973421965382"; private static float _elapsedSecondsDateCheck = 0f; public static DiscordRpcClient Client { get; private set; } internal static Instance Logger { get; private set; } internal static MelonPreferences_ReflectiveCategory Category { get; private set; } internal static DefaultConfig Config { get { MelonPreferences_ReflectiveCategory category = Category; return (category != null) ? category.GetValue<DefaultConfig>() : null; } } public static DateTimeOffset GameLaunch { get; } = DateTimeOffset.Now; public static DateTimeOffset LevelLaunch { get; private set; } = DateTimeOffset.Now; public static Thunderstore Thunderstore { get; private set; } public static bool FirstLevelLoad { get; private set; } private static int LastDay { get; set; } = -1; public override void OnInitializeMelon() { //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Expected O, but got Unknown //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Expected O, but got Unknown //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Expected O, but got Unknown //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Expected O, but got Unknown if (HelperMethods.IsAndroid()) { ((MelonBase)this).LoggerInstance.Error("This mod is not supported as it is unlikely for it to actually work."); ((MelonBase)this).Unregister("Unsupported platform", false); return; } Logger = ((MelonBase)this).LoggerInstance; ((MelonBase)this).LoggerInstance.Msg("Creating preferences"); DirectoryInfo directoryInfo = Directory.CreateDirectory(Path.Combine(MelonEnvironment.UserDataDirectory, "LabPresence")); Category = MelonPreferences.CreateCategory<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.0 A BONELAB Mod"); Thunderstore.BL_FetchPackage("LabPresence", "HAHOOS", "1.3.0", ((MelonBase)this).LoggerInstance); ((MelonBase)this).LoggerInstance.Msg("Adding placeholders"); AddDefaultPlaceholders(); ((MelonBase)this).LoggerInstance.Msg("Initializing..."); Client = new DiscordRpcClient("1338522973421965382", -1, (ILogger)null, false, (INamedPipeClient)null) { Logger = (ILogger)(object)new Logger(((MelonBase)this).LoggerInstance, Config.RPCLogLevel, "RPC") }; Client.OnReady += (OnReadyEvent)delegate(object _, ReadyMessage e) { ((MelonBase)this).LoggerInstance.Msg("User @" + e.User.Username + " is ready"); ((MelonBase)this).LoggerInstance.Msg("Registering URI Scheme"); RegisterURIScheme(); Client.SynchronizeState(); }; Client.OnConnectionEstablished += (OnConnectionEstablishedEvent)delegate { ((MelonBase)this).LoggerInstance.Msg("Successfully established connection"); }; Client.OnConnectionFailed += (OnConnectionFailedEvent)delegate(object _, ConnectionFailedMessage e) { ((MelonBase)this).LoggerInstance.Error($"Failed to establish connection with pipe {e.FailedPipe}"); }; Client.OnError += (OnErrorEvent)delegate(object _, ErrorMessage e) { ((MelonBase)this).LoggerInstance.Error("An unexpected error has occurred when sending a message, error: " + e.Message); }; DiscordRpcClient client = Client; object obj = <>c.<>9__31_4; if (obj == null) { OnJoinEvent val = delegate(object _, JoinMessage e) { Overwrites.OnJoin.Run(e); }; <>c.<>9__31_4 = val; obj = (object)val; } client.OnJoin += (OnJoinEvent)obj; DiscordRpcClient client2 = Client; object obj2 = <>c.<>9__31_5; if (obj2 == null) { OnJoinRequestedEvent val2 = delegate(object _, JoinRequestMessage e) { Overwrites.OnJoinRequested.Run(e); }; <>c.<>9__31_5 = val2; obj2 = (object)val2; } client2.OnJoinRequested += (OnJoinRequestedEvent)obj2; DiscordRpcClient client3 = Client; object obj3 = <>c.<>9__31_6; if (obj3 == null) { OnSpectateEvent val3 = delegate(object _, SpectateMessage e) { Overwrites.OnSpectate.Run(e); }; <>c.<>9__31_6 = val3; obj3 = (object)val3; } client3.OnSpectate += (OnSpectateEvent)obj3; Client.SkipIdenticalPresence = true; Client.SetSubscription((EventType)7); Client.Initialize(); Overwrites.OnLevelLoaded.SetDefault(delegate { RichPresenceManager.TrySetRichPresence(Config.LevelLoaded, (ActivityType)0); }); Overwrites.OnLevelLoading.SetDefault(delegate { RichPresenceManager.TrySetRichPresence(Config.LevelLoading, (ActivityType)0); }); Overwrites.OnAssetWarehouseLoaded.SetDefault(delegate { RichPresenceManager.TrySetRichPresence(Config.AssetWarehouseLoaded, (ActivityType)0); }); Overwrites.OnPreGame.SetDefault(delegate { RichPresenceManager.TrySetRichPresence(Config.PreGameStarted, (ActivityType)0); }); try { if (Fusion.HasFusion) { PluginsManager.Register<FusionPlugin>(); } } catch (Exception value) { Logger.Error($"An unexpected error has occurred while attempting to register the Fusion Plugin, exception:\n{value}"); } 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<LevelCrate>)Delegate.Combine(LevelHooks.OnLevelUnloaded, (Action<LevelCrate>)delegate { Overwrites.OnLevelUnloaded.Run(); }); AssetWarehouse.OnReady(Action.op_Implicit((Action)Overwrites.OnAssetWarehouseLoaded.Run)); Overwrites.OnPreGame.Run(); ((MelonBase)this).LoggerInstance.Msg("Creating BoneMenu"); MenuManager.Init(); ((MelonBase)this).LoggerInstance.Msg("Initialized."); } private void RegisterURIScheme() { try { Client.RegisterUriScheme((string)null, (string)null); } catch (Exception value) { ((MelonBase)this).LoggerInstance.Error($"An unexpected error has occurred while registering URI scheme, exception:\n{value}"); } } public static void ConfigureTimestamp(bool autoUpdate = false) { if (Config.TimeMode == TimeMode.CurrentTime) { RichPresenceManager.SetTimestampToCurrentTime(autoUpdate); } else if (Config.TimeMode == TimeMode.GameSession) { RichPresenceManager.SetTimestamp((ulong)GameLaunch.ToUnixTimeMilliseconds(), null, autoUpdate); } else if (Config.TimeMode == TimeMode.Level) { RichPresenceManager.SetTimestamp((ulong)LevelLaunch.ToUnixTimeMilliseconds(), null, autoUpdate); } } public static SpawnableCrate GetInHand(Handedness handType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 Hand val = (((int)handType == 1) ? Player.LeftHand : Player.RightHand); object result; if (val == null) { result = null; } else { HandReciever attachedReceiver = val.AttachedReceiver; if (attachedReceiver == null) { result = null; } else { IGrippable host = attachedReceiver.Host; if (host == null) { result = null; } else { Grip grip = host.GetGrip(); if (grip == null) { result = null; } else { MarrowEntity marrowEntity = grip._marrowEntity; if (marrowEntity == null) { result = null; } else { Poolee poolee = marrowEntity._poolee; result = ((poolee != null) ? poolee.SpawnableCrate : null); } } } } } return (SpawnableCrate)result; } private static void AddDefaultPlaceholders() { PlaceholderManager.RegisterPlaceholder("default", delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown ScriptObject val = new ScriptObject((IEqualityComparer<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(); LevelHooks.OnUpdate(); _elapsedSecondsDateCheck += Time.deltaTime; if (LastDay == -1) { LastDay = DateTime.Now.Day; } if (RichPresenceManager.CurrentConfig == null || !(_elapsedSecondsDateCheck >= 5f)) { return; } _elapsedSecondsDateCheck = 0f; if (Config.TimeMode == TimeMode.CurrentTime) { DateTime now = DateTime.Now; if (now.Day != LastDay) { LastDay = now.Day; RichPresenceManager.SetTimestampToCurrentTime(autoUpdate: true); } } } public override void OnApplicationQuit() { DiscordRpcClient client = Client; if (client != null) { client.ClearPresence(); } DiscordRpcClient client2 = Client; if (client2 != null) { client2.Dispose(); } } } public class Logger : ILogger { public LogLevel Level { get; set; } public string Prefix { get; internal set; } private Instance LoggerInstance { get; } public Logger(Instance logger, string prefix) { LoggerInstance = logger; Prefix = prefix; } public Logger(Instance logger, LogLevel level, string prefix) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) LoggerInstance = logger; Level = level; Prefix = prefix; } public void Error(string message, params object[] args) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)Level <= 4) { LoggerInstance.Error("[" + Prefix + "] [ERROR] " + message, args); } } public void Info(string message, params object[] args) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)Level <= 2) { LoggerInstance.Msg("[" + Prefix + "] [INFO] " + message, args); } } public void Trace(string message, params object[] args) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)Level <= 1) { LoggerInstance.Msg("[" + Prefix + "] [TRACE] " + message, args); } } public void Warning(string message, params object[] args) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)Level <= 3) { LoggerInstance.Warning("[" + Prefix + "] [WARN] " + message, args); } } } } namespace LabPresence.Utilities { public static class Fps { private static float updateTime; public static int FramesPerSecond { get; private set; } internal static int FramesRendered { get; private set; } internal static DateTime LastTime { get; private set; } internal static void OnUpdate() { FramesRendered++; updateTime += Time.deltaTime; if (updateTime >= 1f) { updateTime = 0f; FramesPerSecond = FramesRendered; FramesRendered = 0; LastTime = DateTime.Now; } } } public static class ImageConversion { private delegate IntPtr TextureOnlyDelegate(IntPtr tex); private delegate IntPtr TextureAndQualityDelegate(IntPtr tex, int quality); private delegate IntPtr TextureAndFlagDelegate(IntPtr tex, EXRFlags flags); private delegate bool LoadImageDelegate(IntPtr tex, IntPtr data, bool markNonReadable); private const string NullTextureError = "The texture cannot be null."; private static readonly TextureAndFlagDelegate EncodeToEXRDelegateField = IL2CPP.ResolveICall<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_003e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)tex == (Object)null) { throw new ArgumentNullException("tex", "The texture cannot be null."); } if (EncodeToEXRDelegateField == null) { throw new InvalidOperationException("The EncodeToEXRDelegateField cannot be null."); } IntPtr intPtr = EncodeToEXRDelegateField(IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)tex), flags); if (intPtr != IntPtr.Zero) { return new Il2CppStructArray<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_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown Texture2D val = new Texture2D(2, 2); val.LoadImage(Il2CppStructArray<byte>.op_Implicit(bytes), markNonReadable: false); ((Object)val).name = name; ((Object)val).hideFlags = (HideFlags)32; return val; } } public static class LevelHooks { public static LevelCrate CurrentLevel { get { StreamSession session = SceneStreamer.Session; return ((session != null) ? session.Level : null) ?? null; } } public static Action<LevelCrate> OnLevelLoaded { get; set; } public static Action<LevelCrate> OnLevelLoading { get; set; } public static Action<LevelCrate> OnLevelUnloaded { get; set; } internal static LastStatus LastStatus { get; private set; } internal static void OnUpdate() { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Invalid comparison between Unknown and I4 //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Invalid comparison between Unknown and I4 //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Invalid comparison between Unknown and I4 //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Invalid comparison between Unknown and I4 if (!MarrowGame.IsInitialized || SceneStreamer.Session == null || (Object)(object)SceneStreamer.Session.Level == (Object)null) { return; } LastStatus lastStatus = LastStatus; if (lastStatus != null && lastStatus.UpToDate(SceneStreamer.Session.Level, SceneStreamer.Session.Status)) { return; } try { if ((int)SceneStreamer.Session.Status == 2) { OnLevelLoaded?.Invoke(SceneStreamer.Session.Level); } else if ((int)SceneStreamer.Session.Status == 1) { OnLevelLoading?.Invoke(SceneStreamer.Session.Level); } if ((int)SceneStreamer.Session.Status != 2) { LastStatus lastStatus2 = LastStatus; if (lastStatus2 != null && (int)lastStatus2.Status == 2) { OnLevelUnloaded?.Invoke(LastStatus?.Level); } } } finally { LastStatus = new LastStatus(SceneStreamer.Session.Status, SceneStreamer.Session.Level); } } } internal class LastStatus { internal StreamStatus Status { get; set; } internal LevelCrate Level { get; set; } public LastStatus(StreamStatus status, LevelCrate level) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Status = status; Level = level; base..ctor(); } internal bool UpToDate(string barcode, StreamStatus status) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) LevelCrate level = Level; object obj; if (level == null) { obj = null; } else { Barcode barcode2 = ((Scannable)level).Barcode; obj = ((barcode2 != null) ? barcode2.ID : null); } return (string?)obj == barcode && Status == status; } internal bool UpToDate(LevelCrate level, StreamStatus status) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) LevelCrate level2 = Level; object obj; if (level2 == null) { obj = null; } else { Barcode barcode = ((Scannable)level2).Barcode; obj = ((barcode != null) ? barcode.ID : null); } object obj2; if (level == null) { obj2 = null; } else { Barcode barcode2 = ((Scannable)level).Barcode; obj2 = ((barcode2 != null) ? barcode2.ID : null); } return (string?)obj == (string?)obj2 && Status == status; } } public class ScribanCrate { public enum CrateType { Spawnable, Avatar, Level, VFX } public CrateType Type { get; } public string Barcode { get; } public string Title { get; } public string Description { get; } public bool Redacted { get; } public bool Unlockable { get; } public ScriptArray<string> Tags { get; } public ScriptArray<ScribanBoneTag> BoneTags { get; } public ScribanPallet Pallet { get; } public ScribanCrate(Crate crate, ScribanPallet pallet = null) { Title = ((Scannable)crate).Title; Description = ((Scannable)crate).Description; Redacted = ((Scannable)crate).Redacted; Barcode = ((Scannable)crate).Barcode.ID; Unlockable = ((Scannable)crate).Unlockable; if (crate.Tags == null) { Tags = new ScriptArray<string>(); } else { ScriptArray<string> val = new ScriptArray<string>(); Enumerator<string> enumerator = crate.Tags.GetEnumerator(); while (enumerator.MoveNext()) { string current = enumerator.Current; val.Add(current); } Tags = val; } Pallet = pallet ?? new ScribanPallet(crate.Pallet); if (crate.BoneTags == null || crate.BoneTags.Tags == null) { BoneTags = new ScriptArray<ScribanBoneTag>(); } else { List<ScribanBoneTag> scribanBoneTags = new List<ScribanBoneTag>(); crate.BoneTags.Tags.ForEach(Action<BoneTagReference>.op_Implicit((Action<BoneTagReference>)delegate(BoneTagReference c) { scribanBoneTags.Add(new ScribanBoneTag(((DataCardReference<BoneTag>)(object)c).DataCard, Pallet)); })); ScriptArray<ScribanBoneTag> val2 = new ScriptArray<ScribanBoneTag>(); foreach (ScribanBoneTag item in scribanBoneTags) { val2.Add(item); } BoneTags = val2; } if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "SpawnableCrate") { Type = CrateType.Spawnable; return; } if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "AvatarCrate") { Type = CrateType.Avatar; return; } if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "LevelCrate") { Type = CrateType.Level; return; } if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "VFXCrate") { Type = CrateType.VFX; return; } throw new ArgumentOutOfRangeException("Crate type " + ((MemberInfo)((Object)crate).GetIl2CppType()).Name + " is not supported."); } } public class ScribanPallet { public string Title { get; } public string Description { get; } public string Author { get; } public string Barcode { get; } public string[] Tags { get; } public bool Redacted { get; } public bool Unlockable { get; } public string Version { get; } public string SDKVersion { get; } public ScriptArray<ScribanCrate> Crates { get; } public ScriptArray<ScribanChangeLog> ChangeLogs { get; } public ScriptArray<ScribanDataCard> 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; if (pallet.Crates == null) { Crates = new ScriptArray<ScribanCrate>(); } else { List<ScribanCrate> scribanCrates = new List<ScribanCrate>(); pallet.Crates.ForEach(Action<Crate>.op_Implicit((Action<Crate>)delegate(Crate c) { scribanCrates.Add(new ScribanCrate(c, this)); })); ScriptArray<ScribanCrate> val = new ScriptArray<ScribanCrate>(); foreach (ScribanCrate item in scribanCrates) { val.Add(item); } Crates = val; } Author = pallet.Author; Description = ((Scannable)pallet).Description; SDKVersion = pallet.SDKVersion; if (pallet.ChangeLogs == null) { ChangeLogs = new ScriptArray<ScribanChangeLog>(); } else { List<ScribanChangeLog> list = new List<ScribanChangeLog>(); Enumerator<ChangeLog> enumerator2 = pallet.ChangeLogs.GetEnumerator(); while (enumerator2.MoveNext()) { ChangeLog current2 = enumerator2.Current; list.Add(new ScribanChangeLog(current2)); } ScriptArray<ScribanChangeLog> val2 = new ScriptArray<ScribanChangeLog>(); foreach (ScribanChangeLog item2 in list) { val2.Add(item2); } ChangeLogs = val2; } if (pallet.DataCards == null) { DataCards = new ScriptArray<ScribanDataCard>(); } else { List<ScribanDataCard> scribanDataCards = new List<ScribanDataCard>(); pallet.DataCards.ForEach(Action<DataCard>.op_Implicit((Action<DataCard>)delegate(DataCard c) { scribanDataCards.Add(new ScribanDataCard(c, this)); })); ScriptArray<ScribanDataCard> val3 = new ScriptArray<ScribanDataCard>(); foreach (ScribanDataCard item3 in scribanDataCards) { val3.Add(item3); } DataCards = val3; } if (pallet.PalletDependencies == null) { Dependencies = Array.Empty<string>(); return; } List<string> dependencies = new List<string>(); pallet.PalletDependencies.ForEach(Action<PalletReference>.op_Implicit((Action<PalletReference>)delegate(PalletReference p) { dependencies.Add(((ScannableReference)p).Barcode.ID); })); Dependencies = dependencies.ToArray(); } } public class ScribanChangeLog { public string Title { get; } = changelog.title; public string Version { get; } = changelog.version; public string Text { get; } = changelog.text; public ScribanChangeLog(ChangeLog changelog) { } } public class ScribanDataCard { public string Title { get; } = ((Scannable)dataCard).Title; public string Description { get; } = ((Scannable)dataCard).Description; public string Barcode { get; } = ((Scannable)dataCard).Barcode.ID; public bool Redacted { get; } = ((Scannable)dataCard).Redacted; public bool Unlockable { get; } = ((Scannable)dataCard).Unlockable; public ScribanPallet Pallet { get; } = pallet ?? new ScribanPallet(dataCard.Pallet); public ScribanDataCard(DataCard dataCard, ScribanPallet pallet = null) { } } public class ScribanBoneTag { public string Title { get; } = ((Scannable)boneTag).Title; public string Description { get; } = ((Scannable)boneTag).Description; public string Barcode { get; } = ((Scannable)boneTag).Barcode.ID; public bool Redacted { get; } = ((Scannable)boneTag).Redacted; public bool Unlockable { get; } = ((Scannable)boneTag).Unlockable; public ScribanPallet Pallet { get; } = pallet ?? new ScribanPallet(((DataCard)boneTag).Pallet); public ScribanBoneTag(BoneTag boneTag, ScribanPallet pallet = null) { } } public class ScribanAmmo : ScriptObject { public static int Light => GetAmmo("light"); public static int Medium => GetAmmo("medium"); public static int Heavy => GetAmmo("heavy"); public static int GetAmmo(string type) { AmmoInventory instance = AmmoInventory.Instance; return (instance != null) ? instance._groupCounts[type] : 0; } } public class ScribanPlayer : ScriptObject { public static ScribanCrate Avatar { get { RigManager rigManager = Player.RigManager; object result; if (!((Object)(object)((rigManager == null) ? null : ((CrateReferenceT<AvatarCrate>)(object)rigManager.AvatarCrate)?.Crate) != (Object)null)) { result = null; } else { RigManager rigManager2 = Player.RigManager; result = new ScribanCrate((Crate)(object)((rigManager2 == null) ? null : ((CrateReferenceT<AvatarCrate>)(object)rigManager2.AvatarCrate)?.Crate)); } return (ScribanCrate)result; } } public static ScribanCrate LeftHand => ((Object)(object)Core.GetInHand((Handedness)1) != (Object)null) ? new ScribanCrate((Crate)(object)Core.GetInHand((Handedness)1)) : null; public static ScribanCrate RightHand => ((Object)(object)Core.GetInHand((Handedness)2) != (Object)null) ? new ScribanCrate((Crate)(object)Core.GetInHand((Handedness)2)) : null; public static float Health { get { RigManager rigManager = Player.RigManager; float? obj; if (rigManager == null) { obj = null; } else { Health health = rigManager.health; obj = ((health != null) ? new float?(health.curr_Health) : null); } float? num = obj; return num.GetValueOrDefault(); } } public static float MaxHealth { get { RigManager rigManager = Player.RigManager; float? obj; if (rigManager == null) { obj = null; } else { Health health = rigManager.health; obj = ((health != null) ? new float?(health.max_Health) : null); } float? num = obj; return num.GetValueOrDefault(); } } public static float HealthPercentange => Health / MaxHealth * 100f; } public class ScribanGame : ScriptObject { public static ScribanCrate Level { get { StreamSession session = SceneStreamer.Session; object result; if (!((Object)(object)((session != null) ? session.Level : null) != (Object)null)) { result = null; } else { StreamSession session2 = SceneStreamer.Session; result = new ScribanCrate((Crate)(object)((session2 != null) ? session2.Level : null)); } return (ScribanCrate)result; } } public static string LevelName => Core.CleanLevelName(); public static string MLVersion => AppDomain.CurrentDomain?.GetAssemblies()?.FirstOrDefault((Assembly x) => x.GetName().Name == "MelonLoader")?.GetName()?.Version?.ToString() ?? "N/A"; public static int FPS => Fps.FramesPerSecond; public static string OperatingSystem => SystemInfo.operatingSystem; public static int ModsCount { get { int result = 0; if (AssetWarehouse.ready && AssetWarehouse.Instance != null) { result = AssetWarehouse.Instance.PalletCount() - AssetWarehouse.Instance.gamePallets.Count; } return result; } } public static int CodeModsCount => MelonTypeBase<MelonMod>.RegisteredMelons.Count; } public class ScribanUtils : ScriptObject { public static ScribanPallet GetPallet(string barcode) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown Pallet pallet = default(Pallet); if (AssetWarehouse.Instance.TryGetPallet(new Barcode(barcode), ref pallet)) { return new ScribanPallet(pallet); } return null; } public static string CleanString(string str) { return Core.RemoveUnityRichText(str); } } public class Thunderstore { public readonly string UserAgent; private Package _fetchedPackage; private bool _isLatestVersion; private string _currentVersion; public bool IsV1Deprecated { get; set; } public Thunderstore(string userAgent) { UserAgent = userAgent; } public Thunderstore(string userAgent, bool isV1Deprecated) : this(userAgent) { IsV1Deprecated = isV1Deprecated; } public Thunderstore() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); if (executingAssembly != null) { AssemblyName name = executingAssembly.GetName(); if (name != null) { UserAgent = $"{name.Name} / {name.Version} C# Application"; } } } public Thunderstore(bool isV1Deprecated) { IsV1Deprecated = isV1Deprecated; Assembly executingAssembly = Assembly.GetExecutingAssembly(); if (executingAssembly != null) { AssemblyName name = executingAssembly.GetName(); if (name != null) { UserAgent = $"{name.Name} / {name.Version}"; } } } public void BL_FetchPackage(string name, string author, string currentVersion, Instance logger = null) { if (_fetchedPackage != null) { return; } _currentVersion = currentVersion; try { _fetchedPackage = GetPackage(author, name); if (_fetchedPackage == null && logger != null) { logger.Warning("Could not find Thunderstore package for " + name); } if (string.IsNullOrWhiteSpace(_fetchedPackage.Latest?.Version) && logger != null) { logger.Warning("Latest version could not be found or the version is empty"); } _isLatestVersion = _fetchedPackage.IsLatestVersion(currentVersion); if (!_isLatestVersion) { if (logger != null) { logger.Warning($"A new version of {name} is available: v{_fetchedPackage.Latest.Version} while the current is v{currentVersion}. It is recommended that you update"); } } else if (SemVersion.Parse(currentVersion, false) == _fetchedPackage.Latest.SemanticVersion) { if (logger != null) { logger.Msg($"Latest version of {name} is installed! (v{currentVersion})"); } } else if (logger != null) { logger.Msg($"Beta release of {name} is installed (v{_fetchedPackage.Latest.Version} is newest, v{currentVersion} is installed)"); } } catch (ThunderstorePackageNotFoundException) { if (logger != null) { logger.Warning("Could not find Thunderstore package for " + name); } } catch (Exception ex2) { if (logger != null) { logger.Error("An unexpected error has occurred while trying to check if " + name + " is the latest version", ex2); } } } public void BL_CreateMenuLabel(Page page, bool createBlankSpace = true) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (_fetchedPackage != null) { if (createBlankSpace) { ((Element)page.CreateFunction("", Color.white, (Action)null)).SetProperty((ElementProperties)1); } ((Element)page.CreateFunction("Current Version: v" + _currentVersion + ((_isLatestVersion || _fetchedPackage == null) ? string.Empty : "<br><color=#00FF00>(Update available!)</color>"), Color.white, (Action)null)).SetProperty((ElementProperties)1); } } public void BL_SendNotification() { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown if (_fetchedPackage != null && !_isLatestVersion) { NotificationText message = default(NotificationText); ((NotificationText)(ref message))..ctor($"There is a new version of {_fetchedPackage.Name}. Go to Thunderstore and download the latest version which is <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 { return task != null && task.IsCompletedSuccessfully && task.Result != null; } public T SendRequest<T>(string url) { HttpClientHandler handler = new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Manual, ServerCertificateCustomValidationCallback = (HttpRequestMessage _, X509Certificate2? _, X509Chain? _, SslPolicyErrors _) => true }; HttpClient httpClient = new HttpClient(handler); httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); Task<HttpResponseMessage> async = httpClient.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_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown return IsLatestVersion(@namespace, name, new SemVersion(currentVersion)); } public bool IsLatestVersion(string @namespace, string name, SemVersion currentVersion) { if (!IsV1Deprecated) { return GetPackageMetrics(@namespace, name)?.IsLatestVersion(currentVersion) ?? false; } return GetPackage(@namespace, name)?.IsLatestVersion(currentVersion) ?? false; } private static bool IsPackageNotFound(HttpResponseMessage response, string details = "") { if (response.StatusCode != HttpStatusCode.NotFound) { return false; } if (details != null && details.Length == 0) { details = GetDetails(response); } return string.Equals(details, "Not found.", StringComparison.OrdinalIgnoreCase); } private static bool IsThunderstoreError(HttpResponseMessage response, out string details) { details = GetDetails(response); return !string.IsNullOrWhiteSpace(details); } private static string GetDetails(HttpResponseMessage response) { if (response.IsSuccessStatusCode) { return null; } Task<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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown if (current == null) { return false; } if (Latest == null || Latest.SemanticVersion == (SemVersion)null) { return false; } return new SemVersion(current) >= Latest.SemanticVersion; } } public class PackageVersion { [JsonProperty("namespace")] public string Namespace { get; internal set; } [JsonProperty("name")] public string Name { get; internal set; } [JsonProperty("version_number")] public string Version { get { return ((object)SemanticVersion).ToString(); } internal set { SemanticVersion = SemVersion.Parse(value, false); } } [JsonIgnore] public SemVersion SemanticVersion { get; internal set; } [JsonProperty("full_name")] public string FullName { get; internal set; } [JsonProperty("description")] public string Description { get; internal set; } [JsonProperty("icon")] public string Icon { get; internal set; } [JsonProperty("dependencies")] public List<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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown if (current == null) { return false; } if (LatestSemanticVersion == (SemVersion)null) { return false; } return new SemVersion(current) >= LatestSemanticVersion; } } public class ThunderstoreErrorResponse { [JsonProperty("detail")] public string Details { get; internal set; } } public class ThunderstoreErrorException : Exception { public string Details { get; } public HttpStatusCode HttpStatusCode { get; } public ThunderstoreErrorException() { } public ThunderstoreErrorException(string message) : base(message) { } public ThunderstoreErrorException(string message, Exception innerException) : base(message, innerException) { } public ThunderstoreErrorException(string message, string details, HttpStatusCode httpStatusCode, Exception innerException) : base(message, innerException) { Details = details; HttpStatusCode = httpStatusCode; } public ThunderstoreErrorException(string message, HttpResponseMessage response) : base(message) { if (response.IsSuccessStatusCode) { return; } HttpStatusCode = response.StatusCode; Task<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 i = 0; i < list.Count; i++) { 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 i = 0; i < list.Count; i++) { 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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (MenuPage == null && CreatesMenu) { MenuPage = MenuManager.PluginsPage.CreatePage(Name, MenuColor, 0, true); ((Element)MenuPage.CreateFunction($"Version: v{Version}", Color.white, (Action)null)).SetProperty((ElementProperties)1); ((Element)MenuPage.CreateFunction("Author: " + Author, Color.white, (Action)null)).SetProperty((ElementProperties)1); ((Element)MenuPage.CreateFunction(string.Empty, Color.white, (Action)null)).SetProperty((ElementProperties)1); PopulateMenu(MenuPage); } } void PopulateMenu(Page page) { } } public abstract class Plugin : IPlugin { public abstract string Name { get; } public abstract SemVersion Version { get; } public abstract string Author { get; } public virtual bool CreatesMenu => false; public virtual Color MenuColor => Color.white; public MelonPreferences_Category Category { get; private set; } public Logger Logger { get; set; } public Page MenuPage { get; set; } public void Internal_Init() { Logger = new Logger(Core.Logger, Name); Category = MelonPreferences.CreateCategory("LabPresence_" + Name + "_Config"); Category.SetFilePath(Path.Combine(MelonEnvironment.UserDataDirectory, "LabPresence", Name.ToLower() + ".cfg")); Category.SaveToFile(false); Init(); } public virtual void Init() { } public virtual void PopulateMenu(Page page) { } } public abstract class Plugin<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) { List<IPlugin> list = new List<IPlugin>(_Plugins); list.ForEach(delegate(IPlugin x) { if (!(x.Name != name)) { if (x.MenuPage != null) { Menu.DestroyPage(x.MenuPage); } _Plugins.Remove(x); } }); } public static void Unregister(Type plugin) { Unregister(GetPluginFromType(plugin).Name); } public static void Unregister<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; } [CompilerGenerated] private sealed class <AfterLevelLoaded>d__24 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Action callback; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <AfterLevelLoaded>d__24(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } StreamSession session = SceneStreamer.Session; if (session == null || (int)session.Status != 2) { <>2__current = null; <>1__state = 1; return true; } callback?.Invoke(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private float ElapsedSeconds = 0f; private bool turnedOff = false; public override string Name => "Fusion"; public override SemVersion Version => new SemVersion(1, 0, 0, "", ""); public override string Author => "HAHOOS"; public override bool CreatesMenu => true; public override Color MenuColor => Color.cyan; internal static FusionPlugin Instance { get; set; } public override void Init() { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown Instance = this; if (!Fusion.HasFusion) { base.Logger.Error("LabFusion is not installed, so FusionPlugin will not be set up!"); return; } PlaceholderManager.RegisterPlaceholder("labfusion", delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown ScriptObject val = new ScriptObject((IEqualityComparer<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(); } public override void PopulateMenu(Page page) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) ((Element)page.CreateBool("Override Time", Color.yellow, GetConfig().OverrideTimeToLobby, (Action<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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown Fusion.Init(base.Logger); object obj = <>O.<0>__OnDisconnect; if (obj == null) { ServerEvent val = OnDisconnect; <>O.<0>__OnDisconnect = val; obj = (object)val; } MultiplayerHooking.OnDisconnected += (ServerEvent)obj; } private static void OnDisconnect() { if (RichPresenceManager.OverrideTimestamp?.Origin == "fusion") { RichPresenceManager.ResetOverrideTimestamp(); } Overwrites.OnLevelLoaded.Run(); } private bool JoinRequested(JoinRequestMessage message) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown try { base.Logger.Info("Join requested"); MelonCoroutines.Start(AfterLevelLoaded(after)); } catch (Exception value) { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Failure | LabPresence"), Message = NotificationText.op_Implicit("An unexpected error has occurred while handling join request, check the console or logs for more details"), Type = (NotificationType)2, PopupLength = 5f, ShowTitleOnPopup = true }); base.Logger.Error($"An unexpected error has occurred while handling join request, exception:\n{value}"); return false; } return true; void after() { Fusion.JoinRequest(message); } } private bool OnLevelLoaded() { if (Fusion.IsConnected) { RichPresenceManager.TrySetRichPresence(GetConfig().LevelLoaded, (ActivityType)0, GetParty(), GetSecrets()); } return Fusion.IsConnected; } private bool OnLevelLoading() { if (Fusion.IsConnected) { RichPresenceManager.TrySetRichPresence(GetConfig().LevelLoading, (ActivityType)0, GetParty(), GetSecrets()); } return Fusion.IsConnected; } private bool Join(JoinMessage e) { if (!Fusion.HasFusion) { return false; } try { base.Logger.Info("Received Join Request"); string text = RichPresenceManager.Decrypt(e.Secret); string[] array = text.Split("|"); if (array.Length <= 1) { throw new ArgumentException("Secret provided to join the lobby did not include all of the necessary info"); } if (array.Length > 2) { throw new ArgumentException("Secret provided to join the lobby was invalid, the name of the network layer or code to the server may have contained the '|' character used to separate network layer & code, causing unexpected results"); } string layer = array[0]; string code = array[1]; MelonCoroutines.Start(AfterLevelLoaded(join)); void join() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown base.Logger.Info("Attempting to join with the following code: " + code); if (code != Fusion.GetLobbyCode()) { Notifier.Send(new Notification { Title = NotificationText.op_Implicit("LabPresence"), Message = NotificationText.op_Implicit("Attempting to join the target lobby, this might take a few seconds..."), PopupLength = 4f, ShowTitleOnPopup = true, Type = (NotificationType)0 }); if (Fusion.EnsureNetworkLayer(layer)) { Fusion.JoinByCode(code); } else { Fusion.ErrorNotif("Failed to ensure network layer, check the console/logs for errors. If none are present, it's likely the user is playing on a network layer that you do not have.", 5f); } } else { base.Logger.Error("Player is already in the lobby"); Fusion.ErrorNotif("Could not join, because you are already in the lobby!", 5f); } } } catch (Exception value) { Fusion.ErrorNotif("An unexpected error has occurred while trying to join the lobby, check the console or logs for more details", 5f); base.Logger.Error($"An unexpected error has occurred while trying to join the lobby, exception:\n{value}"); return false; } return true; } private static Party GetParty() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown if (!Fusion.IsConnected) { return null; } ulong lobbyID = Fusion.GetLobbyID(); if (lobbyID == 0L || lobbyID.ToString().Length < 2) { return null; } return new Party { ID = Fusion.GetLobbyID().ToString(), Privacy = (PrivacySetting)((Fusion.GetPrivacy() == Fusion.ServerPrivacy.Public) ? 1 : 0), Max = Fusion.GetPlayerCount().max, Size = Fusion.GetPlayerCount().current }; } private static Secrets GetSecrets() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown if (!Fusion.IsConnected) { return null; } if ((int)SceneStreamer.Session.Status == 1) { return null; } var (num, num2) = Fusion.GetPlayerCount(); if (num >= num2) { return null; } switch (Fusion.GetPrivacy()) { case Fusion.ServerPrivacy.Locked: return null; default: if (!Fusion.IsAllowedToInvite()) { return null; } break; case Fusion.ServerPrivacy.Public: case Fusion.ServerPrivacy.Friends_Only: break; } string currentNetworkLayerTitle = Fusion.GetCurrentNetworkLayerTitle(); if (string.IsNullOrWhiteSpace(currentNetworkLayerTitle)) { return null; } string lobbyCode = Fusion.GetLobbyCode(); if (string.IsNullOrWhiteSpace(lobbyCode)) { return null; } string joinSecret = RichPresenceManager.Encrypt(currentNetworkLayerTitle + "|" + lobbyCode); return new Secrets { JoinSecret = joinSecret }; } [IteratorStateMachine(typeof(<AfterLevelLoaded>d__24))] private static IEnumerator AfterLevelLoaded(Action callback) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <AfterLevelLoaded>d__24(0) { callback = callback }; } private void Update() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Invalid comparison between Unknown and I4 if (base.Category != null && GetConfig() != null) { Fusion.EnsureMetaDataSync(); } ElapsedSeconds += Time.deltaTime; if (!(ElapsedSeconds >= 5f)) { return; } ElapsedSeconds = 0f; if (Fusion.IsConnected) { StreamSession session = SceneStreamer.Session; if (session != null && (int)session.Status == 2) { RichPresenceManager.AutoUpdate = false; turnedOff = true; var (key, tooltip) = Fusion.GetGamemodeRPC(); RichPresenceManager.TrySetRichPresence(RichPresenceManager.CurrentConfig, (ActivityType)0, GetParty(), GetSecrets(), null, new Asset(key, tooltip)); return; } } if (!RichPresenceManager.AutoUpdate && turnedOff) { RichPresenceManager.AutoUpdate = true; turnedOff = false; } } } public static class Fusion { public enum ServerPrivacy { Unknown = -1, Public, Private, Friends_Only, Locked } [CompilerGenerated] private static class <>O { public static ServerEvent <0>__Update; public static ServerEvent <1>__OnLobby; public static Func<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"; } return string.IsNullOrWhiteSpace(text) ? (lobbyInfo.LobbyHostName + "'s lobby") : text; } public static string GetHost() { if (!IsConnected) { return "N/A"; } return Internal_GetHost(); } internal static string Internal_GetHost() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; string text = ((lobbyInfo != null) ? lobbyInfo.LobbyHostName : null); if (lobbyInfo == null) { return "N/A"; } return string.IsNullOrWhiteSpace(text) ? "N/A" : text; } public static (int current, int max) GetPlayerCount() { if (!IsConnected) { return (-1, -1); } return Internal_GetPlayerCount(); } private static (int current, int max) Internal_GetPlayerCount() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return (-1, -1); } int playerCount = lobbyInfo.PlayerCount; int maxPlayers = lobbyInfo.MaxPlayers; return (playerCount, maxPlayers); } public static ServerPrivacy GetPrivacy() { if (!IsConnected) { return ServerPrivacy.Unknown; } return Internal_GetPrivacy(); } private static ServerPrivacy Internal_GetPrivacy() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected I4, but got Unknown LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return ServerPrivacy.Unknown; } ServerPrivacy privacy = lobbyInfo.Privacy; return (ServerPrivacy)privacy; } public static ulong GetLobbyID() { if (!IsConnected) { return 0uL; } return Internal_GetLobbyID(); } private static ulong Internal_GetLobbyID() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return 0uL; } return lobbyInfo.LobbyID; } public static string GetLobbyCode() { if (!IsConnected) { return string.Empty; } return Internal_GetLobbyCode(); } private static string Internal_GetLobbyCode() { LobbyInfo lobbyInfo = LobbyInfoManager.LobbyInfo; if (lobbyInfo == null) { return string.Empty; } return lobbyInfo.LobbyCode; } public static (string username, string displayName) GetCurrentPlayer() { if (!IsConnected) { return (null, null); } return Internal_GetCurrentPlayer(); } private static (string username, string displayName) Internal_GetCurrentPlayer() { string username = LocalPlayer.Username; string item = default(string); if (MetadataHelper.TryGetDisplayName(PlayerIDManager.LocalID, ref item)) { item = username; } return (username, item); } public static string GetCurrentNetworkLayerTitle() { if (!IsConnected) { return null; } return Internal_GetCurrentNetworkLayerTitle(); } private static string Internal_GetCurrentNetworkLayerTitle() { NetworkLayer layer = NetworkLayerManager.Layer; return (layer != null) ? layer.Title : null; } public static void EnsureMetaDataSync() { if (IsConnected) { Internal_EnsureMetadataSync(); } } internal static void Internal_EnsureMetadataSync() { string value = default(string); bool result; if (!NetworkInfo.IsHost) { LocalPlayer.Metadata.Metadata.TryRemoveMetadata("LabPresence.AllowInvites"); } else if (!LocalPlayer.Metadata.Metadata.TryGetMetadata("LabPresence.AllowInvites", ref value) || !bool.TryParse(value, out result) || result != FusionPlugin.Instance.GetConfig().AllowPlayersToInvite) { LocalPlayer.Metadata.Metadata.TrySetMetadata("LabPresence.AllowInvites", FusionPlugin.Instance.GetConfig().AllowPlayersToInvite.ToString()); } } public static bool IsAllowedToInvite() { if (!IsConnected) { return false; } return Internal_IsAllowedToInvite(); } private static bool Internal_IsAllowedToInvite() { if (!FusionPlugin.Instance.GetConfig().Joins) { return false; } if (NetworkInfo.IsHost) { return true; } if (PlayerIDManager.GetHostID() == null) { return true; } NetworkPlayer val = default(NetworkPlayer); if (!NetworkPlayerManager.TryGetPlayer(PlayerID.op_Implicit(PlayerIDManager.GetHostID()), ref val)) { return true; } if (val == null) { return true; } PlayerID playerID = val.PlayerID; object value; if (playerID == null) { value = null; } else { PlayerMetadata metadata = playerID.Metadata; if (metadata == null) { value = null; } else { NetworkMetadata metadata2 = metadata.Metadata; value = ((metadata2 != null) ? metadata2.GetMetadata("LabPresence.AllowInvites") : null); } } if (string.IsNullOrWhiteSpace((string?)value)) { return true; } PlayerID playerID2 = val.PlayerID; object obj; if (playerID2 == null) { obj = null; } else { PlayerMetadata metadata3 = playerID2.Metadata; if (metadata3 == null) { obj = null; } else { NetworkMetadata metadata4 = metadata3.Metadata; obj = ((metadata4 != null) ? metadata4.GetMetadata("LabPresence.AllowInvites") : null); } } return (string?)obj == bool.TrueString; } public static bool EnsureNetworkLayer(string title) { if (!HasFusion) { return false; } return Internal_EnsureNetworkLayer(title); } private static bool Internal_EnsureNetworkLayer(string title) { if (!NetworkLayer.LayerLookup.TryGetValue(title, out var value)) { Logger.Error("Could find network layer '" + title + "'"); return false; } try { if (NetworkLayerManager.LoggedIn && NetworkLayerManager.Layer == value) { return true; } if (NetworkLayerManager.LoggedIn) { NetworkLayerManager.LogOut(); } NetworkLayerManager.LogIn(value); } catch (Exception value2) { Logger.Error($"An unexpected error has occurred while ensuring fusion is on the right network layer, exception:\n{value2}"); return false; } return true; } public static void JoinByCode(string code) { if (HasFusion && !string.IsNullOrWhiteSpace(code)) { Internal_JoinByCode(code); } } private static void Internal_JoinByCode(string code) { if (string.Equals(NetworkHelper.GetServerCode(), code, StringComparison.OrdinalIgnoreCase)) { ErrorNotif("You are already in the lobby!"); return; } if (NetworkLayerManager.Layer.Matchmaker != null) { NetworkLayerManager.Layer.Matchmaker.RequestLobbiesByCode(code, (Action<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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Invalid comparison between Unknown and I4 //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Invalid comparison between Unknown and I4 LobbyInfo val = x.Lobbies.FirstOrDefault(); LobbyInfo targetLobby = ((LobbyMetadataInfo)(ref val.Metadata)).LobbyInfo; if (targetLobby == null || targetLobby.LobbyCode == null) { Core.Logger.Error("The lobby was not found"); ErrorNotif("The lobby you wanted to join was not found!"); return; } if ((int)targetLobby.Privacy == 2) { PlayerList playerList = targetLobby.PlayerList; PlayerInfo val2 = ((playerList == null) ? null : playerList.Players?.FirstOrDefault((Func<PlayerInfo, bool>)((PlayerInfo x) => x.Username == targetLobby.LobbyHostName))); if (val2 == null) { Core.Logger.Warning("Could not find host, unable to verify if you can join the lobby (Privacy: Friends Only)"); } else if (!NetworkLayerManager.Layer.IsFriend(val2.PlatformID)) { Core.Logger.Error("The lobby is friends only and you are not friends with the host, cannot join"); ErrorNotif("Cannot join the lobby, because it is friends only and you are not friends with the host!"); return; } } if ((int)targetLobby.Privacy == 3) { Core.Logger.Error("The lobby is locked, cannot join"); ErrorNotif("Cannot join the lobby, because it is locked"); return; } if (IsConnected) { NetworkHelper.Disconnect("Joining another lobby"); } NetworkHelper.JoinServerByCode(code); } internal static void ErrorNotif(string msg, float length = 3.5f) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Error | FLB"), Message = NotificationText.op_Implicit(msg), PopupLength = length, Type = (NotificationType)2 }); } internal static void JoinRequest(JoinRequestMessage message) { if (IsConnected && message != null) { Internal_JoinRequest(message); } } private static void Internal_JoinRequest(JoinRequestMessage message) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (message != null) { FusionConfig config = FusionPlugin.Instance.GetConfig(); if (config != null && config.ShowJoinRequestPopUp) { Texture2D customIcon = (Texture2D)(((object)RichPresenceManager.GetAvatar(message.User, (AvatarSize)512)) ?? ((object)new Texture2D(1, 1))); Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Join Request"), Message = NotificationText.op_Implicit(message.User.DisplayName + " (@" + message.User.Username + ") wants to join you! Go to the LabFusion menu to accept or deny the request"), PopupLength = 5f, ShowTitleOnPopup = true, Type = (NotificationType)4, CustomIcon = customIcon }); } Notifier.Send(new Notification { Title = NotificationText.op_Implicit("Join Request"), Message = NotificationText.op_Implicit(message.User.DisplayName + " (@" + message.User.Username + ") wants to join you!"), PopupLength = 5f, SaveToMenu = true, ShowPopup = false, Type = (NotificationType)0, OnAccepted = delegate { Core.Client.Respond(message, true); }, OnDeclined = delegate { Core.Client.Respond(message, false); } }); } } internal static void Init(Logger logger) { if (HasFusion) { Internal_Init(logger); } } private static void Internal_Init(Logger logger) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown Logger = logger; object obj = <>O.<0>__Update;