Decompiled source of RememberServerPassword v1.2.4

BepInEx/plugins/RememberServerPassword/MagiCorp.RememberServerPassword.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("MagiCorp.RememberServerPassword")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.4.0")]
[assembly: AssemblyInformationalVersion("1.2.4+7b8abd5dee3ec64ceec8e1941f1c66822a53c8ff")]
[assembly: AssemblyProduct("MagiCorp.RememberServerPassword")]
[assembly: AssemblyTitle("MagiCorp.RememberServerPassword")]
[assembly: AssemblyVersion("1.2.4.0")]
namespace MagiCorp.RememberServerPassword;

[BepInPlugin("magicorp.valheim.rememberserverpassword", "Remember Server Password", "1.2.4")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Guid = "magicorp.valheim.rememberserverpassword";

	public const string Name = "Remember Server Password";

	public const string Version = "1.2.4";

	private const string QuickConnectObjectName = "MagiCorp_RememberServerPassword_QuickConnect";

	internal static Plugin Instance;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<bool> AutoSubmit;

	internal static ConfigEntry<bool> RememberPasswords;

	internal static ConfigEntry<bool> ShowQuickConnectButton;

	internal static ConfigEntry<string> LastServer;

	internal static ConfigEntry<bool> ForgetAllPasswords;

	internal static ConfigEntry<bool> ForgetLastServer;

	private static readonly Dictionary<string, string> Passwords = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

	private static readonly byte[] DpapiEntropy = Encoding.UTF8.GetBytes("MagiCorp.RememberServerPassword.v1");

	private static string StorePath;

	private static string KeyPath;

	private static string PendingServerKey;

	private static string PendingPassword;

	private static string AutoAttemptServerKey;

	private static string ActiveServerKey;

	private static bool QuickConnectPending;

	private static float NextPasswordPromptAttempt;

	private void Awake()
	{
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable remembered server passwords.");
		AutoSubmit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AutoSubmit", true, "Automatically submit a remembered password when a known server requests one.");
		RememberPasswords = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RememberPasswords", true, "Remember manually entered passwords after a successful connection.");
		ShowQuickConnectButton = ((BaseUnityPlugin)this).Config.Bind<bool>("Quick Connect", "ShowQuickConnectButton", true, "Add a Quick Connect button to the Valheim main menu for the last successfully joined server.");
		LastServer = ((BaseUnityPlugin)this).Config.Bind<string>("Quick Connect", "LastServer", "", "Last successfully joined server identity. Managed automatically, but may be cleared manually.");
		ForgetAllPasswords = ((BaseUnityPlugin)this).Config.Bind<bool>("Maintenance", "ForgetAllPasswords", false, "Set true and restart the game to erase all remembered passwords. It resets to false automatically.");
		ForgetLastServer = ((BaseUnityPlugin)this).Config.Bind<bool>("Maintenance", "ForgetLastServer", false, "Set true and restart the game to clear the Quick Connect destination. It resets to false automatically.");
		StorePath = Path.Combine(Paths.ConfigPath, "MagiCorp.RememberServerPassword.dat");
		KeyPath = Path.Combine(Paths.ConfigPath, "MagiCorp.RememberServerPassword.key");
		bool flag = false;
		if (ForgetAllPasswords.Value)
		{
			Passwords.Clear();
			TryDelete(StorePath);
			TryDelete(KeyPath);
			ForgetAllPasswords.Value = false;
			flag = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"All remembered server passwords were erased.");
		}
		if (ForgetLastServer.Value)
		{
			LastServer.Value = "";
			ForgetLastServer.Value = false;
			flag = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Quick Connect last server was cleared.");
		}
		if (flag)
		{
			((BaseUnityPlugin)this).Config.Save();
		}
		LoadStore();
		new Harmony("magicorp.valheim.rememberserverpassword").PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Remember Server Password 1.2.4 loaded. Remembered servers: " + Passwords.Count));
	}

	internal static void TryAutoSubmitVisiblePasswordPrompt()
	{
		if ((Object)(object)Instance == (Object)null || !Enabled.Value || !AutoSubmit.Value || Time.unscaledTime < NextPasswordPromptAttempt)
		{
			return;
		}
		try
		{
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				object? obj = AccessTools.Field(typeof(ZNet), "m_passwordDialog")?.GetValue(instance);
				RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null);
				if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy)
				{
					NextPasswordPromptAttempt = Time.unscaledTime + 1f;
					LogInfo("[Flow] Password dialog detected; attempting remembered-password auto-submit fallback.");
					AutoSubmitRememberedPassword(instance, passwordRequired: true);
				}
			}
		}
		catch (Exception ex)
		{
			NextPasswordPromptAttempt = Time.unscaledTime + 1f;
			LogError("Password-dialog watcher failed: " + ex.GetBaseException().Message);
		}
	}

	internal unsafe static void CaptureJoinRequest(ServerJoinData joinData)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		string serverKey = GetServerKey(joinData);
		string[] obj = new string[6]
		{
			"[Flow] Join request: valid=",
			((ServerJoinData)(ref joinData)).IsValid.ToString(),
			", raw=",
			null,
			null,
			null
		};
		ServerJoinData val = joinData;
		obj[3] = ((object)(*(ServerJoinData*)(&val))/*cast due to .constrained prefix*/).ToString();
		obj[4] = ", key=";
		obj[5] = serverKey ?? "<none>";
		LogInfo(string.Concat(obj));
		if (!string.IsNullOrEmpty(serverKey))
		{
			ActiveServerKey = serverKey;
			LogInfo("[Flow] Active server key set: " + serverKey);
		}
	}

	internal unsafe static string GetServerKey(ServerJoinData joinData)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: 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_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Expected I4, but got Unknown
		if (!((ServerJoinData)(ref joinData)).IsValid)
		{
			return null;
		}
		try
		{
			ServerJoinDataType val = (ServerJoinDataType)((AccessTools.Field(typeof(ServerJoinData), "m_type")?.GetValue(joinData) is ServerJoinDataType val2) ? ((int)val2) : 0);
			string text = ((object)(*(ServerJoinData*)(&joinData))/*cast due to .constrained prefix*/).ToString();
			if (string.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			return (val - 1) switch
			{
				0 => "steam/" + text.Trim(), 
				1 => "playfab/" + text.Trim().ToLowerInvariant(), 
				2 => "host/" + text.Trim().ToLowerInvariant(), 
				_ => null, 
			};
		}
		catch (Exception ex)
		{
			LogWarning("Could not identify join request: " + ex.Message);
			return null;
		}
	}

	internal static string GetCurrentServerKey()
	{
		if (!string.IsNullOrEmpty(ActiveServerKey))
		{
			return ActiveServerKey;
		}
		try
		{
			string text = AccessTools.Field(typeof(ZNet), "m_serverHost")?.GetValue(null) as string;
			object obj = AccessTools.Field(typeof(ZNet), "m_serverHostPort")?.GetValue(null);
			object obj2 = AccessTools.Field(typeof(ZNet), "m_serverSteamID")?.GetValue(null);
			string text2 = AccessTools.Field(typeof(ZNet), "m_serverPlayFabPlayerId")?.GetValue(null) as string;
			int num = ((obj is int num2) ? num2 : 0);
			ulong num3 = ((obj2 is ulong num4) ? num4 : 0);
			if (!string.IsNullOrWhiteSpace(text) && num > 0)
			{
				return FormatHostKey(text, num);
			}
			if (num3 != 0L)
			{
				return "steam/" + num3;
			}
			if (!string.IsNullOrWhiteSpace(text2))
			{
				return "playfab/" + text2.Trim().ToLowerInvariant();
			}
		}
		catch (Exception ex)
		{
			LogWarning("Could not identify current server: " + ex.Message);
		}
		return null;
	}

	private static string FormatHostKey(string host, int port)
	{
		host = host.Trim().ToLowerInvariant();
		if (host.IndexOf(':') >= 0 && (!host.StartsWith("[") || !host.EndsWith("]")))
		{
			host = "[" + host + "]";
		}
		return "host/" + host + ":" + port;
	}

	internal static bool TryGetPassword(string serverKey, out string password)
	{
		password = null;
		bool flag = Enabled.Value && AutoSubmit.Value && !string.IsNullOrEmpty(serverKey) && Passwords.TryGetValue(serverKey, out password) && !string.IsNullOrEmpty(password);
		LogInfo("[Flow] Password lookup: key=" + (serverKey ?? "<none>") + ", enabled=" + Enabled.Value + ", autoSubmit=" + AutoSubmit.Value + ", storeCount=" + Passwords.Count + ", result=" + (flag ? "HIT" : "MISS"));
		return flag;
	}

	internal static void CapturePendingPassword(string serverKey, string password)
	{
		bool flag = !string.IsNullOrEmpty(AutoAttemptServerKey) && string.Equals(AutoAttemptServerKey, serverKey, StringComparison.OrdinalIgnoreCase);
		LogInfo("[Flow] Password-entry callback: key=" + (serverKey ?? "<none>") + ", enabled=" + Enabled.Value + ", remember=" + RememberPasswords.Value + ", valuePresent=" + !string.IsNullOrEmpty(password) + ", auto=" + flag);
		if (flag)
		{
			LogInfo("[Flow] Ignoring password-entry capture because this submission came from remembered-password auto-submit.");
		}
		else if (Enabled.Value && RememberPasswords.Value && !string.IsNullOrEmpty(serverKey) && !string.IsNullOrEmpty(password))
		{
			PendingServerKey = serverKey;
			PendingPassword = password;
			LogInfo("[Flow] Manual password captured for " + serverKey + "; awaiting successful connection before saving.");
		}
	}

	internal static void MarkAutoAttempt(string serverKey)
	{
		AutoAttemptServerKey = serverKey;
		LogInfo("[Flow] Remembered password injected for " + serverKey + "; awaiting handshake result.");
	}

	internal static bool AutoSubmitRememberedPassword(ZNet net, bool passwordRequired)
	{
		if (!passwordRequired || (Object)(object)net == (Object)null)
		{
			return false;
		}
		string currentServerKey = GetCurrentServerKey();
		LogInfo("[Flow] Password auto-submit stage: key=" + (currentServerKey ?? "<none>") + ", passwordRequired=" + passwordRequired);
		if (!TryGetPassword(currentServerKey, out var password))
		{
			LogInfo("[Flow] Handshake fallback has no remembered password to submit.");
			return false;
		}
		try
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(ZNet), "OnPasswordEntered", new Type[1] { typeof(string) }, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException("ZNet.OnPasswordEntered(string)");
			}
			AccessTools.Field(typeof(ZNet), "m_serverPassword")?.SetValue(net, password);
			SetServerPassword(password, "password prompt fallback");
			MarkAutoAttempt(currentServerKey);
			methodInfo.Invoke(net, new object[1] { password });
			LogInfo("[Flow] Remembered password auto-submitted via ZNet.OnPasswordEntered reflection fallback.");
			return true;
		}
		catch (Exception ex)
		{
			LogError("Remembered password auto-submit fallback failed: " + ex.GetBaseException().Message);
			return false;
		}
	}

	internal static void AutoSelectLastCharacter(FejdStartup startup)
	{
		if (!QuickConnectPending || (Object)(object)startup == (Object)null)
		{
			return;
		}
		try
		{
			string text = PlatformPrefs.GetString("profile", string.Empty);
			LogInfo("[Flow] Quick Connect character stage: lastProfile=" + (string.IsNullOrEmpty(text) ? "<none>" : text));
			if (string.IsNullOrEmpty(text))
			{
				LogWarning("Quick Connect could not skip character selection because Valheim has no last-used profile.");
				QuickConnectPending = false;
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(FejdStartup), "SetSelectedProfile", new Type[1] { typeof(string) }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(FejdStartup), "OnCharacterStart", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				throw new MissingMethodException("FejdStartup character selection methods");
			}
			methodInfo.Invoke(startup, new object[1] { text });
			int num = (int)(AccessTools.Field(typeof(FejdStartup), "m_profileIndex")?.GetValue(startup) ?? ((object)(-1)));
			IList list = AccessTools.Field(typeof(FejdStartup), "m_profiles")?.GetValue(startup) as IList;
			if (num < 0 || list == null || num >= list.Count)
			{
				LogWarning("Quick Connect could not find the last-used profile '" + text + "'; leaving character selection open.");
				QuickConnectPending = false;
				return;
			}
			LogInfo("[Flow] Quick Connect selected last-used profile '" + text + "' at index " + num + "; starting join.");
			QuickConnectPending = false;
			methodInfo2.Invoke(startup, null);
		}
		catch (Exception ex)
		{
			QuickConnectPending = false;
			LogError("Quick Connect character auto-select failed: " + ex.GetBaseException().Message);
		}
	}

	internal static void RecordSuccessfulConnection()
	{
		string currentServerKey = GetCurrentServerKey();
		LogInfo("[Flow] Connection success path: key=" + (currentServerKey ?? "<none>") + ", autoAttempt=" + (AutoAttemptServerKey ?? "<none>") + ", pending=" + (PendingServerKey ?? "<none>"));
		if (!string.IsNullOrEmpty(currentServerKey) && LastServer.Value != currentServerKey)
		{
			LastServer.Value = currentServerKey;
			((BaseUnityPlugin)Instance).Config.Save();
		}
		CommitPendingPassword();
	}

	internal static void CommitPendingPassword()
	{
		AutoAttemptServerKey = null;
		SetServerPassword(string.Empty, "clear transient password");
		if (!string.IsNullOrEmpty(PendingServerKey) && !string.IsNullOrEmpty(PendingPassword))
		{
			string value;
			bool num = !Passwords.TryGetValue(PendingServerKey, out value) || value != PendingPassword;
			Passwords[PendingServerKey] = PendingPassword;
			if (num)
			{
				SaveStore();
				LogInfo("Remembered password for " + PendingServerKey);
			}
			PendingServerKey = null;
			PendingPassword = null;
		}
	}

	internal static void HandleWrongPassword()
	{
		string currentServerKey = GetCurrentServerKey();
		LogInfo("[Flow] Wrong-password path: key=" + (currentServerKey ?? "<none>") + ", autoAttempt=" + (AutoAttemptServerKey ?? "<none>"));
		PendingServerKey = null;
		PendingPassword = null;
		if (!string.IsNullOrEmpty(AutoAttemptServerKey) && string.Equals(AutoAttemptServerKey, currentServerKey, StringComparison.OrdinalIgnoreCase) && Passwords.Remove(currentServerKey))
		{
			SaveStore();
			LogWarning("Stored password was rejected and removed for " + currentServerKey + ". The next join will prompt normally.");
		}
		AutoAttemptServerKey = null;
		SetServerPassword(string.Empty, "clear transient password");
	}

	internal static void ClearTransientState()
	{
		PendingServerKey = null;
		PendingPassword = null;
		AutoAttemptServerKey = null;
		QuickConnectPending = false;
		NextPasswordPromptAttempt = 0f;
	}

	internal static void EnsureQuickConnectButton(FejdStartup startup)
	{
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: 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_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0187: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Expected O, but got Unknown
		if (!ShowQuickConnectButton.Value || (Object)(object)startup == (Object)null)
		{
			return;
		}
		try
		{
			object? obj = AccessTools.Field(typeof(FejdStartup), "m_menuList")?.GetValue(startup);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Button val2 = null;
			Button[] componentsInChildren = val.GetComponentsInChildren<Button>(true);
			Button val3 = null;
			Button[] array = componentsInChildren;
			foreach (Button val4 in array)
			{
				if (!((Object)(object)val4 == (Object)null))
				{
					if (((Object)((Component)val4).gameObject).name == "MagiCorp_RememberServerPassword_QuickConnect")
					{
						val2 = val4;
					}
					else if ((Object)(object)val3 == (Object)null && ((Component)val4).gameObject.activeSelf)
					{
						val3 = val4;
					}
				}
			}
			if ((Object)(object)val2 == (Object)null)
			{
				if ((Object)(object)val3 == (Object)null)
				{
					LogWarning("Could not add Quick Connect button because the main menu button template was not found.");
					return;
				}
				GameObject val5 = Object.Instantiate<GameObject>(((Component)val3).gameObject, ((Component)val3).transform.parent);
				((Object)val5).name = "MagiCorp_RememberServerPassword_QuickConnect";
				Button component = val5.GetComponent<Button>();
				if ((Object)(object)component == (Object)null)
				{
					Object.Destroy((Object)(object)val5);
					return;
				}
				Graphic targetGraphic = ((Selectable)component).targetGraphic;
				Transition transition = ((Selectable)component).transition;
				ColorBlock colors = ((Selectable)component).colors;
				SpriteState spriteState = ((Selectable)component).spriteState;
				Navigation navigation = ((Selectable)component).navigation;
				Object.DestroyImmediate((Object)(object)component);
				val2 = val5.AddComponent<Button>();
				((Selectable)val2).targetGraphic = targetGraphic;
				((Selectable)val2).transition = transition;
				((Selectable)val2).colors = colors;
				((Selectable)val2).spriteState = spriteState;
				((Selectable)val2).navigation = navigation;
				((UnityEvent)val2.onClick).AddListener((UnityAction)delegate
				{
					QuickConnect(startup);
				});
				PlaceQuickConnectButton(val2, val3);
			}
			ServerJoinData joinData;
			bool flag = (((Selectable)val2).interactable = TryBuildJoinData(LastServer.Value, out joinData));
			TMP_Text componentInChildren = ((Component)val2).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = (flag ? ("Quick Connect: " + DisplayServer(LastServer.Value)) : "Quick Connect: No recent server");
			}
		}
		catch (Exception ex)
		{
			LogWarning("Could not create Quick Connect button: " + ex.Message);
		}
	}

	private static void PlaceQuickConnectButton(Button button, Button template)
	{
		//IL_0087: 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_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		Transform transform = ((Component)button).transform;
		RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		Transform parent = ((Component)button).transform.parent;
		RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
		if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
		{
			return;
		}
		if ((Object)(object)((Component)val2).GetComponent<LayoutGroup>() != (Object)null)
		{
			((Component)button).transform.SetSiblingIndex(Math.Min(((Component)template).transform.GetSiblingIndex() + 1, ((Transform)val2).childCount - 1));
			LayoutRebuilder.ForceRebuildLayoutImmediate(val2);
			return;
		}
		Transform transform2 = ((Component)template).transform;
		RectTransform val3 = (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null);
		float num = ((val3 != null) ? val3.anchoredPosition.y : val.anchoredPosition.y);
		Rect rect = val.rect;
		float num2 = Math.Max(((Rect)(ref rect)).height, 35f);
		Button[] componentsInChildren = ((Component)val2).GetComponentsInChildren<Button>(true);
		foreach (Button val4 in componentsInChildren)
		{
			if (!((Object)(object)val4 == (Object)(object)button) && !((Object)(object)((Component)val4).transform.parent != (Object)(object)val2))
			{
				Transform transform3 = ((Component)val4).transform;
				RectTransform val5 = (RectTransform)(object)((transform3 is RectTransform) ? transform3 : null);
				if (val5 != null)
				{
					num = Math.Min(num, val5.anchoredPosition.y);
				}
			}
		}
		val.anchoredPosition = new Vector2(val.anchoredPosition.x, num - num2 - 8f);
	}

	private unsafe static void QuickConnect(FejdStartup startup)
	{
		//IL_006b: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
		LogInfo("[Flow] Quick Connect clicked: lastServer=" + (LastServer.Value ?? "<none>"));
		if (!TryBuildJoinData(LastServer.Value, out var joinData))
		{
			LogWarning("Quick Connect has no valid last server.");
			return;
		}
		try
		{
			string[] obj = new string[6]
			{
				"[Flow] Quick Connect join data: valid=",
				((ServerJoinData)(ref joinData)).IsValid.ToString(),
				", raw=",
				null,
				null,
				null
			};
			ServerJoinData val = joinData;
			obj[3] = ((object)(*(ServerJoinData*)(&val))/*cast due to .constrained prefix*/).ToString();
			obj[4] = ", lastProfile=";
			obj[5] = PlatformPrefs.GetString("profile", string.Empty);
			LogInfo(string.Concat(obj));
			QuickConnectPending = true;
			SetServerPassword(string.Empty, "Quick Connect pre-join clear");
			MethodInfo methodInfo = AccessTools.Method(typeof(FejdStartup), "ProceedJoinRequest", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException("FejdStartup.ProceedJoinRequest");
			}
			LogInfo("[Flow] Invoking FejdStartup.ProceedJoinRequest via reflection.");
			methodInfo.Invoke(startup, new object[1] { joinData });
			LogInfo("Quick Connect requested for " + DisplayServer(LastServer.Value));
		}
		catch (Exception ex)
		{
			QuickConnectPending = false;
			LogError("Quick Connect failed: " + ex.GetBaseException().Message);
		}
	}

	private static bool TryBuildJoinData(string serverKey, out ServerJoinData joinData)
	{
		//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_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//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_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		joinData = ServerJoinData.None;
		if (string.IsNullOrWhiteSpace(serverKey))
		{
			return false;
		}
		try
		{
			if (serverKey.StartsWith("steam/", StringComparison.OrdinalIgnoreCase))
			{
				if (!ulong.TryParse(serverKey.Substring(6), out var result) || result == 0L)
				{
					return false;
				}
				joinData = new ServerJoinData(new ServerJoinDataSteamUser(result));
				return ((ServerJoinData)(ref joinData)).IsValid;
			}
			if (serverKey.StartsWith("playfab/", StringComparison.OrdinalIgnoreCase))
			{
				string text = serverKey.Substring(8);
				if (string.IsNullOrWhiteSpace(text))
				{
					return false;
				}
				joinData = new ServerJoinData(new ServerJoinDataPlayFabUser(text));
				return ((ServerJoinData)(ref joinData)).IsValid;
			}
			if (serverKey.StartsWith("host/", StringComparison.OrdinalIgnoreCase))
			{
				string text2 = serverKey.Substring(5);
				string text3;
				string s;
				if (text2.StartsWith("[", StringComparison.Ordinal))
				{
					int num = text2.LastIndexOf("]:", StringComparison.Ordinal);
					if (num <= 1)
					{
						return false;
					}
					text3 = text2.Substring(1, num - 1);
					s = text2.Substring(num + 2);
				}
				else
				{
					int num2 = text2.LastIndexOf(':');
					if (num2 <= 0)
					{
						return false;
					}
					text3 = text2.Substring(0, num2);
					s = text2.Substring(num2 + 1);
				}
				if (!ushort.TryParse(s, out var result2) || result2 == 0 || string.IsNullOrWhiteSpace(text3))
				{
					return false;
				}
				joinData = new ServerJoinData(new ServerJoinDataDedicated(text3, result2));
				return ((ServerJoinData)(ref joinData)).IsValid;
			}
		}
		catch (Exception ex)
		{
			LogWarning("Could not parse last server identity: " + ex.Message);
		}
		return false;
	}

	private static string DisplayServer(string serverKey)
	{
		if (string.IsNullOrWhiteSpace(serverKey))
		{
			return "No recent server";
		}
		int num = serverKey.IndexOf('/');
		string text = ((num >= 0 && num + 1 < serverKey.Length) ? serverKey.Substring(num + 1) : serverKey);
		if (text.Length > 42)
		{
			return text.Substring(0, 39) + "...";
		}
		return text;
	}

	private static void LoadStore()
	{
		Passwords.Clear();
		if (!File.Exists(StorePath))
		{
			return;
		}
		try
		{
			string[] array = File.ReadAllLines(StorePath);
			foreach (string text in array)
			{
				if (string.IsNullOrWhiteSpace(text))
				{
					continue;
				}
				string[] array2 = text.Split(new char[1] { '\t' }, 2);
				if (array2.Length == 2)
				{
					string text2 = Encoding.UTF8.GetString(Convert.FromBase64String(array2[0]));
					string text3 = Decrypt(array2[1]);
					if (!string.IsNullOrEmpty(text2) && text3 != null)
					{
						Passwords[text2] = text3;
					}
				}
			}
		}
		catch (Exception ex)
		{
			LogWarning("Could not read password store: " + ex.Message);
		}
	}

	private static void SaveStore()
	{
		try
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, string> password in Passwords)
			{
				string text = Convert.ToBase64String(Encoding.UTF8.GetBytes(password.Key));
				list.Add(text + "\t" + Encrypt(password.Value));
			}
			File.WriteAllLines(StorePath, list.ToArray());
		}
		catch (Exception ex)
		{
			LogError("Could not save password store: " + ex.Message);
		}
	}

	private static string Encrypt(string value)
	{
		byte[] bytes = Encoding.UTF8.GetBytes(value);
		try
		{
			byte[] inArray = ProtectedData.Protect(bytes, DpapiEntropy, (DataProtectionScope)0);
			return "D:" + Convert.ToBase64String(inArray);
		}
		catch (PlatformNotSupportedException)
		{
			return EncryptPortable(bytes);
		}
		catch (CryptographicException)
		{
			return EncryptPortable(bytes);
		}
	}

	private static string Decrypt(string value)
	{
		if (value.StartsWith("D:", StringComparison.Ordinal))
		{
			byte[] array = Convert.FromBase64String(value.Substring(2));
			try
			{
				return Encoding.UTF8.GetString(ProtectedData.Unprotect(array, DpapiEntropy, (DataProtectionScope)0));
			}
			catch (PlatformNotSupportedException)
			{
				return null;
			}
			catch (CryptographicException)
			{
				return null;
			}
		}
		if (value.StartsWith("A:", StringComparison.Ordinal))
		{
			return DecryptPortable(value.Substring(2));
		}
		return null;
	}

	private static string EncryptPortable(byte[] clear)
	{
		byte[] orCreatePortableKey = GetOrCreatePortableKey();
		using Aes aes = Aes.Create();
		aes.Key = orCreatePortableKey;
		aes.GenerateIV();
		using ICryptoTransform cryptoTransform = aes.CreateEncryptor();
		byte[] array = cryptoTransform.TransformFinalBlock(clear, 0, clear.Length);
		byte[] array2 = new byte[aes.IV.Length + array.Length];
		Buffer.BlockCopy(aes.IV, 0, array2, 0, aes.IV.Length);
		Buffer.BlockCopy(array, 0, array2, aes.IV.Length, array.Length);
		return "A:" + Convert.ToBase64String(array2);
	}

	private static string DecryptPortable(string value)
	{
		try
		{
			byte[] orCreatePortableKey = GetOrCreatePortableKey();
			byte[] array = Convert.FromBase64String(value);
			using Aes aes = Aes.Create();
			int num = aes.BlockSize / 8;
			if (array.Length <= num)
			{
				return null;
			}
			byte[] array2 = new byte[num];
			byte[] array3 = new byte[array.Length - num];
			Buffer.BlockCopy(array, 0, array2, 0, num);
			Buffer.BlockCopy(array, num, array3, 0, array3.Length);
			aes.Key = orCreatePortableKey;
			aes.IV = array2;
			using ICryptoTransform cryptoTransform = aes.CreateDecryptor();
			byte[] bytes = cryptoTransform.TransformFinalBlock(array3, 0, array3.Length);
			return Encoding.UTF8.GetString(bytes);
		}
		catch
		{
			return null;
		}
	}

	private static byte[] GetOrCreatePortableKey()
	{
		if (File.Exists(KeyPath))
		{
			return Convert.FromBase64String(File.ReadAllText(KeyPath).Trim());
		}
		byte[] array = new byte[32];
		using (RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create())
		{
			randomNumberGenerator.GetBytes(array);
		}
		File.WriteAllText(KeyPath, Convert.ToBase64String(array));
		LogWarning("OS protected storage was unavailable. Using a per-install AES key in the BepInEx config directory instead.");
		return array;
	}

	internal static bool SetServerPassword(string value, string reason)
	{
		value = value ?? string.Empty;
		try
		{
			MethodInfo methodInfo = AccessTools.Property(typeof(FejdStartup), "ServerPassword")?.GetSetMethod(nonPublic: true);
			if (methodInfo != null)
			{
				methodInfo.Invoke(null, new object[1] { value });
				LogInfo("[Flow] ServerPassword reflection setter succeeded: reason=" + reason + ", state=" + ((value.Length == 0) ? "empty" : "present"));
				return true;
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(FejdStartup), "<ServerPassword>k__BackingField");
			if (fieldInfo != null)
			{
				fieldInfo.SetValue(null, value);
				LogInfo("[Flow] ServerPassword backing-field fallback succeeded: reason=" + reason + ", state=" + ((value.Length == 0) ? "empty" : "present"));
				return true;
			}
			LogError("Could not set FejdStartup.ServerPassword: setter and backing field were not found. Reason=" + reason);
		}
		catch (Exception ex)
		{
			LogError("Could not set FejdStartup.ServerPassword via reflection: reason=" + reason + ", error=" + ex.GetBaseException().Message);
		}
		return false;
	}

	private static void TryDelete(string path)
	{
		try
		{
			if (File.Exists(path))
			{
				File.Delete(path);
			}
		}
		catch (Exception ex)
		{
			LogWarning("Could not delete " + path + ": " + ex.Message);
		}
	}

	internal static void LogFlow(string message)
	{
	}

	private static void LogInfo(string message)
	{
		if ((Object)(object)Instance != (Object)null && !message.StartsWith("[Flow]", StringComparison.Ordinal))
		{
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)message);
		}
	}

	private static void LogWarning(string message)
	{
		if ((Object)(object)Instance != (Object)null)
		{
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)message);
		}
	}

	private static void LogError(string message)
	{
		if ((Object)(object)Instance != (Object)null)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)message);
		}
	}
}
[HarmonyPatch(typeof(FejdStartup), "SetupGui")]
internal static class MainMenuPatch
{
	private static void Postfix(FejdStartup __instance)
	{
		Plugin.EnsureQuickConnectButton(__instance);
	}
}
[HarmonyPatch(typeof(FejdStartup), "ProceedJoinRequest")]
internal static class JoinRequestPatch
{
	private static void Prefix(ServerJoinData joinData)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		Plugin.CaptureJoinRequest(joinData);
	}
}
[HarmonyPatch(typeof(FejdStartup), "ShowCharacterSelection")]
internal static class CharacterSelectionPatch
{
	private static void Postfix(FejdStartup __instance)
	{
		Plugin.AutoSelectLastCharacter(__instance);
	}
}
[HarmonyPatch(typeof(ZNet), "RPC_ClientHandshake", new Type[]
{
	typeof(ZRpc),
	typeof(bool),
	typeof(string)
})]
internal static class ClientHandshakePatch
{
	private static void Prefix(bool __1)
	{
		string currentServerKey = Plugin.GetCurrentServerKey();
		Plugin.LogFlow("RPC_ClientHandshake prefix: key=" + (currentServerKey ?? "<none>") + ", passwordRequired=" + __1);
		if (__1 && Plugin.TryGetPassword(currentServerKey, out var password))
		{
			if (Plugin.SetServerPassword(password, "RPC_ClientHandshake remembered-password injection"))
			{
				Plugin.MarkAutoAttempt(currentServerKey);
			}
		}
		else if (__1)
		{
			Plugin.LogFlow("RPC_ClientHandshake has no remembered password to inject.");
		}
	}
}
[HarmonyPatch(typeof(ZNet), "OnPasswordEntered")]
internal static class PasswordEnteredPatch
{
	private static void Prefix(string __0)
	{
		Plugin.CapturePendingPassword(Plugin.GetCurrentServerKey(), __0);
	}
}
[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
internal static class PeerInfoPatch
{
	private static void Postfix(ZNet __instance)
	{
		if (!__instance.IsServer())
		{
			Plugin.RecordSuccessfulConnection();
		}
	}
}
[HarmonyPatch(typeof(ZNet), "RPC_Error")]
internal static class RpcErrorPatch
{
	private static void Postfix(int __1)
	{
		if (__1 == 6)
		{
			Plugin.HandleWrongPassword();
		}
	}
}
[HarmonyPatch(typeof(ZNet), "ResetServerHost")]
internal static class ResetServerHostPatch
{
	private static void Prefix()
	{
		Plugin.ClearTransientState();
	}
}