using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using BepInEx; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppSystem; using Microsoft.CodeAnalysis; using ProjectM; using ProjectM.Network; using ProjectM.UI; using Stunlock.Network; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("RememberPassword")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Remembers V Rising dedicated-server join passwords and autofills them on the next Join.")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("RememberPassword")] [assembly: AssemblyTitle("RememberPassword")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RememberPassword { internal sealed class StoredServer { [JsonPropertyName("key")] public string Key { get; set; } = ""; [JsonPropertyName("name")] public string Name { get; set; } = ""; [JsonPropertyName("address")] public string Address { get; set; } = ""; [JsonPropertyName("password")] public string Password { get; set; } = ""; } internal sealed class StoreFile { [JsonPropertyName("version")] public int Version { get; set; } [JsonPropertyName("protected")] public bool Protected { get; set; } [JsonPropertyName("servers")] public List Servers { get; set; } = new List(); } internal static class AddressKey { internal const string SteamIPv4Prefix = "SteamIPv4://"; private static readonly string[] KnownSchemes = new string[5] { "SteamIPv4://", "Lidgren://", "SteamP2P://", "EosP2P://", "LocalOnly://" }; internal static string Canonicalize(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return ""; } raw = raw.Trim(); string[] knownSchemes = KnownSchemes; foreach (string text in knownSchemes) { if (raw.StartsWith(text, StringComparison.OrdinalIgnoreCase)) { return text + raw.Substring(text.Length).Trim(); } } if (raw.IndexOf("://", StringComparison.Ordinal) >= 0) { return raw; } if (TrySplitHostPort(raw, out var host, out var port)) { return "SteamIPv4://" + host + ":" + port; } if (LooksLikeBareIPv4(raw)) { return "SteamIPv4://" + raw + ":9876"; } return raw; } internal static bool Equivalent(string a, string b) { string text = Canonicalize(a); string b2 = Canonicalize(b); if (!string.IsNullOrEmpty(text)) { return string.Equals(text, b2, StringComparison.OrdinalIgnoreCase); } return false; } private static bool TrySplitHostPort(string raw, out string host, out string port) { host = null; port = null; int num = raw.LastIndexOf(':'); if (num <= 0 || num == raw.Length - 1) { return false; } host = raw.Substring(0, num).Trim(); port = raw.Substring(num + 1).Trim(); if (string.IsNullOrEmpty(host) || !ushort.TryParse(port, out var _)) { return false; } return true; } private static bool LooksLikeBareIPv4(string raw) { string[] array = raw.Split('.'); if (array.Length != 4) { return false; } string[] array2 = array; for (int i = 0; i < array2.Length; i++) { if (!byte.TryParse(array2[i], out var _)) { return false; } } return true; } } internal static class PasswordProtector { private static readonly byte[] Entropy = new byte[16] { 82, 80, 45, 86, 82, 49, 46, 49, 45, 102, 97, 110, 103, 108, 121, 1 }; internal static string Protect(string plaintext) { if (string.IsNullOrEmpty(plaintext)) { return ""; } return Convert.ToBase64String(ProtectedData.Protect(Encoding.UTF8.GetBytes(plaintext), Entropy, (DataProtectionScope)0)); } internal static bool TryUnprotect(string stored, out string plaintext) { plaintext = null; if (string.IsNullOrEmpty(stored)) { return false; } try { byte[] bytes = ProtectedData.Unprotect(Convert.FromBase64String(stored), Entropy, (DataProtectionScope)0); plaintext = Encoding.UTF8.GetString(bytes); return !string.IsNullOrEmpty(plaintext); } catch (CryptographicException) { return false; } catch (FormatException) { return false; } } } internal static class PasswordStore { internal const int CurrentFileVersion = 2; private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions { WriteIndented = true, PropertyNameCaseInsensitive = true }; private static readonly object Sync = new object(); private static string _path; private static StoreFile _data = new StoreFile(); internal static string FilePath => _path; internal static void Initialize() { _path = Path.Combine(Paths.ConfigPath, "RememberPassword.json"); Load(); } internal static void Load() { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown lock (Sync) { try { if (!File.Exists(_path)) { _data = NewEmptyStore(); return; } _data = JsonSerializer.Deserialize(File.ReadAllText(_path), JsonOptions) ?? NewEmptyStore(); StoreFile data = _data; if (data.Servers == null) { List list = (data.Servers = new List()); } bool flag = _data.Protected || _data.Version >= 2; if (flag) { DecryptRowsInPlace(); } Plugin.Logger.LogInfo((object)($"Loaded {_data.Servers.Count} saved server password(s) from RememberPassword.json" + (flag ? " (DPAPI)." : " (plaintext; will encrypt on next successful save)."))); } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; bool flag2 = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(89, 1, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to load RememberPassword.json: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Leaving the file on disk until a successful save."); } logger.LogWarning(val); _data = NewEmptyStore(); } } } internal static bool TryGet(string addressOrKey, out string password) { password = null; lock (Sync) { StoredServer storedServer = Find(addressOrKey); if (storedServer == null || string.IsNullOrEmpty(storedServer.Password)) { return false; } password = storedServer.Password; return true; } } internal static void Save(string addressOrKey, string name, string password) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown if (string.IsNullOrEmpty(password)) { return; } string text = AddressKey.Canonicalize(addressOrKey); if (string.IsNullOrWhiteSpace(text)) { return; } lock (Sync) { StoredServer storedServer = Find(text); if (storedServer == null) { storedServer = new StoredServer(); _data.Servers.Add(storedServer); } storedServer.Key = text; storedServer.Address = text; if (!string.IsNullOrWhiteSpace(name)) { storedServer.Name = name.Trim(); } storedServer.Password = password; PersistUnlocked(); ManualLogSource logger = Plugin.Logger; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(24, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Saved join password for "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Describe(storedServer.Name, storedServer.Address, storedServer.Key)); } logger.LogInfo(val); } } private static StoredServer Find(string addressOrKey) { if (string.IsNullOrWhiteSpace(addressOrKey)) { return null; } return _data.Servers.FirstOrDefault((StoredServer s) => AddressKey.Equivalent(s.Key, addressOrKey) || AddressKey.Equivalent(s.Address, addressOrKey)); } private static void PersistUnlocked() { //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown try { string directoryName = Path.GetDirectoryName(_path); if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } StoreFile storeFile = new StoreFile { Version = 2, Protected = true, Servers = new List(_data.Servers.Count) }; foreach (StoredServer server in _data.Servers) { storeFile.Servers.Add(new StoredServer { Key = server.Key, Name = server.Name, Address = server.Address, Password = (string.IsNullOrEmpty(server.Password) ? "" : PasswordProtector.Protect(server.Password)) }); } string contents = JsonSerializer.Serialize(storeFile, JsonOptions); string text = _path + ".tmp"; File.WriteAllText(text, contents); if (File.Exists(_path)) { File.Replace(text, _path, _path + ".bak"); } else { File.Move(text, _path); } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(39, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Failed to write RememberPassword.json: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); } logger.LogWarning(val); try { if (File.Exists(_path + ".tmp")) { File.Delete(_path + ".tmp"); } } catch { } } } private static void DecryptRowsInPlace() { foreach (StoredServer server in _data.Servers) { if (!string.IsNullOrEmpty(server.Password)) { if (PasswordProtector.TryUnprotect(server.Password, out var plaintext)) { server.Password = plaintext; continue; } Plugin.Logger.LogWarning((object)("Could not decrypt saved password for " + Describe(server.Name, server.Address, server.Key) + " (wrong Windows user, or file copied from another PC). Row ignored.")); server.Password = ""; } } } private static StoreFile NewEmptyStore() { return new StoreFile { Version = 2, Protected = true, Servers = new List() }; } internal static string Describe(string name, string address, string key = null) { if (!string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(address)) { return $"'{name}' ({address})"; } if (!string.IsNullOrWhiteSpace(address)) { return address; } if (!string.IsNullOrWhiteSpace(key)) { return key; } if (!string.IsNullOrWhiteSpace(name)) { return "'" + name + "' (name only, not used as a key)"; } return "(unknown server)"; } } [BepInPlugin("fangly.RememberPassword", "RememberPassword", "1.1.0")] [BepInProcess("VRising.exe")] public class Plugin : BasePlugin { internal const string GUID = "fangly.RememberPassword"; internal const string NAME = "RememberPassword"; internal const string VERSION = "1.1.0"; private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } public override void Load() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown Instance = this; Logger = ((BasePlugin)this).Log; if (Application.productName == "VRisingServer") { ((BasePlugin)this).Log.LogInfo((object)"RememberPassword is a client-only plugin; not loading on VRisingServer."); return; } PasswordStore.Initialize(); _harmony = new Harmony("fangly.RememberPassword"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); ManualLogSource log = ((BasePlugin)this).Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(74, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("RememberPassword "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("1.1.0"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded (client). Passwords are never written to the log."); } log.LogInfo(val); } public override bool Unload() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } return true; } } internal static class PluginInfo { public const string GUID = "fangly.RememberPassword"; public const string Name = "RememberPassword"; public const string Version = "1.1.0"; } public static class MyPluginInfo { public const string PLUGIN_GUID = "fangly.RememberPassword"; public const string PLUGIN_NAME = "RememberPassword"; public const string PLUGIN_VERSION = "1.1.0"; } } namespace RememberPassword.Patches { internal readonly struct PendingPasswordBind { internal readonly string Address; internal readonly string Name; internal readonly string Password; internal PendingPasswordBind(string address, string name, string password) { Address = address ?? ""; Name = name ?? ""; Password = password ?? ""; } internal bool BelongsTo(string addressOrKey) { if (!string.IsNullOrEmpty(Password)) { return AddressKey.Equivalent(Address, addressOrKey); } return false; } } internal static class JoinContext { internal static string Key; internal static string Name; internal static string Address; internal static PendingPasswordBind? Pending; internal static bool FromServerBrowser; internal static string FilledForKey; internal static int FilledInstanceId; internal static void RememberIdentity(string key, string name, string address, bool fromBrowser) { string text = (string.IsNullOrWhiteSpace(key) ? "" : key.Trim()); string name2 = (string.IsNullOrWhiteSpace(name) ? "" : name.Trim()); string obj = (string.IsNullOrWhiteSpace(address) ? "" : address.Trim()); if (!AddressKey.Equivalent(obj, Address) && !AddressKey.Equivalent(text, Key)) { FilledForKey = null; FilledInstanceId = 0; } Key = text; Name = name2; Address = obj; FromServerBrowser = fromBrowser; } internal static void RememberPendingPassword(string password, string address, string name) { if (!string.IsNullOrEmpty(password)) { string text = AddressUtil.Normalize(address); if (!string.IsNullOrWhiteSpace(text)) { Pending = new PendingPasswordBind(text, name, password); } } } internal static void ClearPending() { Pending = null; } internal static void ClearIdentity() { Key = ""; Name = ""; Address = ""; FromServerBrowser = false; FilledForKey = null; FilledInstanceId = 0; } internal static void ClearJoin() { ClearPending(); ClearIdentity(); } } internal static class AddressUtil { internal unsafe static string Format(ConnectAddress address) { //IL_0042: 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) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) try { string text = ((object)(*(ConnectAddress*)(&address))/*cast due to .constrained prefix*/).ToString(); if (!string.IsNullOrWhiteSpace(text) && text != "0.0.0.0:0" && text != "SteamIPv4://0.0.0.0:0") { return text.Trim(); } } catch { } try { string text2 = SafeFixed(address.Address); if (!string.IsNullOrWhiteSpace(text2)) { return AddressKey.Canonicalize(text2 + ":" + address.Port); } if (address.TargetSteamId != 0L) { return "SteamP2P://" + address.TargetSteamId; } string text3 = SafeFixed(address.TargetEosId); if (!string.IsNullOrWhiteSpace(text3)) { return "EosP2P://" + text3; } } catch { } return ""; } internal static string Normalize(string raw) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(raw)) { return ""; } raw = raw.Trim(); try { ConnectAddress address = default(ConnectAddress); if (ConnectAddress.TryParse(raw, ref address)) { string text = Format(address); if (!string.IsNullOrWhiteSpace(text)) { return text; } } } catch { } return AddressKey.Canonicalize(raw); } internal static string SafeFixed(object fixedString) { if (fixedString == null) { return ""; } try { string text = fixedString.ToString(); if (string.IsNullOrWhiteSpace(text)) { return ""; } text = text.Trim(); if (text.StartsWith("FixedString", StringComparison.Ordinal)) { return ""; } return text; } catch { return ""; } } internal static bool ReadGameConnect(GameConnect connect, out string key, out string name, out string address) { //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) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_001d: Unknown result type (might be due to invalid IL or missing references) address = Normalize(Format(connect.ConnectAddress)); if (string.IsNullOrWhiteSpace(address)) { address = Normalize(SafeFixed(connect.HostAddress)); } name = SafeFixed(connect.ServerSaveName); key = address; return !string.IsNullOrWhiteSpace(address); } internal static void FromGameConnect(GameConnect connect, bool fromBrowser, out string key, out string name, out string address) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (ReadGameConnect(connect, out key, out name, out address)) { JoinContext.RememberIdentity(key, name, address, fromBrowser); return; } key = ""; name = ""; address = ""; } } [HarmonyPatch(typeof(GameHelper), "LaunchPasswordProtectedGame")] internal static class LaunchPasswordProtectedGamePatch { [HarmonyPrefix] private static void Prefix(PasswordGameConnect launchData) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0035: Unknown result type (might be due to invalid IL or missing references) try { AddressUtil.FromGameConnect(launchData.ConnectData, launchData.FromServerBrowser, out var _, out var _, out var _); Plugin.Logger.LogInfo((object)("Password prompt opening for " + PasswordStore.Describe(JoinContext.Name, JoinContext.Address, JoinContext.Key) + (launchData.FromServerBrowser ? " [server browser]" : " [direct/other]"))); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("LaunchPasswordProtectedGame prefix failed: " + ex.Message)); } } } [HarmonyPatch(typeof(Join_PasswordView))] internal static class JoinPasswordViewPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPostfix(Join_PasswordView __instance) { TryFill(__instance); } [HarmonyPatch("Update")] [HarmonyPostfix] private static void UpdatePostfix(Join_PasswordView __instance) { TryFill(__instance); } [HarmonyPatch("OnButtonClick_Connect")] [HarmonyPrefix] private static void ConnectClickedPrefix(Join_PasswordView __instance) { try { ResolveViewIdentity(__instance, out var _, out var name, out var address); string text = ReadInput(__instance); if (!string.IsNullOrEmpty(text)) { JoinContext.RememberPendingPassword(text, address, name); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Join_PasswordView connect click failed: " + ex.Message)); } } [HarmonyPatch("OnButtonClick_Cancel")] [HarmonyPrefix] private static void CancelClickedPrefix() { try { Plugin.Logger.LogInfo((object)"Password dialog cancelled; pending password and identity cleared."); JoinContext.ClearJoin(); } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Join_PasswordView cancel failed: " + ex.Message)); } } private static void TryFill(Join_PasswordView view) { try { if ((Object)(object)view == (Object)null || !Object.op_Implicit((Object)(object)view)) { return; } TMP_InputField passwordInput = view.PasswordInput; if ((Object)(object)passwordInput == (Object)null || !Object.op_Implicit((Object)(object)passwordInput)) { return; } ResolveViewIdentity(view, out var key, out var name, out var address); if (string.IsNullOrWhiteSpace(address) && string.IsNullOrWhiteSpace(key)) { return; } string text = ((!string.IsNullOrWhiteSpace(address)) ? address : key); int instanceID = ((Object)view).GetInstanceID(); if ((JoinContext.FilledInstanceId != instanceID || !(JoinContext.FilledForKey == text)) && string.IsNullOrEmpty(passwordInput.text) && PasswordStore.TryGet(text, out var password)) { try { passwordInput.SetText(password, true); } catch { passwordInput.text = password; } JoinContext.FilledForKey = text; JoinContext.FilledInstanceId = instanceID; Plugin.Logger.LogInfo((object)("Autofilled saved password for " + PasswordStore.Describe(name, address, key))); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Join_PasswordView autofill failed: " + ex.Message)); } } private static void ResolveViewIdentity(Join_PasswordView view, out string key, out string name, out string address) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) key = ""; name = ""; address = ""; try { if ((Object)(object)view != (Object)null) { Nullable_Unboxed passwordGameConnect = view._PasswordGameConnect; if (passwordGameConnect.HasValue) { PasswordGameConnect value = passwordGameConnect.Value; if (AddressUtil.ReadGameConnect(value.ConnectData, out key, out name, out address)) { JoinContext.RememberIdentity(key, name, address, value.FromServerBrowser); return; } } } } catch { } key = JoinContext.Key; name = JoinContext.Name; address = JoinContext.Address; } private static string ReadInput(Join_PasswordView view) { TMP_InputField val = (((Object)(object)view != (Object)null) ? view.PasswordInput : null); if ((Object)(object)val == (Object)null) { return null; } return val.text; } } [HarmonyPatch(typeof(ClientBootstrapSystem), "Connect")] internal static class ClientBootstrapConnectPatch { [HarmonyPrefix] private static void Prefix(ConnectAddress connectAddress, ref string password) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { string text = AddressUtil.Normalize(AddressUtil.Format(connectAddress)); if (!string.IsNullOrWhiteSpace(text)) { bool flag = AddressKey.Equivalent(text, JoinContext.Address); string name = (flag ? JoinContext.Name : ""); JoinContext.RememberIdentity(text, name, text, flag && JoinContext.FromServerBrowser); if (string.IsNullOrEmpty(password) && PasswordStore.TryGet(text, out var password2)) { password = password2; JoinContext.RememberPendingPassword(password2, text, name); Plugin.Logger.LogInfo((object)("Injected saved password into Connect for " + PasswordStore.Describe(name, text, text))); } else if (!string.IsNullOrEmpty(password)) { JoinContext.RememberPendingPassword(password, text, name); } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("ClientBootstrapSystem.Connect prefix failed: " + ex.Message)); } } } [HarmonyPatch(typeof(ClientBootstrapSystem), "OnStatusChangedEvent")] internal static class ClientBootstrapStatusPatch { [HarmonyPostfix] private unsafe static void Postfix(ClientBootstrapSystem __instance, StatusChangedEvent statusChangedEvent) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) try { ClientConnectState status = statusChangedEvent.Status; if ((int)status == 4) { TrySaveOnConnected(__instance); } else if (IsTerminalFailure(status, __instance.DisconnectChangeReason)) { ConnectionStatusChangeReason disconnectChangeReason = __instance.DisconnectChangeReason; Plugin.Logger.LogInfo((object)("Join ended (" + ((object)(*(ClientConnectState*)(&status))/*cast due to .constrained prefix*/).ToString() + "/" + ((object)(*(ConnectionStatusChangeReason*)(&disconnectChangeReason))/*cast due to .constrained prefix*/).ToString() + ") for " + PasswordStore.Describe(JoinContext.Name, JoinContext.Address, JoinContext.Key) + "; pending password and identity cleared.")); JoinContext.ClearJoin(); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("OnStatusChangedEvent postfix failed: " + ex.Message)); } } private static void TrySaveOnConnected(ClientBootstrapSystem instance) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) string text = ""; try { text = AddressUtil.Normalize(AddressUtil.Format(instance._LastConnectData.ConnectAddress)); if (!string.IsNullOrWhiteSpace(text)) { string name = (AddressKey.Equivalent(text, JoinContext.Address) ? JoinContext.Name : ""); if (JoinContext.Pending.HasValue && !string.IsNullOrWhiteSpace(JoinContext.Pending.Value.Name)) { name = JoinContext.Pending.Value.Name; } JoinContext.RememberIdentity(text, name, text, JoinContext.FromServerBrowser); } } catch { } if (JoinContext.Pending.HasValue) { PendingPasswordBind value = JoinContext.Pending.Value; if (!string.IsNullOrWhiteSpace(text) && value.BelongsTo(text)) { PasswordStore.Save(text, value.Name, value.Password); } else if (!string.IsNullOrWhiteSpace(text)) { Plugin.Logger.LogInfo((object)("Connected to " + text + " but pending password was bound to a different host; not saving.")); } JoinContext.ClearPending(); } } private static bool IsTerminalFailure(ClientConnectState status, ConnectionStatusChangeReason reason) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected I4, but got Unknown if ((int)status == 1 || (int)status == 2 || (int)status == 3 || (int)status == 4) { return false; } if ((int)status == 5 || (int)status == 0) { return true; } switch (reason - 1) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 9: case 10: case 11: case 12: case 14: case 15: case 16: case 17: case 18: case 19: return true; default: return false; } } } [HarmonyPatch(typeof(Popup_DirectConnect), "OnButtonClick_Connect")] internal static class DirectConnectPatch { [HarmonyPrefix] private static void Prefix(Popup_DirectConnect __instance) { try { TMP_InputField serverInputField = __instance.ServerInputField; string text = (((Object)(object)serverInputField != (Object)null) ? serverInputField.text : null); if (!string.IsNullOrWhiteSpace(text)) { string text2 = AddressUtil.Normalize(text); JoinContext.RememberIdentity(text2, "", text2, fromBrowser: false); Plugin.Logger.LogInfo((object)("Direct Connect submit for " + text2)); } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("Popup_DirectConnect connect prefix failed: " + ex.Message)); } } } }