Decompiled source of RefocusGuard v0.1.3

plugins\RefocusGuard\RefocusGuard.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using HarmonyLib;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyTitle("RefocusGuard")]
[assembly: AssemblyDescription("Consumes mouse input used to refocus Valheim after Alt-Tab")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyCompany("Daishi11")]
[assembly: AssemblyProduct("RefocusGuard")]
[assembly: AssemblyVersion("0.1.3.0")]
namespace Daishi11.RefocusGuard;

[BepInPlugin("daishi11.valheim.refocusguard", "Refocus Guard", "0.1.3")]
public sealed class Plugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Player), "Update")]
	private static class PlayerUpdateFocusPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!Application.isFocused)
			{
				if (!sawFocusLoss)
				{
					sawFocusLoss = true;
					ResetGuard();
				}
			}
			else if (sawFocusLoss && !guardActive)
			{
				BeginGuard();
			}
		}
	}

	[HarmonyPatch]
	private static class PlayerAttackInputPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return FindInstanceMethods(typeof(Player), "PlayerAttackInput");
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix(Player __instance)
		{
			if (!IsLocalPlayer(__instance) || !IsGuardingGameplay())
			{
				return true;
			}
			return false;
		}
	}

	[HarmonyPatch]
	private static class PlayerUpdatePlacementPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return FindInstanceMethods(typeof(Player), "UpdatePlacement");
		}

		[HarmonyPriority(800)]
		[HarmonyPrefix]
		private static bool Prefix(Player __instance)
		{
			if (!IsLocalPlayer(__instance) || !IsGuardingGameplay())
			{
				return true;
			}
			return false;
		}
	}

	[HarmonyPatch]
	private static class HumanoidStartAttackPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return FindInstanceMethods(typeof(Humanoid), "StartAttack");
		}

		[HarmonyPriority(800)]
		[HarmonyPrefix]
		private static bool Prefix(Humanoid __instance)
		{
			Player player = (Player)(object)((__instance is Player) ? __instance : null);
			if (!IsLocalPlayer(player) || !IsGuardingGameplay())
			{
				return true;
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(ZInput), "GetButton", new Type[] { typeof(string) })]
	private static class ZInputGetButtonPatch
	{
		private static bool Prefix(string name, ref bool __result)
		{
			if (!ShouldBlockAction(name))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}

	[HarmonyPatch(typeof(ZInput), "GetButtonDown", new Type[] { typeof(string) })]
	private static class ZInputGetButtonDownPatch
	{
		private static bool Prefix(string name, ref bool __result)
		{
			if (!ShouldBlockAction(name))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}

	[HarmonyPatch(typeof(ZInput), "GetButtonUp", new Type[] { typeof(string) })]
	private static class ZInputGetButtonUpPatch
	{
		private static bool Prefix(string name, ref bool __result)
		{
			if (!ShouldBlockAction(name))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}

	[HarmonyPatch(typeof(ZInput), "GetMouseButton", new Type[] { typeof(int) })]
	private static class ZInputGetMouseButtonPatch
	{
		private static bool Prefix(int button, ref bool __result)
		{
			if (!ShouldBlockMouseButton(button))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}

	[HarmonyPatch(typeof(ZInput), "GetMouseButtonDown", new Type[] { typeof(int) })]
	private static class ZInputGetMouseButtonDownPatch
	{
		private static bool Prefix(int button, ref bool __result)
		{
			if (!ShouldBlockMouseButton(button))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}

	[HarmonyPatch(typeof(ZInput), "GetMouseButtonUp", new Type[] { typeof(int) })]
	private static class ZInputGetMouseButtonUpPatch
	{
		private static bool Prefix(int button, ref bool __result)
		{
			if (!ShouldBlockMouseButton(button))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}

	public const string PluginGuid = "daishi11.valheim.refocusguard";

	public const string PluginName = "Refocus Guard";

	public const string PluginVersion = "0.1.3";

	private const int LeftMouseMask = 1;

	private const int RightMouseMask = 2;

	private const int MiddleMouseMask = 4;

	private const int GuardedMouseMask = 7;

	private const float FocusSafetyWindow = 0.15f;

	private Harmony harmony;

	private static bool sawFocusLoss;

	private static bool guardActive;

	private static float guardUntilUnscaledTime;

	private static int heldMouseMask;

	private static int releaseMouseMask;

	private void Awake()
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		sawFocusLoss = !Application.isFocused;
		harmony = new Harmony("daishi11.valheim.refocusguard");
		harmony.PatchAll(typeof(Plugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Refocus Guard active. Mouse clicks used to refocus Valheim will be consumed until the pressed button is released.");
	}

	private void OnDestroy()
	{
		ResetGuard();
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}

	private void OnApplicationFocus(bool hasFocus)
	{
		if (!hasFocus)
		{
			sawFocusLoss = true;
			ResetGuard();
		}
		else if (sawFocusLoss)
		{
			BeginGuard();
		}
	}

	private static void BeginGuard()
	{
		sawFocusLoss = false;
		guardActive = true;
		guardUntilUnscaledTime = Time.unscaledTime + 0.15f;
		heldMouseMask = ReadRawMouseMask();
		releaseMouseMask = 0;
	}

	private static void ResetGuard()
	{
		guardActive = false;
		guardUntilUnscaledTime = 0f;
		heldMouseMask = 0;
		releaseMouseMask = 0;
	}

	private void Update()
	{
		if (!Application.isFocused)
		{
			if (!sawFocusLoss)
			{
				sawFocusLoss = true;
				ResetGuard();
			}
			return;
		}
		if (sawFocusLoss && !guardActive)
		{
			BeginGuard();
		}
		if (guardActive)
		{
			int num = ReadRawMouseMask();
			if (Time.unscaledTime <= guardUntilUnscaledTime)
			{
				heldMouseMask |= num;
			}
			int num2 = heldMouseMask & ~num;
			if (num2 != 0)
			{
				releaseMouseMask |= num2;
			}
			heldMouseMask &= num;
			if (Time.unscaledTime > guardUntilUnscaledTime && heldMouseMask == 0)
			{
				guardActive = false;
			}
		}
	}

	private void LateUpdate()
	{
		releaseMouseMask = 0;
	}

	private static int ReadRawMouseMask()
	{
		int num = 0;
		if (Input.GetMouseButton(0))
		{
			num |= 1;
		}
		if (Input.GetMouseButton(1))
		{
			num |= 2;
		}
		if (Input.GetMouseButton(2))
		{
			num |= 4;
		}
		return num;
	}

	private static bool ShouldBlockMouseButton(int button)
	{
		if (!guardActive || button < 0 || button > 2)
		{
			return false;
		}
		int num = 1 << button;
		if (Time.unscaledTime <= guardUntilUnscaledTime && (7 & num) != 0)
		{
			return true;
		}
		if ((heldMouseMask & num) == 0)
		{
			return (releaseMouseMask & num) != 0;
		}
		return true;
	}

	private static bool TryGetActionMouseButton(string action, out int button)
	{
		button = -1;
		if (string.IsNullOrEmpty(action))
		{
			return false;
		}
		switch (action)
		{
		case "Attack":
			button = 0;
			return true;
		case "Block":
		case "BuildMenu":
			button = 1;
			return true;
		case "SecondaryAttack":
		case "Remove":
			button = 2;
			return true;
		default:
			return false;
		}
	}

	private static bool ShouldBlockAction(string action)
	{
		if (TryGetActionMouseButton(action, out var button))
		{
			return ShouldBlockMouseButton(button);
		}
		return false;
	}

	private static bool IsGuardingGameplay()
	{
		if (!sawFocusLoss && !guardActive)
		{
			return !Application.isFocused;
		}
		return true;
	}

	private static bool IsLocalPlayer(Player player)
	{
		if ((Object)(object)player != (Object)null)
		{
			return (Object)(object)player == (Object)(object)Player.m_localPlayer;
		}
		return false;
	}

	private static IEnumerable<MethodBase> FindInstanceMethods(Type type, string name)
	{
		MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (MethodInfo candidate in methods)
		{
			if (candidate.Name == name)
			{
				yield return candidate;
			}
		}
	}
}