Decompiled source of ValheimCursorLockFix v1.0.2

plugins\ValheimCursorLockFix.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ValheimCursorLockFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ValheimCursorLockFix")]
[assembly: AssemblyTitle("ValheimCursorLockFix")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 ValheimCursorLockFix
{
	[BepInPlugin("ua.gyrocopter.valheim.cursorlockfix", "ValheimCursorLockFix", "1.0.2")]
	public sealed class ValheimCursorLockFixPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ua.gyrocopter.valheim.cursorlockfix";

		public const string PluginName = "ValheimCursorLockFix";

		public const string PluginVersion = "1.0.2";

		private ConfigEntry<bool> _enableFix;

		private ConfigEntry<bool> _forceLockInGameplay;

		private ConfigEntry<bool> _unlockInMenus;

		private ConfigEntry<KeyboardShortcut> _toggleKey;

		private ConfigEntry<bool> _debugLogging;

		private bool _lastShouldLock;

		private bool _hasAppliedCursorState;

		private string _lastReason;

		private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();

		private void Awake()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			_enableFix = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableFix", true, "Enable or disable the cursor lock fix.");
			_forceLockInGameplay = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForceLockInGameplay", true, "Force Unity cursor lock while gameplay is active.");
			_unlockInMenus = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UnlockInMenus", true, "Force cursor unlock while menus, inventory, chat, console, build menu, map, or text input are active.");
			_toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ToggleKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Keyboard shortcut that toggles EnableFix during play.");
			_debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Enable verbose debug logging.");
			DebugLog("Plugin loaded.");
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = _toggleKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				_enableFix.Value = !_enableFix.Value;
				((BaseUnityPlugin)this).Config.Save();
				DebugLog("EnableFix toggled to " + _enableFix.Value + ".");
				if (!_enableFix.Value)
				{
					ApplyUnlock("fix disabled");
				}
			}
			if (!_enableFix.Value)
			{
				return;
			}
			if (ShouldLockCursor(out var reason))
			{
				if (_forceLockInGameplay.Value)
				{
					ApplyLock(reason);
				}
			}
			else if (_unlockInMenus.Value)
			{
				ApplyUnlock(reason);
			}
		}

		private bool ShouldLockCursor(out string reason)
		{
			if (!Application.isFocused)
			{
				reason = "application not focused";
				return false;
			}
			if (!HasZInputInstance())
			{
				reason = "ZInput unavailable";
				return false;
			}
			if (!HasLocalPlayer())
			{
				reason = "local player unavailable";
				return false;
			}
			if (IsMenuVisibleSafe())
			{
				reason = "menu visible";
				return false;
			}
			if (IsInventoryVisibleSafe())
			{
				reason = "inventory visible";
				return false;
			}
			if (IsBuildMenuVisibleSafe())
			{
				reason = "build menu visible";
				return false;
			}
			if (IsMapVisibleSafe())
			{
				reason = "map visible";
				return false;
			}
			if (IsStoreVisibleSafe())
			{
				reason = "store visible";
				return false;
			}
			if (IsTextViewerVisibleSafe())
			{
				reason = "text viewer visible";
				return false;
			}
			if (IsTextInputVisibleSafe())
			{
				reason = "text input visible";
				return false;
			}
			if (IsChatFocusedSafe())
			{
				reason = "chat focused";
				return false;
			}
			if (IsConsoleVisibleSafe())
			{
				reason = "console visible";
				return false;
			}
			reason = "gameplay";
			return true;
		}

		private void ApplyLock(string reason)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)Cursor.lockState != 1)
			{
				Cursor.lockState = (CursorLockMode)1;
			}
			if (Cursor.visible)
			{
				Cursor.visible = false;
			}
			LogCursorStateChanged(shouldLock: true, reason);
		}

		private void ApplyUnlock(string reason)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if ((int)Cursor.lockState != 0)
			{
				Cursor.lockState = (CursorLockMode)0;
			}
			if (!Cursor.visible)
			{
				Cursor.visible = true;
			}
			LogCursorStateChanged(shouldLock: false, reason);
		}

		private void LogCursorStateChanged(bool shouldLock, string reason)
		{
			if (!_hasAppliedCursorState || _lastShouldLock != shouldLock || !string.Equals(_lastReason, reason, StringComparison.Ordinal))
			{
				_hasAppliedCursorState = true;
				_lastShouldLock = shouldLock;
				_lastReason = reason;
				DebugLog((shouldLock ? "Locked" : "Unlocked") + " cursor: " + reason + ".");
			}
		}

		private bool HasZInputInstance()
		{
			return GetStaticMemberValue(FindType("ZInput"), "instance") != null;
		}

		private bool HasLocalPlayer()
		{
			return GetStaticMemberValue(FindType("Player"), "m_localPlayer") != null;
		}

		private bool IsMenuVisibleSafe()
		{
			return InvokeStaticBool(FindType("Menu"), "IsVisible");
		}

		private bool IsInventoryVisibleSafe()
		{
			return InvokeInstanceBool(GetStaticMemberValue(FindType("InventoryGui"), "instance"), "IsVisible");
		}

		private bool IsTextInputVisibleSafe()
		{
			return InvokeStaticBool(FindType("TextInput"), "IsVisible");
		}

		private bool IsBuildMenuVisibleSafe()
		{
			object staticMemberValue = GetStaticMemberValue(FindType("Hud"), "instance");
			if (InvokeInstanceBool(staticMemberValue, "IsPieceSelectionVisible") || InvokeInstanceBool(staticMemberValue, "IsBuildMenuVisible") || InvokeInstanceBool(staticMemberValue, "IsBuildSelectionVisible"))
			{
				return true;
			}
			return IsAnyInstanceMemberActive(staticMemberValue, "m_pieceSelectionWindow", "m_pieceSelection", "m_buildSelection", "m_buildMenu", "m_pieceListRoot");
		}

		private bool IsMapVisibleSafe()
		{
			object staticMemberValue = GetStaticMemberValue(FindType("Minimap"), "instance");
			if (!InvokeInstanceBool(staticMemberValue, "IsOpen") && !InvokeInstanceBool(staticMemberValue, "IsVisible"))
			{
				return IsAnyInstanceMemberActive(staticMemberValue, "m_largeRoot", "m_mapRoot");
			}
			return true;
		}

		private bool IsStoreVisibleSafe()
		{
			object staticMemberValue = GetStaticMemberValue(FindType("StoreGui"), "instance");
			if (!InvokeInstanceBool(staticMemberValue, "IsVisible"))
			{
				return IsAnyInstanceMemberActive(staticMemberValue, "m_rootPanel", "m_panel");
			}
			return true;
		}

		private bool IsTextViewerVisibleSafe()
		{
			object staticMemberValue = GetStaticMemberValue(FindType("TextViewer"), "instance");
			if (!InvokeInstanceBool(staticMemberValue, "IsVisible"))
			{
				return IsAnyInstanceMemberActive(staticMemberValue, "m_root", "m_texts");
			}
			return true;
		}

		private bool IsChatFocusedSafe()
		{
			return InvokeInstanceBool(GetStaticMemberValue(FindType("Chat"), "instance"), "HasFocus");
		}

		private bool IsConsoleVisibleSafe()
		{
			return InvokeStaticBool(FindType("Console"), "IsVisible");
		}

		private static Type FindType(string typeName)
		{
			if (TypeCache.TryGetValue(typeName, out var value))
			{
				return value;
			}
			Type type = Type.GetType(typeName);
			if (type != null)
			{
				TypeCache[typeName] = type;
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type2 = assemblies[i].GetType(typeName);
				if (type2 != null)
				{
					TypeCache[typeName] = type2;
					return type2;
				}
			}
			TypeCache[typeName] = null;
			return null;
		}

		private static object GetStaticMemberValue(Type type, string memberName)
		{
			if (type == null)
			{
				return null;
			}
			try
			{
				FieldInfo field = type.GetField(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field.GetValue(null);
				}
				PropertyInfo property = type.GetProperty(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				return (property != null) ? property.GetValue(null, null) : null;
			}
			catch
			{
				return null;
			}
		}

		private static object GetInstanceMemberValue(object instance, string memberName)
		{
			if (instance == null)
			{
				return null;
			}
			try
			{
				Type type = instance.GetType();
				FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field.GetValue(instance);
				}
				PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				return (property != null) ? property.GetValue(instance, null) : null;
			}
			catch
			{
				return null;
			}
		}

		private static bool IsAnyInstanceMemberActive(object instance, params string[] memberNames)
		{
			if (instance == null || memberNames == null)
			{
				return false;
			}
			for (int i = 0; i < memberNames.Length; i++)
			{
				if (IsUnityObjectActive(GetInstanceMemberValue(instance, memberNames[i])))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsUnityObjectActive(object value)
		{
			if (value == null)
			{
				return false;
			}
			if (TryGetBoolProperty(value, "activeInHierarchy", out var result) || TryGetBoolProperty(value, "activeSelf", out result) || TryGetNestedGameObjectActive(value, out result))
			{
				return result;
			}
			return false;
		}

		private static bool TryGetNestedGameObjectActive(object value, out bool active)
		{
			active = false;
			try
			{
				PropertyInfo property = value.GetType().GetProperty("gameObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				object value2 = ((property != null) ? property.GetValue(value, null) : null);
				return TryGetBoolProperty(value2, "activeInHierarchy", out active) || TryGetBoolProperty(value2, "activeSelf", out active);
			}
			catch
			{
				return false;
			}
		}

		private static bool TryGetBoolProperty(object value, string propertyName, out bool result)
		{
			result = false;
			if (value == null)
			{
				return false;
			}
			try
			{
				PropertyInfo property = value.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property == null || property.PropertyType != typeof(bool))
				{
					return false;
				}
				result = (bool)property.GetValue(value, null);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool InvokeStaticBool(Type type, string methodName)
		{
			if (type == null)
			{
				return false;
			}
			MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			if (method == null || method.ReturnType != typeof(bool))
			{
				return false;
			}
			try
			{
				return (bool)method.Invoke(null, null);
			}
			catch
			{
				return false;
			}
		}

		private static bool InvokeInstanceBool(object instance, string methodName)
		{
			if (instance == null)
			{
				return false;
			}
			MethodInfo method = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			if (method == null || method.ReturnType != typeof(bool))
			{
				return false;
			}
			try
			{
				return (bool)method.Invoke(instance, null);
			}
			catch
			{
				return false;
			}
		}

		private void DebugLog(string message)
		{
			if (_debugLogging != null && _debugLogging.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			}
		}
	}
}