Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AutoServerPassword v1.0.2
plugins\AutoServerPassword.dll
Decompiled 2 weeks agousing 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.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GUIFramework; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: IgnoresAccessChecksTo("gui_framework")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("Allifreyr")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: AssemblyInformationalVersion("1.0.2")] [assembly: AssemblyProduct("AutoServerPassword")] [assembly: AssemblyTitle("AutoServerPassword")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.2.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 AutoServerPassword { public static class PasswordManagerGui { [CompilerGenerated] private static class <>O { public static WindowFunction <0>__DrawWindowContents; } private const int WindowId = 4281168; private static Rect _windowRect = new Rect(70f, 90f, 540f, 440f); private static Vector2 _scrollPosition; private static string _filterText = ""; private static readonly HashSet<string> _revealedPasswords = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static bool _confirmClearAll; private static string _statusMessage = ""; private static float _statusMessageTimer; public static bool Visible { get; set; } public static void Toggle() { Visible = !Visible; if (Visible) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; _confirmClearAll = false; _statusMessage = ""; } } public static void Draw() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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_00ab: Expected O, but got Unknown if (Visible) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; ((Rect)(ref _windowRect)).x = Mathf.Clamp(((Rect)(ref _windowRect)).x, 0f, Mathf.Max(0f, (float)Screen.width - ((Rect)(ref _windowRect)).width)); ((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, 0f, Mathf.Max(0f, (float)Screen.height - ((Rect)(ref _windowRect)).height)); Rect windowRect = _windowRect; object obj = <>O.<0>__DrawWindowContents; if (obj == null) { WindowFunction val = DrawWindowContents; <>O.<0>__DrawWindowContents = val; obj = (object)val; } _windowRect = GUI.Window(4281168, windowRect, (WindowFunction)obj, "AutoServerPassword — Saved Server Passwords"); } } private static void DrawWindowContents(int id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) if ((int)Event.current.type == 4 && (int)Event.current.keyCode == 27) { Visible = false; Event.current.Use(); return; } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width - 30f, 25f)); if (GUI.Button(new Rect(((Rect)(ref _windowRect)).width - 25f, 3f, 22f, 20f), "X")) { Visible = false; return; } GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); if (!string.IsNullOrEmpty(_statusMessage)) { _statusMessageTimer -= Time.unscaledDeltaTime; if (_statusMessageTimer <= 0f) { _statusMessage = ""; } else { GUILayout.Space(2f); GUI.color = Color.cyan; GUILayout.Label(_statusMessage, Array.Empty<GUILayoutOption>()); GUI.color = Color.white; } } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); bool flag = GUILayout.Toggle(PluginConfig.AutoSubmit.Value, "Auto-Submit", Array.Empty<GUILayoutOption>()); if (flag != PluginConfig.AutoSubmit.Value) { PluginConfig.AutoSubmit.Value = flag; } bool flag2 = GUILayout.Toggle(PluginConfig.HidePasswordDialog.Value, "Hide Dialog", Array.Empty<GUILayoutOption>()); if (flag2 != PluginConfig.HidePasswordDialog.Value) { PluginConfig.HidePasswordDialog.Value = flag2; } bool flag3 = GUILayout.Toggle(PluginConfig.AutoSave.Value, "Auto-Save", Array.Empty<GUILayoutOption>()); if (flag3 != PluginConfig.AutoSave.Value) { PluginConfig.AutoSave.Value = flag3; } GUILayout.EndHorizontal(); GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Search:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); _filterText = GUILayout.TextField(_filterText, Array.Empty<GUILayoutOption>()); if (!string.IsNullOrEmpty(_filterText) && GUILayout.Button("Clear", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) })) { _filterText = ""; } GUILayout.EndHorizontal(); GUILayout.Space(4f); IReadOnlyList<SavedServerEntry> readOnlyList = ServerPasswordManager.GetAllSavedServers(); if (!string.IsNullOrWhiteSpace(_filterText)) { readOnlyList = readOnlyList.Where((SavedServerEntry s) => (!string.IsNullOrEmpty(s.ServerName) && s.ServerName.IndexOf(_filterText, StringComparison.OrdinalIgnoreCase) >= 0) || (!string.IsNullOrEmpty(s.ServerKey) && s.ServerKey.IndexOf(_filterText, StringComparison.OrdinalIgnoreCase) >= 0) || (s.Aliases != null && s.Aliases.Any((string a) => a.IndexOf(_filterText, StringComparison.OrdinalIgnoreCase) >= 0))).ToList(); } _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(260f) }); if (readOnlyList.Count == 0) { GUILayout.Space(20f); GUILayout.Label("<color=#CCCCCC><i>No saved server passwords found.\nWhen you connect to a password-protected server, it will be automatically saved here.</i></color>", Array.Empty<GUILayoutOption>()); } else { for (int num = 0; num < readOnlyList.Count; num++) { DrawServerCard(readOnlyList[num], num); } } GUILayout.EndScrollView(); GUILayout.Space(6f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (!_confirmClearAll) { if (GUILayout.Button("Clear All", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { _confirmClearAll = true; } } else { GUI.color = Color.red; if (GUILayout.Button("CONFIRM CLEAR?", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) })) { ServerPasswordManager.ClearAllPasswords(); _confirmClearAll = false; SetStatus("All saved server passwords cleared."); } GUI.color = Color.white; if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { _confirmClearAll = false; } } GUILayout.FlexibleSpace(); if (GUILayout.Button("Reload", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { ServerPasswordManager.LoadFromDisk(); SetStatus("Reloaded passwords from disk."); } if (GUILayout.Button("Close", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { Visible = false; } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private static void DrawServerCard(SavedServerEntry entry, int index) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(GUI.skin.box, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); string text = ((!string.IsNullOrEmpty(entry.ServerName)) ? entry.ServerName : entry.ServerKey); GUILayout.Label("<b><color=#FFD700>" + text + "</color></b>", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.color = new Color(1f, 0.4f, 0.4f, 1f); if (GUILayout.Button("Delete", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { ServerPasswordManager.RemoveSavedPassword(entry.ServerKey); SetStatus("Removed saved password for " + text + "."); } GUI.color = Color.white; GUILayout.EndHorizontal(); GUILayout.Label("<color=#AAAAAA>Key: " + entry.ServerKey + "</color>", Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); bool flag = _revealedPasswords.Contains(entry.ServerKey); string text2 = (flag ? entry.Password : new string('•', Math.Max(6, entry.Password.Length))); GUILayout.Label("Password:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.TextField(text2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) }); if (GUILayout.Button(flag ? "Hide" : "Show", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) })) { if (flag) { _revealedPasswords.Remove(entry.ServerKey); } else { _revealedPasswords.Add(entry.ServerKey); } } if (GUILayout.Button("Copy", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) })) { GUIUtility.systemCopyBuffer = entry.Password; SetStatus("Password copied to clipboard!"); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Label($"<color=#888888><size=10>Last used: {entry.LastUsed:yyyy-MM-dd HH:mm}</size></color>", Array.Empty<GUILayoutOption>()); GUILayout.EndVertical(); GUILayout.Space(2f); } private static void SetStatus(string message) { _statusMessage = message; _statusMessageTimer = 3.5f; } } [HarmonyPatch(typeof(FejdStartup), "JoinServer")] public static class Patch_FejdStartup_JoinServer { [HarmonyPrefix] public unsafe static void Prefix(FejdStartup __instance) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.Enabled.Value) { return; } try { ServerJoinData serverToJoin = __instance.GetServerToJoin(); ServerPasswordManager.LastJoiningServerData = ((object)(*(ServerJoinData*)(&serverToJoin))/*cast due to .constrained prefix*/).ToString(); ServerPasswordManager.LastJoiningServerName = MultiBackendMatchmaking.GetServerName(serverToJoin); } catch (Exception arg) { Plugin.Log.LogError((object)$"[AutoServerPassword] Error capturing joining server data: {arg}"); } } } [HarmonyPatch(typeof(ZNet), "RPC_ClientHandshake")] public static class Patch_ZNet_RPC_ClientHandshake { [HarmonyPrefix] public static bool Prefix(ZNet __instance, ZRpc rpc, bool needPassword, string serverPasswordSalt) { if (__instance.IsServer() || !PluginConfig.Enabled.Value) { return true; } if (!needPassword) { return true; } try { ServerIdentifiers serverIdentifiers = ServerPasswordManager.CollectCurrentIdentifiers(__instance, rpc); if (PluginConfig.AutoSubmit.Value && ServerPasswordManager.TryFindSavedPassword(serverIdentifiers, out var matchedEntry, out var matchedKey)) { string serverKey = matchedEntry.ServerKey; if (!ServerPasswordManager.IsFailedThisSession(serverKey)) { string text = ((!string.IsNullOrEmpty(matchedEntry.ServerName)) ? matchedEntry.ServerName : serverKey); Plugin.Log.LogInfo((object)("[AutoServerPassword] Server '" + text + "' requires a password. Found saved password (matched key: '" + matchedKey + "'). Auto-submitting...")); ZNet.m_serverPasswordSalt = serverPasswordSalt; ((Component)__instance.m_connectingDialog).gameObject.SetActive(false); ServerPasswordManager.RecordActiveAttempt(serverKey, matchedEntry.Password, isAuto: true, serverIdentifiers); if (PluginConfig.HidePasswordDialog.Value) { ((Component)__instance.m_passwordDialog).gameObject.SetActive(false); } else { ((Component)__instance.m_passwordDialog).gameObject.SetActive(true); GuiInputField componentInChildren = ((Component)__instance.m_passwordDialog).GetComponentInChildren<GuiInputField>(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_InputField)componentInChildren).text = matchedEntry.Password; } } __instance.m_tempPasswordRPC = rpc; __instance.OnPasswordEntered(matchedEntry.Password); return false; } Plugin.Log.LogWarning((object)("[AutoServerPassword] Saved password for '" + serverKey + "' previously failed this session. Prompting for new password.")); } else { Plugin.Log.LogInfo((object)"[AutoServerPassword] Server requires password, but no saved password was found. Displaying password prompt."); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[AutoServerPassword] Error in RPC_ClientHandshake: {arg}"); } return true; } } [HarmonyPatch(typeof(ZNet), "OnPasswordEntered")] public static class Patch_ZNet_OnPasswordEntered { [HarmonyPrefix] public static void Prefix(ZNet __instance, string pwd) { if (__instance.IsServer() || !PluginConfig.Enabled.Value) { return; } try { if (!string.IsNullOrEmpty(pwd) && !ServerPasswordManager.HasActiveAutoAttempt) { ServerIdentifiers serverIdentifiers = ServerPasswordManager.CollectCurrentIdentifiers(__instance, __instance.m_tempPasswordRPC); string primaryServerKey = serverIdentifiers.PrimaryServerKey; string text = ((!string.IsNullOrEmpty(serverIdentifiers.ServerName)) ? serverIdentifiers.ServerName : primaryServerKey); Plugin.Log.LogInfo((object)("[AutoServerPassword] User entered password for '" + text + "'. Waiting to confirm connection...")); ServerPasswordManager.RecordActiveAttempt(primaryServerKey, pwd, isAuto: false, serverIdentifiers); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[AutoServerPassword] Error in OnPasswordEntered: {arg}"); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] public static class Patch_ZNet_RPC_PeerInfo { [HarmonyPostfix] public static void Postfix(ZNet __instance, ZRpc rpc) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 if (__instance.IsServer() || !PluginConfig.Enabled.Value) { return; } try { if ((int)ZNet.GetConnectionStatus() == 2) { ServerPasswordManager.OnConnectionSuccess(__instance, rpc); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[AutoServerPassword] Error in RPC_PeerInfo: {arg}"); } } } [HarmonyPatch(typeof(ZNet), "RPC_Error")] public static class Patch_ZNet_RPC_Error { [HarmonyPrefix] public static void Prefix(ZNet __instance, ZRpc rpc, int error) { if (__instance.IsServer() || !PluginConfig.Enabled.Value) { return; } try { if (error == 6) { ServerPasswordManager.OnPasswordError(__instance, rpc); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[AutoServerPassword] Error in RPC_Error: {arg}"); } } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] public static class Patch_FejdStartup_ShowConnectError { [HarmonyPostfix] public static void Postfix(FejdStartup __instance, ConnectionStatus statusOverride) { if (!PluginConfig.Enabled.Value) { return; } try { if (ServerPasswordManager.WasAutoPasswordPurged && (Object)(object)__instance.m_connectionFailedError != (Object)null) { string text = ((!string.IsNullOrEmpty(ServerPasswordManager.PurgedServerName)) ? ServerPasswordManager.PurgedServerName : "the server"); TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n\n<color=#FFCC00>AutoServerPassword: Saved password for " + text + " was incorrect (or changed) and has been removed.\nClick Join to enter the new password.</color>"; ServerPasswordManager.WasAutoPasswordPurged = false; ServerPasswordManager.PurgedServerName = null; } } catch (Exception arg) { Plugin.Log.LogError((object)$"[AutoServerPassword] Error in ShowConnectError: {arg}"); } } } [HarmonyPatch(typeof(ServerListElement), "UpdateTextAndIcons")] public static class Patch_ServerListElement_UpdateTextAndIcons { private static readonly Color SavedPasswordLockColor = new Color(0.35f, 1f, 0.45f, 1f); [HarmonyPostfix] public static void Postfix(ServerListElement __instance, ref ServerListEntryData serverEntry) { //IL_0043: 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_0059: Unknown result type (might be due to invalid IL or missing references) if (!PluginConfig.Enabled.Value || !PluginConfig.HighlightSavedInServerList.Value) { return; } try { if (!((ServerListEntryData)(ref serverEntry)).IsPasswordProtected || !((Object)(object)__instance.m_private != (Object)null)) { return; } Image component = ((Component)__instance.m_private).GetComponent<Image>(); if (ServerPasswordManager.HasSavedPassword(serverEntry.m_joinData)) { if ((Object)(object)component != (Object)null) { ((Graphic)component).color = SavedPasswordLockColor; } if ((Object)(object)__instance.m_tooltip != (Object)null && !string.IsNullOrEmpty(__instance.m_tooltip.m_text) && !__instance.m_tooltip.m_text.Contains("Password Saved")) { UITooltip tooltip = __instance.m_tooltip; tooltip.m_text += "\n<color=#55FF55>Password Saved (Auto-login enabled)</color>"; } } else if ((Object)(object)component != (Object)null) { ((Graphic)component).color = Color.white; } } catch { } } } [HarmonyPatch(typeof(Game), "Update")] public static class Patch_Game_Update { [HarmonyPostfix] public static void Postfix() { if (PluginConfig.Enabled.Value) { ServerPasswordManager.TryDisplayQueuedNotification(); } } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] public static class Patch_ZNet_OnDestroy { [HarmonyPostfix] public static void Postfix() { ServerPasswordManager.ClearActiveAttempt(); } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] public static class Patch_Terminal_InitTerminal { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__HandleSavedPasswordsCommand; } [HarmonyPostfix] public static void Postfix() { //IL_002f: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown object obj = <>O.<0>__HandleSavedPasswordsCommand; if (obj == null) { ConsoleEvent val = HandleSavedPasswordsCommand; <>O.<0>__HandleSavedPasswordsCommand = val; obj = (object)val; } new ConsoleCommand("savedpasswords", "Manage saved server passwords. Subcommands: list, list --show, remove <name/key>, clear", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static void HandleSavedPasswordsCommand(ConsoleEventArgs args) { Terminal context = args.Context; if ((Object)(object)context == (Object)null) { return; } if (args.Length < 2 || string.Equals(args[1], "list", StringComparison.OrdinalIgnoreCase)) { bool flag = args.Length >= 3 && string.Equals(args[2], "--show", StringComparison.OrdinalIgnoreCase); IReadOnlyList<SavedServerEntry> allSavedServers = ServerPasswordManager.GetAllSavedServers(); if (allSavedServers.Count == 0) { context.AddString("AutoServerPassword: No saved server passwords found."); return; } context.AddString($"AutoServerPassword: {allSavedServers.Count} saved server password(s):"); for (int i = 0; i < allSavedServers.Count; i++) { SavedServerEntry savedServerEntry = allSavedServers[i]; string text = (flag ? savedServerEntry.Password : MaskPassword(savedServerEntry.Password)); string text2 = ((!string.IsNullOrEmpty(savedServerEntry.ServerName)) ? ("\"" + savedServerEntry.ServerName + "\"") : "(unnamed)"); context.AddString($"[{i + 1}] {text2} (Key: {savedServerEntry.ServerKey}) -> Password: [{text}] (Last used: {savedServerEntry.LastUsed:yyyy-MM-dd HH:mm})"); } } else if (string.Equals(args[1], "remove", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 3) { context.AddString("Usage: savedpasswords remove <server_name_or_key>"); return; } string text3 = string.Join(" ", args.Args.Skip(2)); if (ServerPasswordManager.RemoveSavedPassword(text3)) { context.AddString("AutoServerPassword: Removed saved password for '" + text3 + "'."); } else { context.AddString("AutoServerPassword: No saved password found matching '" + text3 + "'."); } } else if (string.Equals(args[1], "clear", StringComparison.OrdinalIgnoreCase)) { ServerPasswordManager.ClearAllPasswords(); context.AddString("AutoServerPassword: All saved server passwords have been cleared."); } else { context.AddString("Unknown subcommand. Usage: savedpasswords [list / list --show / remove <target> / clear]"); } } private static string MaskPassword(string pw) { if (string.IsNullOrEmpty(pw)) { return ""; } if (pw.Length <= 2) { return new string('*', pw.Length); } return pw[0] + new string('*', Math.Max(1, pw.Length - 2)) + pw[pw.Length - 1]; } } [BepInPlugin("Allifreyr.AutoServerPassword", "AutoServerPassword", "1.0.2")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "Allifreyr.AutoServerPassword"; public const string PluginName = "AutoServerPassword"; public const string PluginVersion = "1.0.2"; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static Harmony Harmony { get; private set; } private void Awake() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; PluginConfig.Bind(((BaseUnityPlugin)this).Config); ServerPasswordManager.Initialize(); Harmony = new Harmony("Allifreyr.AutoServerPassword"); Harmony.PatchAll(Assembly.GetExecutingAssembly()); Log.LogInfo((object)string.Format("{0} {1} loaded successfully. Press {2} to open password manager.", "AutoServerPassword", "1.0.2", PluginConfig.ToggleHotkey.Value)); } private void Update() { //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_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_004d: 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) //IL_0031: 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_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) if (!PluginConfig.Enabled.Value) { PasswordManagerGui.Visible = false; return; } KeyboardShortcut value = PluginConfig.ToggleHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { value = PluginConfig.ToggleHotkey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0) { PasswordManagerGui.Toggle(); } } value = PluginConfig.ConsoleHotkey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0) { value = PluginConfig.ConsoleHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { ToggleConsole(); } } } private void OnGUI() { if (PluginConfig.Enabled.Value) { PasswordManagerGui.Draw(); } } private void ToggleConsole() { if ((Object)(object)Console.instance != (Object)null && (Object)(object)((Terminal)Console.instance).m_chatWindow != (Object)null) { bool flag = !((Component)((Terminal)Console.instance).m_chatWindow).gameObject.activeSelf; ((Component)((Terminal)Console.instance).m_chatWindow).gameObject.SetActive(flag); if (flag && (Object)(object)((Terminal)Console.instance).m_input != (Object)null) { ((Terminal)Console.instance).m_input.ActivateInputField(); } } } private void OnDestroy() { Harmony harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } Instance = null; } } public static class PluginConfig { public static ConfigEntry<bool> Enabled { get; private set; } public static ConfigEntry<KeyboardShortcut> ToggleHotkey { get; private set; } public static ConfigEntry<KeyboardShortcut> ConsoleHotkey { get; private set; } public static ConfigEntry<bool> AutoSubmit { get; private set; } public static ConfigEntry<bool> HidePasswordDialog { get; private set; } public static ConfigEntry<bool> AutoSave { get; private set; } public static ConfigEntry<bool> PurgeOnWrongPassword { get; private set; } public static ConfigEntry<bool> ShowNotifications { get; private set; } public static ConfigEntry<bool> HighlightSavedInServerList { get; private set; } public static void Bind(ConfigFile config) { //IL_0030: 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) Enabled = config.Bind<bool>("General", "Enabled", true, "Master switch to enable or disable the AutoServerPassword mod."); ToggleHotkey = config.Bind<KeyboardShortcut>("General", "ToggleHotkey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Key to toggle the AutoServerPassword in-game manager window. Change this to any key to avoid conflicts with dev console mods."); ConsoleHotkey = config.Bind<KeyboardShortcut>("General", "ConsoleHotkey", new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>()), "Optional alternate key to toggle Valheim's developer console. Useful if a dev console mod overrides or conflicts with F5."); AutoSubmit = config.Bind<bool>("General", "AutoSubmit", true, "Automatically submit the saved password when connecting to a known server."); HidePasswordDialog = config.Bind<bool>("General", "HidePasswordDialog", true, "If true, the password prompt dialog will not render when a saved password is automatically entered."); AutoSave = config.Bind<bool>("General", "AutoSave", true, "Automatically save the password for a server after successfully connecting with it."); PurgeOnWrongPassword = config.Bind<bool>("General", "PurgeOnWrongPassword", true, "Automatically remove the saved password if the server rejects it (e.g. if the server password changed), so you are prompted next time."); ShowNotifications = config.Bind<bool>("UI", "ShowNotifications", true, "Show an in-game HUD notification when a password is auto-submitted or saved."); HighlightSavedInServerList = config.Bind<bool>("UI", "HighlightSavedInServerList", true, "In the server browser list, tint the lock icon green and note in tooltip for servers that have saved passwords."); } } public class SavedServerEntry { public string ServerKey { get; set; } = ""; public string ServerName { get; set; } = ""; public string Password { get; set; } = ""; public List<string> Aliases { get; set; } = new List<string>(); public DateTime LastUsed { get; set; } = DateTime.UtcNow; public DateTime LastSaved { get; set; } = DateTime.UtcNow; } public class ServerIdentifiers { public string PrimaryServerKey { get; set; } = ""; public string ServerStringNoBackend { get; set; } = ""; public string SocketEndpoint { get; set; } = ""; public string SocketHost { get; set; } = ""; public string ServerHost { get; set; } = ""; public string JoinData { get; set; } = ""; public string ServerName { get; set; } = ""; public IEnumerable<string> GetAllKeys() { HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase); AddIfValid(PrimaryServerKey); AddIfValid(SocketEndpoint); AddIfValid(ServerStringNoBackend); AddIfValid(ServerHost); AddIfValid(JoinData); AddIfValid(SocketHost); return seen; void AddIfValid(string key) { if (!string.IsNullOrWhiteSpace(key)) { seen.Add(key.Trim()); } } } } public class InFlightAttempt { public string ServerKey { get; set; } = ""; public string Password { get; set; } = ""; public bool IsAuto { get; set; } public ServerIdentifiers Identifiers { get; set; } } public static class ServerPasswordManager { private static readonly object _lock = new object(); private static Dictionary<string, SavedServerEntry> _servers = new Dictionary<string, SavedServerEntry>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> _failedThisSession = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static InFlightAttempt _activeAttempt; private static string _queuedNotification; public static bool WasAutoPasswordPurged { get; set; } public static string PurgedServerName { get; set; } public static string LastJoiningServerData { get; set; } = ""; public static string LastJoiningServerName { get; set; } = ""; private static string StorageDirectory => Path.Combine(Paths.ConfigPath, "AutoServerPassword"); private static string StorageFilePath => Path.Combine(StorageDirectory, "passwords.json"); public static bool HasActiveAutoAttempt { get { lock (_lock) { return _activeAttempt != null && _activeAttempt.IsAuto; } } } public static void Initialize() { LoadFromDisk(); } public static void LoadFromDisk() { lock (_lock) { try { if (!Directory.Exists(StorageDirectory)) { Directory.CreateDirectory(StorageDirectory); } if (File.Exists(StorageFilePath)) { Dictionary<string, SavedServerEntry> dictionary = JsonConvert.DeserializeObject<Dictionary<string, SavedServerEntry>>(File.ReadAllText(StorageFilePath, Encoding.UTF8)); if (dictionary != null) { _servers = new Dictionary<string, SavedServerEntry>(dictionary, StringComparer.OrdinalIgnoreCase); Plugin.Log.LogInfo((object)$"[AutoServerPassword] Loaded {_servers.Count} saved server password(s) from disk."); } } else { _servers = new Dictionary<string, SavedServerEntry>(StringComparer.OrdinalIgnoreCase); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[AutoServerPassword] Failed to load passwords from {StorageFilePath}: {arg}"); _servers = new Dictionary<string, SavedServerEntry>(StringComparer.OrdinalIgnoreCase); } } } public static void SaveToDisk() { lock (_lock) { try { if (!Directory.Exists(StorageDirectory)) { Directory.CreateDirectory(StorageDirectory); } string contents = JsonConvert.SerializeObject((object)_servers, (Formatting)1); string text = StorageFilePath + ".tmp"; File.WriteAllText(text, contents, Encoding.UTF8); if (File.Exists(StorageFilePath)) { File.Delete(StorageFilePath); } File.Move(text, StorageFilePath); } catch (Exception arg) { Plugin.Log.LogError((object)$"[AutoServerPassword] Failed to save passwords to {StorageFilePath}: {arg}"); } } } public static ServerIdentifiers CollectCurrentIdentifiers(ZNet net, ZRpc rpc) { ServerIdentifiers serverIdentifiers = new ServerIdentifiers(); try { serverIdentifiers.PrimaryServerKey = ZNet.GetServerString(true)?.Trim() ?? ""; serverIdentifiers.ServerStringNoBackend = ZNet.GetServerString(false)?.Trim() ?? ""; } catch { } try { ISocket val = ((rpc != null) ? rpc.GetSocket() : null); if (val != null) { serverIdentifiers.SocketEndpoint = val.GetEndPointString()?.Trim() ?? ""; serverIdentifiers.SocketHost = val.GetHostName()?.Trim() ?? ""; } } catch { } try { if (!string.IsNullOrEmpty(ZNet.m_serverHost)) { serverIdentifiers.ServerHost = ((ZNet.m_serverHostPort > 0) ? $"{ZNet.m_serverHost}:{ZNet.m_serverHostPort}".Trim() : ZNet.m_serverHost.Trim()); } } catch { } serverIdentifiers.JoinData = LastJoiningServerData?.Trim() ?? ""; serverIdentifiers.ServerName = LastJoiningServerName?.Trim() ?? ""; if (string.IsNullOrEmpty(serverIdentifiers.PrimaryServerKey)) { serverIdentifiers.PrimaryServerKey = ((!string.IsNullOrEmpty(serverIdentifiers.SocketEndpoint)) ? serverIdentifiers.SocketEndpoint : ((!string.IsNullOrEmpty(serverIdentifiers.JoinData)) ? serverIdentifiers.JoinData : serverIdentifiers.ServerHost)); } return serverIdentifiers; } public static bool TryFindSavedPassword(ServerIdentifiers identifiers, out SavedServerEntry matchedEntry, out string matchedKey) { lock (_lock) { matchedEntry = null; matchedKey = null; if (identifiers == null) { return false; } List<string> list = identifiers.GetAllKeys().ToList(); foreach (string item in list) { if (_servers.TryGetValue(item, out var value)) { matchedEntry = value; matchedKey = item; return true; } } foreach (KeyValuePair<string, SavedServerEntry> server in _servers) { SavedServerEntry value2 = server.Value; if (value2.Aliases == null) { continue; } foreach (string key in list) { if (value2.Aliases.Any((string a) => string.Equals(a, key, StringComparison.OrdinalIgnoreCase))) { matchedEntry = value2; matchedKey = key; return true; } } } if (!string.IsNullOrWhiteSpace(identifiers.ServerName)) { List<SavedServerEntry> list2 = _servers.Values.Where((SavedServerEntry e) => !string.IsNullOrWhiteSpace(e.ServerName) && string.Equals(e.ServerName, identifiers.ServerName, StringComparison.OrdinalIgnoreCase)).ToList(); if (list2.Count == 1) { matchedEntry = list2[0]; matchedKey = identifiers.ServerName; return true; } } return false; } } public unsafe static bool HasSavedPassword(ServerJoinData joinData) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) string joinData2 = ((object)(*(ServerJoinData*)(&joinData))/*cast due to .constrained prefix*/).ToString(); string serverName = MultiBackendMatchmaking.GetServerName(joinData); SavedServerEntry matchedEntry; string matchedKey; return TryFindSavedPassword(new ServerIdentifiers { JoinData = joinData2, ServerName = serverName }, out matchedEntry, out matchedKey); } public static bool IsFailedThisSession(string serverKey) { lock (_lock) { return _failedThisSession.Contains(serverKey); } } public static void RecordActiveAttempt(string serverKey, string password, bool isAuto, ServerIdentifiers ids) { lock (_lock) { _activeAttempt = new InFlightAttempt { ServerKey = serverKey, Password = password, IsAuto = isAuto, Identifiers = ids }; } } public static void ClearActiveAttempt() { lock (_lock) { _activeAttempt = null; } } public static void SavePassword(string primaryKey, string password, ServerIdentifiers ids) { lock (_lock) { if (string.IsNullOrWhiteSpace(primaryKey) || string.IsNullOrEmpty(password)) { return; } if (!_servers.TryGetValue(primaryKey, out var value)) { value = new SavedServerEntry { ServerKey = primaryKey, Password = password, Aliases = new List<string>(), LastSaved = DateTime.UtcNow, LastUsed = DateTime.UtcNow }; _servers[primaryKey] = value; } else { value.Password = password; value.LastSaved = DateTime.UtcNow; value.LastUsed = DateTime.UtcNow; } if (ids != null) { if (!string.IsNullOrWhiteSpace(ids.ServerName)) { value.ServerName = ids.ServerName; } foreach (string allKey in ids.GetAllKeys()) { if (!string.Equals(allKey, primaryKey, StringComparison.OrdinalIgnoreCase) && !value.Aliases.Contains<string>(allKey, StringComparer.OrdinalIgnoreCase)) { value.Aliases.Add(allKey); } } } SaveToDisk(); } } public static void UpdateLastUsedAndAliases(string primaryKey, ServerIdentifiers ids) { lock (_lock) { if (!_servers.TryGetValue(primaryKey, out var value)) { return; } value.LastUsed = DateTime.UtcNow; if (ids != null) { if (!string.IsNullOrWhiteSpace(ids.ServerName) && string.IsNullOrWhiteSpace(value.ServerName)) { value.ServerName = ids.ServerName; } foreach (string allKey in ids.GetAllKeys()) { if (!string.Equals(allKey, primaryKey, StringComparison.OrdinalIgnoreCase) && !value.Aliases.Contains<string>(allKey, StringComparer.OrdinalIgnoreCase)) { value.Aliases.Add(allKey); } } } SaveToDisk(); } } public static bool RemoveSavedPassword(string keyOrName) { lock (_lock) { if (string.IsNullOrWhiteSpace(keyOrName)) { return false; } if (_servers.Remove(keyOrName)) { SaveToDisk(); return true; } string text = null; foreach (KeyValuePair<string, SavedServerEntry> server in _servers) { if (string.Equals(server.Value.ServerName, keyOrName, StringComparison.OrdinalIgnoreCase) || server.Value.Aliases.Any((string a) => string.Equals(a, keyOrName, StringComparison.OrdinalIgnoreCase))) { text = server.Key; break; } } if (text != null && _servers.Remove(text)) { SaveToDisk(); return true; } return false; } } public static void ClearAllPasswords() { lock (_lock) { _servers.Clear(); SaveToDisk(); } } public static IReadOnlyList<SavedServerEntry> GetAllSavedServers() { lock (_lock) { return _servers.Values.ToList(); } } public static void OnConnectionSuccess(ZNet net, ZRpc rpc) { InFlightAttempt activeAttempt; lock (_lock) { activeAttempt = _activeAttempt; _activeAttempt = null; } if (activeAttempt == null) { return; } ServerIdentifiers serverIdentifiers = CollectCurrentIdentifiers(net, rpc); string text = ((!string.IsNullOrEmpty(activeAttempt.ServerKey)) ? activeAttempt.ServerKey : serverIdentifiers.PrimaryServerKey); string text2 = ((!string.IsNullOrEmpty(serverIdentifiers.ServerName)) ? serverIdentifiers.ServerName : text); lock (_lock) { _failedThisSession.Remove(text); } if (!activeAttempt.IsAuto) { if (PluginConfig.AutoSave.Value) { SavePassword(text, activeAttempt.Password, serverIdentifiers); Plugin.Log.LogInfo((object)("[AutoServerPassword] Successfully connected to '" + text2 + "'! Password saved.")); QueueNotification("AutoServerPassword: Saved password for " + text2); } } else { UpdateLastUsedAndAliases(text, serverIdentifiers); Plugin.Log.LogInfo((object)("[AutoServerPassword] Successfully auto-connected to '" + text2 + "'.")); QueueNotification("AutoServerPassword: Auto-entered password for " + text2); } } public static void OnPasswordError(ZNet net, ZRpc rpc) { InFlightAttempt activeAttempt; lock (_lock) { activeAttempt = _activeAttempt; _activeAttempt = null; } if (activeAttempt == null) { return; } ServerIdentifiers serverIdentifiers = CollectCurrentIdentifiers(net, rpc); string text = ((!string.IsNullOrEmpty(activeAttempt.ServerKey)) ? activeAttempt.ServerKey : serverIdentifiers.PrimaryServerKey); string text2 = ((!string.IsNullOrEmpty(serverIdentifiers.ServerName)) ? serverIdentifiers.ServerName : text); if (activeAttempt.IsAuto) { if (PluginConfig.PurgeOnWrongPassword.Value) { Plugin.Log.LogWarning((object)("[AutoServerPassword] Saved password rejected by server '" + text2 + "' (" + text + "). Purging saved password.")); RemoveSavedPassword(text); lock (_lock) { _failedThisSession.Add(text); WasAutoPasswordPurged = true; PurgedServerName = text2; } } } else { Plugin.Log.LogWarning((object)("[AutoServerPassword] Manually entered password rejected by server '" + text2 + "'. Password will not be saved.")); } } public static void QueueNotification(string message) { if (PluginConfig.ShowNotifications.Value) { _queuedNotification = message; TryDisplayQueuedNotification(); } } public static void TryDisplayQueuedNotification() { if (!string.IsNullOrEmpty(_queuedNotification) && (Object)(object)MessageHud.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, _queuedNotification, 0, (Sprite)null, false, true); _queuedNotification = null; } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }