Decompiled source of QuickConnect v1.7.0

plugins/QuickConnect.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("QuickConnect")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("QuickConnect")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("1c6b0fc4-ad65-4bb6-a9db-7ca3b47eadf6")]
[assembly: AssemblyFileVersion("1.5.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.5.0.0")]
namespace QuickConnect;

[HarmonyPatch(typeof(ZNet), "RPC_ClientHandshake")]
internal class PatchPasswordPrompt
{
	private static bool Prefix(ZNet __instance, ZRpc rpc, bool needPassword, string serverPasswordSalt)
	{
		string text = QuickConnectUI.CurrentPass();
		if (text != null)
		{
			if (needPassword)
			{
				Mod.Log.LogInfo((object)"Authenticating with saved password...");
				((Component)__instance.m_connectingDialog).gameObject.SetActive(false);
				FieldInfo field = typeof(ZNet).GetField("m_serverPasswordSalt", BindingFlags.Static | BindingFlags.NonPublic);
				field.SetValue(null, serverPasswordSalt);
				MethodInfo method = typeof(ZNet).GetMethod("SendPeerInfo", BindingFlags.Instance | BindingFlags.NonPublic);
				method.Invoke(__instance, new object[2] { rpc, text });
				QuickConnectUI.connecting = null;
				return false;
			}
			Mod.Log.LogInfo((object)"Server didn't want password?");
		}
		return true;
	}
}
[HarmonyPatch(typeof(ZNet), "OnDestroy")]
internal class PatchConnectFailed
{
	private static void Postfix()
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		if ((int)ZNet.GetConnectionStatus() == 5)
		{
			QuickConnectUI.instance.JoinServerFailed();
		}
	}
}
[HarmonyPatch(typeof(FejdStartup), "OnSelelectCharacterBack")]
internal class PatchCharacterBack
{
	private static void Postfix()
	{
		QuickConnectUI.instance.AbortConnect();
	}
}
[HarmonyPatch(typeof(FejdStartup), "SetupGui")]
internal class PatchUiInit
{
	private static void Postfix()
	{
		((Object)QuickConnectUI.instance).GetInstanceID();
	}
}
[BepInPlugin("net.bdew.valheim.QuickConnect", "QuickConnect", "1.6.1")]
internal class Mod : BaseUnityPlugin
{
	public static ManualLogSource Log;

	public static ConfigEntry<float> windowPosX;

	public static ConfigEntry<float> windowPosY;

	public static ConfigEntry<int> buttonFontSize;

	public static ConfigEntry<int> labelFontSize;

	public static ConfigEntry<int> windowWidth;

	public static ConfigEntry<int> windowHeight;

	public static ConfigEntry<bool> customConnectionError;

	public static ConfigEntry<string> customDelimiter;

	private void Awake()
	{
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Expected O, but got Unknown
		Log = Logger.CreateLogSource("QuickConnect");
		windowPosX = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "WindowPosX", 20f, (ConfigDescription)null);
		windowPosY = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "WindowPosY", 20f, (ConfigDescription)null);
		buttonFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "ButtonFontSize", 0, (ConfigDescription)null);
		labelFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "LabelFontSize", 0, (ConfigDescription)null);
		windowWidth = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "WindowWidth", 250, (ConfigDescription)null);
		windowHeight = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "WindowHeight", 50, (ConfigDescription)null);
		customConnectionError = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "CustomConnectionError", false, "Show custom connection failure message in addition to vanilla connection failure message.");
		customDelimiter = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "CustomDelimiter", "", "Override server config delimiter. Defaults to a colon (':').");
		((BaseUnityPlugin)this).Config.SettingChanged += delegate
		{
			Servers.Init();
			QuickConnectUI.instance.redraw = true;
		};
		Harmony val = new Harmony("net.bdew.valheim.QuickConnect");
		val.PatchAll();
	}
}
internal class QuickConnectUI : MonoBehaviour
{
	private enum windowState
	{
		ServerList,
		Connecting,
		NoServers
	}

	private static QuickConnectUI _instance;

	public Rect windowRect;

	public Rect lastRect;

	public Rect errorWinRect;

	private Rect dragRect = new Rect(0f, 0f, 10000f, 20f);

	private int buttonFontSize;

	private int labelFontSize;

	private GUIStyle buttonStyle;

	private GUIStyle labelStyle;

	public bool redraw = true;

	private windowState state = windowState.ServerList;

	private Task<IPHostEntry> resolveTask;

	public static Servers.Entry connecting;

	private static string errorMsg;

	public static QuickConnectUI instance
	{
		get
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			if ((Object)(object)_instance == (Object)null)
			{
				Servers.Init();
				GameObject val = new GameObject("QuickConnect");
				_instance = val.AddComponent<QuickConnectUI>();
			}
			return _instance;
		}
	}

	private void Update()
	{
		if (resolveTask == null)
		{
			return;
		}
		if (resolveTask.IsFaulted)
		{
			Mod.Log.LogError((object)$"Error resolving IP: {resolveTask.Exception}");
			if (resolveTask.Exception != null)
			{
				ShowError(resolveTask.Exception.InnerException.Message);
			}
			else
			{
				ShowError(resolveTask.Exception.Message);
			}
			resolveTask = null;
			connecting = null;
		}
		else if (resolveTask.IsCanceled)
		{
			resolveTask = null;
			connecting = null;
		}
		else
		{
			if (!resolveTask.IsCompleted)
			{
				return;
			}
			IPAddress[] addressList = resolveTask.Result.AddressList;
			foreach (IPAddress iPAddress in addressList)
			{
				if (iPAddress.AddressFamily == AddressFamily.InterNetwork)
				{
					Mod.Log.LogInfo((object)$"Resolved: {iPAddress}");
					resolveTask = null;
					ZSteamMatchmaking.instance.QueueServerJoin($"{iPAddress}:{connecting.port}");
					return;
				}
			}
			resolveTask = null;
			connecting = null;
			ShowError("Server DNS resolved to no valid addresses");
		}
	}

	private void Draw()
	{
		//IL_007d: 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)
		((Rect)(ref windowRect)).x = Mod.windowPosX.Value;
		((Rect)(ref windowRect)).y = Mod.windowPosY.Value;
		buttonFontSize = Mod.buttonFontSize.Value;
		labelFontSize = Mod.labelFontSize.Value;
		((Rect)(ref windowRect)).width = Mod.windowWidth.Value;
		((Rect)(ref windowRect)).height = Mod.windowHeight.Value;
		lastRect = windowRect;
	}

	private void Awake()
	{
		Draw();
	}

	private void OnGUI()
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Expected O, but got Unknown
		//IL_008c: 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_00ac: Expected O, but got Unknown
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Expected O, but got Unknown
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		if (buttonStyle == null)
		{
			buttonStyle = new GUIStyle(GUI.skin.button);
		}
		if (labelStyle == null)
		{
			labelStyle = new GUIStyle(GUI.skin.label);
		}
		if (errorMsg != null)
		{
			errorWinRect = GUILayout.Window(1586464, errorWinRect, new WindowFunction(DrawErrorWindow), "Error", Array.Empty<GUILayoutOption>());
			return;
		}
		windowRect = GUILayout.Window(1586463, windowRect, new WindowFunction(DrawConnectWindow), "Quick Connect", Array.Empty<GUILayoutOption>());
		if (!((Rect)(ref lastRect)).Equals(windowRect))
		{
			Mod.windowPosX.Value = ((Rect)(ref windowRect)).x;
			Mod.windowPosY.Value = ((Rect)(ref windowRect)).y;
			lastRect = windowRect;
		}
		else if (redraw)
		{
			Draw();
			buttonStyle.fontSize = buttonFontSize;
			labelStyle.fontSize = labelFontSize;
			redraw = false;
		}
	}

	private void DrawConnectWindow(int windowID)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		GUI.DragWindow(dragRect);
		if (connecting != null)
		{
			CheckWindowState(windowState.Connecting);
			GUILayout.Label("Connecting to " + connecting.name, labelStyle, Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Cancel", buttonStyle, Array.Empty<GUILayoutOption>()))
			{
				AbortConnect();
			}
			return;
		}
		if (Servers.entries.Count > 0)
		{
			CheckWindowState(windowState.ServerList);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Choose A Server:", labelStyle, Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Reload from Disk", buttonStyle, Array.Empty<GUILayoutOption>()))
			{
				Servers.Init();
				DrawConnectWindow(windowID);
			}
			GUILayout.EndHorizontal();
			{
				foreach (Servers.Entry entry in Servers.entries)
				{
					if (GUILayout.Button(entry.name, buttonStyle, Array.Empty<GUILayoutOption>()))
					{
						DoConnect(entry);
					}
				}
				return;
			}
		}
		CheckWindowState(windowState.NoServers);
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		GUILayout.Label("No servers defined", labelStyle, Array.Empty<GUILayoutOption>());
		if (GUILayout.Button("Reload from Disk", buttonStyle, Array.Empty<GUILayoutOption>()))
		{
			Servers.Init();
			DrawConnectWindow(windowID);
		}
		GUILayout.EndHorizontal();
		GUILayout.Label("Add/modify quick_connect_servers.cfg", labelStyle, Array.Empty<GUILayoutOption>());
	}

	private void DrawErrorWindow(int windowID)
	{
		GUILayout.Label(errorMsg, labelStyle, Array.Empty<GUILayoutOption>());
		if (GUILayout.Button("Close", buttonStyle, Array.Empty<GUILayoutOption>()))
		{
			errorMsg = null;
		}
	}

	private void DoConnect(Servers.Entry server)
	{
		connecting = server;
		try
		{
			IPAddress.Parse(server.ip);
			ZSteamMatchmaking.instance.QueueServerJoin($"{server.ip}:{server.port}");
		}
		catch (FormatException)
		{
			Mod.Log.LogInfo((object)("Resolving: " + server.ip));
			resolveTask = Dns.GetHostEntryAsync(server.ip);
		}
	}

	public static string CurrentPass()
	{
		if (connecting != null)
		{
			return connecting.pass;
		}
		return null;
	}

	public void JoinServerFailed()
	{
		if (Mod.customConnectionError.Value)
		{
			ShowError("Server connection failed");
		}
		connecting = null;
	}

	public void ShowError(string msg)
	{
		//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)
		errorMsg = msg;
		errorWinRect = new Rect((float)(Screen.width / 2 - 125), (float)(Screen.height / 2), 250f, 30f);
	}

	public void AbortConnect()
	{
		connecting = null;
		resolveTask = null;
	}

	private void CheckWindowState(windowState state)
	{
		if (this.state != state)
		{
			this.state = state;
			Draw();
		}
	}
}
internal class Servers
{
	public class Entry
	{
		public string name;

		public string ip;

		public int port;

		public string pass;

		public override string ToString()
		{
			return $"Server(name={name},ip={ip},port={port})";
		}
	}

	public static string ConfigPath;

	public static List<Entry> entries;

	public static void Init()
	{
		entries.Clear();
		try
		{
			char c = (Mod.customDelimiter.Value.Equals(string.Empty) ? ':' : Mod.customDelimiter.Value[0]);
			if (!File.Exists(ConfigPath))
			{
				return;
			}
			using StreamReader streamReader = new StreamReader(ConfigPath);
			string text;
			while ((text = streamReader.ReadLine()) != null)
			{
				text = text.Trim();
				if (text.Length == 0 || text.StartsWith("#"))
				{
					continue;
				}
				string[] array = text.Split(new char[1] { c });
				if (array.Length >= 3)
				{
					string name = array[0];
					string ip = array[1];
					int port = int.Parse(array[2]);
					string pass = null;
					if (array.Length >= 4)
					{
						pass = array[3];
					}
					entries.Add(new Entry
					{
						name = name,
						ip = ip,
						port = port,
						pass = pass
					});
				}
				else
				{
					Mod.Log.LogDebug((object)("Invalid config line: " + text));
				}
			}
			Mod.Log.LogInfo((object)$"Loaded {entries.Count} server entries");
		}
		catch (Exception arg)
		{
			Mod.Log.LogError((object)$"Error loading config {arg}");
			QuickConnectUI.instance.ShowError("Error loading server config");
		}
	}

	static Servers()
	{
		string? directoryName = Path.GetDirectoryName(Paths.BepInExConfigPath);
		char directorySeparatorChar = Path.DirectorySeparatorChar;
		ConfigPath = directoryName + directorySeparatorChar + "quick_connect_servers.cfg";
		entries = new List<Entry>();
	}
}