Decompiled source of LCVR Fixes v2.0.1

BepInEx/plugins/BonemansLCVRFixes/BonemansLCVRFixes.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DunGen.Graph;
using GameNetcodeStuff;
using HarmonyLib;
using LCVR;
using LCVR.Input;
using LCVR.Managers;
using LCVR.Networking;
using LCVR.Physics.Interactions;
using LCVR.Physics.Interactions.Car;
using LCVR.Player;
using LCVR.UI.Spectating;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.UI;
using UnityEngine.XR;
using UnityEngine.XR.OpenXR;
using VRShaderFix;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BonemansLCVRFixes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f00e3e0edbf530a36e7eb201259842f2ebe46042")]
[assembly: AssemblyProduct("BonemansLCVRFixes")]
[assembly: AssemblyTitle("BonemansLCVRFixes")]
[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 VRShaderFix
{
	public class ShaderFixer : MonoBehaviour
	{
		internal static ManualLogSource Log;

		private readonly Dictionary<Material, Material> converted = new Dictionary<Material, Material>();

		private readonly Dictionary<Material, Material> convertedWorld = new Dictionary<Material, Material>();

		private readonly Dictionary<string, Shader> gameShaders = new Dictionary<string, Shader>();

		private readonly HashSet<string> reportedUnfixable = new HashSet<string>();

		private int worldFixed;

		private ConfigEntry<bool> pluginEnabled;

		private ConfigEntry<bool> fixCosmetics;

		private ConfigEntry<bool> fixItems;

		private ConfigEntry<bool> fixInteriors;

		private Type configMenuType;

		private Type cosmeticType;

		private Shader vrTextShader;

		private bool loggedFirstFix;

		internal static void Bind(ConfigFile config, out ConfigEntry<bool> enabled, out ConfigEntry<bool> cosmetics, out ConfigEntry<bool> items, out ConfigEntry<bool> interiors)
		{
			enabled = config.Bind<bool>("Shaders", "Enabled", true, "Swap mod shaders that only draw in one eye for VR-safe ones (LethalConfig text plus the options below). Only does anything in VR.");
			cosmetics = config.Bind<bool>("Shaders", "FixCosmetics", true, "Put MoreCompany cosmetics on the game's own VR-capable shaders so they stop floating off players.");
			items = config.Bind<bool>("Shaders", "FixItems", true, "Put modded scrap and items on the game's own VR-capable shaders.");
			interiors = config.Bind<bool>("Shaders", "FixInteriors", true, "Put custom interiors (modded dungeons) on the game's own VR-capable shaders so walls stop wobbling.");
		}

		internal void Init(ManualLogSource log, ConfigEntry<bool> enabled, ConfigEntry<bool> cosmetics, ConfigEntry<bool> items, ConfigEntry<bool> interiors)
		{
			Log = log;
			pluginEnabled = enabled;
			fixCosmetics = cosmetics;
			fixItems = items;
			fixInteriors = interiors;
		}

		private void Start()
		{
			if (!pluginEnabled.Value)
			{
				Log.LogInfo((object)"Disabled by config");
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(Run());
			}
		}

		private IEnumerator Run()
		{
			yield return null;
			if (Type.GetType("LCVR.Managers.VRSession, LCVR")?.GetProperty("InVR", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) as bool? != true)
			{
				Log.LogInfo((object)"Not running in VR, doing nothing");
				yield break;
			}
			configMenuType = Type.GetType("LethalConfig.MonoBehaviours.ConfigMenu, LethalConfig");
			cosmeticType = Type.GetType("MoreCompany.Cosmetics.CosmeticInstance, MoreCompany");
			if (fixInteriors.Value)
			{
				((MonoBehaviour)this).StartCoroutine(FixInteriorLoop());
			}
			while (true)
			{
				yield return (object)new WaitForSeconds(1f);
				try
				{
					if (configMenuType != null)
					{
						FixConfigMenus();
					}
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"Failed to fix LethalConfig text: {arg}");
				}
				try
				{
					FixWorld();
				}
				catch (Exception arg2)
				{
					Log.LogError((object)$"Failed to fix cosmetics/items: {arg2}");
				}
			}
		}

		private bool LoadGameShaders()
		{
			if (gameShaders.Count > 0)
			{
				return true;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.playerPrefab == (Object)null)
			{
				return false;
			}
			Renderer[] componentsInChildren = instance.playerPrefab.GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Material[] sharedMaterials = componentsInChildren[i].sharedMaterials;
				foreach (Material val in sharedMaterials)
				{
					if ((Object)(object)val != (Object)null && (Object)(object)val.shader != (Object)null && !gameShaders.ContainsKey(((Object)val.shader).name))
					{
						gameShaders[((Object)val.shader).name] = val.shader;
					}
				}
			}
			if (gameShaders.Count > 0)
			{
				Log.LogInfo((object)("Game shaders from player prefab: " + string.Join(", ", gameShaders.Keys)));
			}
			return gameShaders.Count > 0;
		}

		private void FixWorld()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (((!fixCosmetics.Value || cosmeticType == null) && !fixItems.Value) || !LoadGameShaders())
			{
				return;
			}
			int num = worldFixed;
			if (fixCosmetics.Value && cosmeticType != null)
			{
				Object[] array = Object.FindObjectsOfType(cosmeticType, true);
				foreach (Object val in array)
				{
					FixRenderers(((Component)val).gameObject, "cosmetic");
				}
			}
			if (fixItems.Value)
			{
				GrabbableObject[] array2 = Object.FindObjectsOfType<GrabbableObject>();
				foreach (GrabbableObject val2 in array2)
				{
					FixRenderers(((Component)val2).gameObject, "item");
				}
			}
			if (worldFixed > num)
			{
				Log.LogInfo((object)$"Switched {worldFixed - num} cosmetic/item material(s) to the game's shaders ({worldFixed} total)");
			}
		}

		private void FixRenderers(GameObject root, string kind)
		{
			Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer renderer in componentsInChildren)
			{
				FixRenderer(renderer, kind, ((Object)root).name);
			}
		}

		private void FixRenderer(Renderer renderer, string kind, string ownerName)
		{
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			Material[] sharedMaterials = renderer.sharedMaterials;
			bool flag = false;
			for (int i = 0; i < sharedMaterials.Length; i++)
			{
				Material val = sharedMaterials[i];
				if ((Object)(object)val == (Object)null || (Object)(object)val.shader == (Object)null)
				{
					continue;
				}
				if (!gameShaders.TryGetValue(((Object)val.shader).name, out var value))
				{
					if (reportedUnfixable.Add(((Object)val.shader).name))
					{
						Log.LogInfo((object)("Can't fix " + kind + " '" + ownerName + "': shader '" + ((Object)val.shader).name + "' has no game version"));
					}
				}
				else if (!((Object)(object)val.shader == (Object)(object)value))
				{
					if (!convertedWorld.TryGetValue(val, out var value2) || (Object)(object)value2 == (Object)null)
					{
						value2 = new Material(val)
						{
							shader = value,
							renderQueue = val.renderQueue,
							name = ((Object)val).name + " (VR)"
						};
						convertedWorld[val] = value2;
					}
					sharedMaterials[i] = value2;
					flag = true;
					worldFixed++;
				}
			}
			if (flag)
			{
				renderer.sharedMaterials = sharedMaterials;
			}
		}

		private IEnumerator FixInteriorLoop()
		{
			int lastDungeon = 0;
			while (true)
			{
				yield return (object)new WaitForSeconds(2f);
				RoundManager round = RoundManager.Instance;
				GameObject root = ((!((Object)(object)round != (Object)null) || !round.dungeonCompletedGenerating) ? null : round.dungeonGenerator?.Generator?.Root);
				if ((Object)(object)root == (Object)null || ((Object)root).GetInstanceID() == lastDungeon || !LoadGameShaders())
				{
					continue;
				}
				lastDungeon = ((Object)root).GetInstanceID();
				Renderer[] renderers = root.GetComponentsInChildren<Renderer>(true);
				int before = worldFixed;
				for (int i = 0; i < renderers.Length; i++)
				{
					try
					{
						if ((Object)(object)renderers[i] != (Object)null)
						{
							ShaderFixer shaderFixer = this;
							Renderer renderer = renderers[i];
							DungeonFlow dungeonFlow = round.dungeonGenerator.Generator.DungeonFlow;
							shaderFixer.FixRenderer(renderer, "interior", ((dungeonFlow != null) ? ((Object)dungeonFlow).name : null) ?? ((Object)root).name);
						}
					}
					catch (Exception ex)
					{
						Log.LogError((object)("Failed to fix interior renderer: " + ex.Message));
					}
					if (i % 250 == 249)
					{
						yield return null;
					}
				}
				Log.LogInfo((object)$"Interior: switched {worldFixed - before} material(s) on {renderers.Length} renderers to the game's shaders");
			}
		}

		private void FixConfigMenus()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			Object[] array = Object.FindObjectsOfType(configMenuType, true);
			if (array.Length == 0)
			{
				return;
			}
			if ((Object)(object)vrTextShader == (Object)null)
			{
				ref Shader reference = ref vrTextShader;
				object? obj = Type.GetType("LCVR.Assets.AssetManager, LCVR")?.GetField("TMPAlwaysOnTop", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				reference = (Shader)((obj is Shader) ? obj : null);
				if ((Object)(object)vrTextShader == (Object)null)
				{
					return;
				}
			}
			Object[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				Transform transform = ((Component)array2[i]).transform;
				FixTree(transform);
				if ((Object)(object)transform.parent == (Object)null)
				{
					continue;
				}
				foreach (Transform item in transform.parent)
				{
					Transform val = item;
					if ((Object)(object)val != (Object)(object)transform && ((Object)val).name.StartsWith("ConfigMenu"))
					{
						FixTree(val);
					}
				}
			}
		}

		private void FixTree(Transform root)
		{
			int num = 0;
			TMP_Text[] componentsInChildren = ((Component)root).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				Material val2 = Convert(val.fontSharedMaterial);
				if ((Object)(object)val2 != (Object)null)
				{
					val.fontSharedMaterial = val2;
					num++;
				}
			}
			TMP_SubMeshUI[] componentsInChildren2 = ((Component)root).GetComponentsInChildren<TMP_SubMeshUI>(true);
			foreach (TMP_SubMeshUI val3 in componentsInChildren2)
			{
				Material val4 = Convert(val3.sharedMaterial);
				if ((Object)(object)val4 != (Object)null)
				{
					val3.sharedMaterial = val4;
					num++;
				}
			}
			if (num > 0 && !loggedFirstFix)
			{
				loggedFirstFix = true;
				Log.LogInfo((object)$"Switched LethalConfig text to LCVR's VR text shader ({num} elements under {((Object)root).name})");
			}
		}

		private Material Convert(Material source)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			if ((Object)(object)source == (Object)null || (Object)(object)source.shader == (Object)(object)vrTextShader)
			{
				return null;
			}
			if (!converted.TryGetValue(source, out var value) || (Object)(object)value == (Object)null)
			{
				value = new Material(source)
				{
					shader = vrTextShader,
					name = ((Object)source).name + " (VR)"
				};
				converted[source] = value;
			}
			return value;
		}
	}
}
namespace LCVRFixes
{
	internal static class ConfigMigration
	{
		public static void FromOldPlugins(ConfigFile config)
		{
			int num = 0;
			num += Copy(config, "kylew.lcvrfixes.cfg", (string section, string key) => (section, key));
			num += Copy(config, "kylew.vrshaderfix.cfg", (string section, string key) => ("Shaders", key));
			if (num > 0)
			{
				config.Save();
				Plugin.Log.LogInfo((object)$"Copied {num} setting(s) from the old LCVRFixes / VRShaderFix configs");
			}
		}

		private static int Copy(ConfigFile config, string oldFile, Func<string, string, (string, string)> map)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			string path = Path.Combine(Paths.ConfigPath, oldFile);
			if (!File.Exists(path))
			{
				return 0;
			}
			int num = 0;
			try
			{
				foreach (var item6 in Read(path))
				{
					string item = item6.section;
					string item2 = item6.key;
					string item3 = item6.value;
					(string, string) tuple = map(item, item2);
					string item4 = tuple.Item1;
					string item5 = tuple.Item2;
					ConfigDefinition val = new ConfigDefinition(item4, item5);
					if (config.ContainsKey(val))
					{
						config[val].SetSerializedValue(item3);
						num++;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Couldn't read old settings from " + oldFile + ": " + ex.Message));
			}
			return num;
		}

		private static IEnumerable<(string section, string key, string value)> Read(string path)
		{
			string section = "";
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0 || text.StartsWith("#"))
				{
					continue;
				}
				if (text.StartsWith("[") && text.EndsWith("]"))
				{
					section = text.Substring(1, text.Length - 2).Trim();
					continue;
				}
				int num = text.IndexOf('=');
				if (num > 0)
				{
					yield return (section: section, key: text.Substring(0, num).Trim(), value: text.Substring(num + 1).Trim());
				}
			}
		}
	}
	internal static class EveryoneFixes
	{
		private static Dictionary<string, Type> vrItemTypes;

		private static Type vrShovelType;

		internal static readonly Dictionary<GearStick, ushort> gearStickHolders = new Dictionary<GearStick, ushort>();

		private static FieldInfo gearIsHeld;

		private static FieldInfo gearIsHeldByLocal;

		internal static FieldInfo leverActor;

		internal static FieldInfo leverRotateTo;

		internal static FieldInfo leverAnimator;

		internal static FieldInfo leverShouldTrigger;

		private static FieldInfo muffledPlayers;

		private static FieldInfo networkPlayers;

		private static FieldInfo clientsField;

		private static FieldInfo subscribersField;

		private static FieldInfo instanceField;

		private static MethodInfo initializedGetter;

		private static MethodInfo sendPacket;

		private static object handshakeResponse;

		private static Type clientInfoType;

		public static void ApplyItemErrors()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			vrItemTypes = (Dictionary<string, Type>)AccessTools.Field(AccessTools.TypeByName("LCVR.Player.Items"), "items").GetValue(null);
			vrShovelType = AccessTools.TypeByName("LCVR.Items.VRShovelItem");
			Type type = AccessTools.TypeByName("LCVR.Patches.UniversalItemPatches");
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(type, "HandleUpdateItemOffset", (Type[])null, (Type[])null), new HarmonyMethod(typeof(EveryoneFixes), "ItemOffsetPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			HarmonyMethod val = new HarmonyMethod(typeof(EveryoneFixes), "SwallowSlotSwitch", (Type[])null);
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(AccessTools.TypeByName("LCVR.Patches.PlayerControllerPatches"), "SwitchedToItemSlot", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null);
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(AccessTools.TypeByName("LCVR.Patches.UniversalPlayerControllerPatches"), "SwitchedToItemSlot", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null);
		}

		private static bool ItemOffsetPrefix(GrabbableObject item, ref bool __result)
		{
			if (item.itemProperties == null)
			{
				__result = false;
				return false;
			}
			if (!Object.op_Implicit((Object)(object)item.itemProperties))
			{
				__result = true;
				return false;
			}
			if (((object)item).GetType() == typeof(Shovel) && vrItemTypes.ContainsKey(((Object)item.itemProperties).name) && (Object)(object)((Component)item).GetComponent(vrShovelType) == (Object)null)
			{
				__result = true;
				return false;
			}
			return true;
		}

		private static Exception SwallowSlotSwitch(Exception __exception)
		{
			if (__exception != null)
			{
				Plugin.ReportOnce("LCVR slot switch", __exception);
			}
			return null;
		}

		public static void ApplyPlayerIdMixup()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Expected O, but got Unknown
			HarmonyMethod val = new HarmonyMethod(typeof(EveryoneFixes), "SlotLookupTranspiler", (Type[])null);
			MethodInfo[] array = new MethodInfo[5]
			{
				AccessTools.Method(AccessTools.TypeByName("LCVR.Patches.UniversalItemPatches"), "HandleUpdateItemOffset", (Type[])null, (Type[])null),
				AccessTools.Method(AccessTools.TypeByName("LCVR.Patches.Items.FlashlightItemPatches"), "SwitchFlashlightPatch", (Type[])null, (Type[])null),
				AccessTools.Method(AccessTools.TypeByName("LCVR.Patches.Items.FlashlightItemPatches"), "PocketFlashlightPatch", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(SpectatingPlayer), "UpdateState", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(SpectatingManager), "TeleportToPlayer", (Type[])null, (Type[])null)
			};
			foreach (MethodInfo methodInfo in array)
			{
				if (methodInfo == null)
				{
					Plugin.Log.LogWarning((object)"A player-ID lookup target wasn't found; skipping it");
				}
				else
				{
					Plugin.Harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(AccessTools.TypeByName("LCVR.Patches.Items.FlashlightItemPatches"), "PocketFlashlightPatch", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(EveryoneFixes), "SwallowFlashlight", (Type[])null), (HarmonyMethod)null);
		}

		private static IEnumerable<CodeInstruction> SlotLookupTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo isInVR = AccessTools.Method(typeof(NetworkSystem), "IsInVR", (Type[])null, (Type[])null);
			MethodInfo tryGetPlayer = AccessTools.Method(typeof(NetworkSystem), "TryGetPlayer", (Type[])null, (Type[])null);
			MethodInfo isInVRBySlot = AccessTools.Method(typeof(EveryoneFixes), "IsInVRBySlot", (Type[])null, (Type[])null);
			MethodInfo tryGetBySlot = AccessTools.Method(typeof(EveryoneFixes), "TryGetPlayerBySlot", (Type[])null, (Type[])null);
			foreach (CodeInstruction instruction in instructions)
			{
				if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo methodInfo)
				{
					if (methodInfo == isInVR)
					{
						instruction.opcode = OpCodes.Call;
						instruction.operand = isInVRBySlot;
					}
					else if (methodInfo == tryGetPlayer)
					{
						instruction.opcode = OpCodes.Call;
						instruction.operand = tryGetBySlot;
					}
				}
				yield return instruction;
			}
		}

		public static bool IsInVRBySlot(NetworkSystem _, ushort slot)
		{
			VRNetPlayer player;
			return TryGetPlayerBySlot(_, slot, out player);
		}

		public static bool TryGetPlayerBySlot(NetworkSystem _, ushort slot, out VRNetPlayer player)
		{
			player = null;
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if ((Object)(object)val != (Object)null && (ushort)val.playerClientId == slot)
				{
					player = ((Component)val).GetComponent<VRNetPlayer>();
					return (Object)(object)player != (Object)null;
				}
			}
			return false;
		}

		private static Exception SwallowFlashlight(Exception __exception)
		{
			if (__exception != null)
			{
				Plugin.ReportOnce("LCVR pocketed flashlight sync", __exception);
			}
			return null;
		}

		public static void ApplyStuckLeverAndGearStick()
		{
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			//IL_00e3: Expected O, but got Unknown
			gearIsHeld = AccessTools.Field(typeof(GearStick), "isHeld");
			gearIsHeldByLocal = AccessTools.Field(typeof(GearStick), "isHeldByLocal");
			leverActor = AccessTools.Field(typeof(ShipLever), "currentActor");
			leverRotateTo = AccessTools.Field(typeof(ShipLever), "rotateTo");
			leverAnimator = AccessTools.Field(typeof(ShipLever), "animator");
			leverShouldTrigger = AccessTools.Field(typeof(ShipLever), "shouldTrigger");
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(GearStick), "OnPacketReceived", (Type[])null, (Type[])null), new HarmonyMethod(typeof(EveryoneFixes), "GearPacketPrefix", (Type[])null), new HarmonyMethod(typeof(EveryoneFixes), "GearPacketPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Watchdog.CheckLeversAndGearSticks = true;
		}

		private static void GearPacketPrefix(GearStick __instance, out bool __state)
		{
			__state = (bool)gearIsHeld.GetValue(__instance);
		}

		private static void GearPacketPostfix(GearStick __instance, ushort sender, bool __state)
		{
			bool flag = (bool)gearIsHeld.GetValue(__instance);
			if (!__state && flag && !(bool)gearIsHeldByLocal.GetValue(__instance))
			{
				gearStickHolders[__instance] = sender;
			}
			else if (__state && !flag)
			{
				gearStickHolders.Remove(__instance);
			}
		}

		internal static void ReleaseAbandonedLeversAndGearSticks()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			NetworkSystem instance = NetworkSystem.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			VRNetPlayer[] players = instance.Players;
			ShipLever[] array = Object.FindObjectsOfType<ShipLever>();
			foreach (ShipLever obj in array)
			{
				if ((int)leverActor.GetValue(obj) != 2)
				{
					continue;
				}
				Transform hand = (Transform)leverRotateTo.GetValue(obj);
				if (!((Object)(object)hand != (Object)null) || !players.Any((VRNetPlayer p) => (Object)(object)p != (Object)null && (Object)(object)p.Bones.RightHand == (Object)(object)hand))
				{
					leverRotateTo.SetValue(obj, null);
					leverShouldTrigger.SetValue(obj, Enum.ToObject(leverShouldTrigger.FieldType, 0));
					leverActor.SetValue(obj, (object)(Actor)0);
					object? value = leverAnimator.GetValue(obj);
					Behaviour val = (Behaviour)((value is Behaviour) ? value : null);
					if (val != null && (Object)(object)val != (Object)null)
					{
						val.enabled = true;
					}
					Plugin.Log.LogInfo((object)"Released ship lever held by a VR player who left");
				}
			}
			GearStick[] array2 = Object.FindObjectsOfType<GearStick>();
			VRNetPlayer val3 = default(VRNetPlayer);
			foreach (GearStick val2 in array2)
			{
				if ((bool)gearIsHeld.GetValue(val2) && !(bool)gearIsHeldByLocal.GetValue(val2) && (gearStickHolders.TryGetValue(val2, out var value2) ? (instance.TryGetPlayer(value2, ref val3) ? 1 : 0) : players.Length) == 0)
				{
					gearIsHeld.SetValue(val2, false);
					gearStickHolders.Remove(val2);
					Plugin.Log.LogInfo((object)"Released gear stick held by a VR player who left");
				}
			}
			foreach (GearStick item in gearStickHolders.Keys.Where((GearStick k) => (Object)(object)k == (Object)null).ToList())
			{
				gearStickHolders.Remove(item);
			}
		}

		public static void ApplyMuffleFixes()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Expected O, but got Unknown
			//IL_00f3: Expected O, but got Unknown
			muffledPlayers = AccessTools.Field(typeof(MuffleManager), "muffledPlayers");
			networkPlayers = AccessTools.Field(typeof(NetworkSystem), "players");
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(AccessTools.TypeByName("LCVR.Physics.Interactions.UniversalMufflePatches"), "OnUpdatePlayerVoiceEffects", (Type[])null, (Type[])null), new HarmonyMethod(typeof(EveryoneFixes), "VoiceEffectsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(VRNetPlayer), "OnDestroy", (Type[])null, (Type[])null), new HarmonyMethod(typeof(EveryoneFixes), "VRNetPlayerDestroyedPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(Muffler), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(EveryoneFixes), "MufflerPrefix", (Type[])null), new HarmonyMethod(typeof(EveryoneFixes), "MufflerPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static bool VoiceEffectsPrefix(StartOfRound __instance)
		{
			try
			{
				MuffleManager val = (((Object)(object)VRSession.Instance != (Object)null) ? VRSession.Instance.MuffleManager : null);
				NetworkSystem instance = NetworkSystem.Instance;
				if (val == null || (Object)(object)instance == (Object)null)
				{
					return false;
				}
				ushort[] array = ((HashSet<ushort>)muffledPlayers.GetValue(val)).ToArray();
				VRNetPlayer val2 = default(VRNetPlayer);
				foreach (ushort num in array)
				{
					if (!instance.TryGetPlayer(num, ref val2) || (Object)(object)val2 == (Object)null)
					{
						continue;
					}
					PlayerControllerB playerController = val2.PlayerController;
					if (!((Object)(object)playerController == (Object)null) && !((Object)(object)playerController == (Object)(object)__instance.localPlayerController) && !playerController.isPlayerDead && !((Object)(object)playerController.currentVoiceChatAudioSource == (Object)null))
					{
						OccludeAudio component = ((Component)playerController.currentVoiceChatAudioSource).GetComponent<OccludeAudio>();
						if (!((Object)(object)component == (Object)null))
						{
							component.overridingLowPass = true;
							component.lowPassOverride = 1000f;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.ReportOnce("muffle voice effects", ex);
			}
			return false;
		}

		private static void VRNetPlayerDestroyedPrefix(VRNetPlayer __instance)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			try
			{
				NetworkSystem instance = NetworkSystem.Instance;
				MuffleManager val = (((Object)(object)VRSession.Instance != (Object)null) ? VRSession.Instance.MuffleManager : null);
				if ((Object)(object)instance == (Object)null || val == null)
				{
					return;
				}
				IDictionary obj = (IDictionary)networkPlayers.GetValue(instance);
				HashSet<ushort> hashSet = (HashSet<ushort>)muffledPlayers.GetValue(val);
				foreach (DictionaryEntry item in obj)
				{
					if ((Object)item.Value == (Object)(object)__instance)
					{
						hashSet.Remove((ushort)item.Key);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.ReportOnce("muffle cleanup", ex);
			}
		}

		private static void MufflerPrefix(Muffler __instance, out bool __state)
		{
			__state = __instance.Muffled;
		}

		private static void MufflerPostfix(Muffler __instance, bool __state)
		{
			if (__state && !__instance.Muffled && (Object)(object)VRSession.Instance != (Object)null)
			{
				VRSession.Instance.MuffleManager.SetLocalMuffled(false);
			}
		}

		public static void ApplyLateJoinHands()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			clientsField = AccessTools.Field(typeof(NetworkSystem), "clients");
			subscribersField = AccessTools.Field(typeof(NetworkSystem), "subscribers");
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(NetworkSystem), "HandleHandshakeRequest", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(EveryoneFixes), "HandshakePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void HandshakePostfix(NetworkSystem __instance, ushort sender, ushort protocol)
		{
			if (protocol == 9 && ((IDictionary)clientsField.GetValue(__instance)).Contains(sender))
			{
				((HashSet<ushort>)subscribersField.GetValue(__instance)).Add(sender);
			}
		}

		public static void ApplyAnnounceVR()
		{
			clientsField = AccessTools.Field(typeof(NetworkSystem), "clients");
			instanceField = AccessTools.Field(typeof(NetworkSystem), "_instance");
			initializedGetter = AccessTools.PropertyGetter(typeof(NetworkSystem), "Initialized");
			sendPacket = AccessTools.Method(typeof(NetworkSystem), "SendPacket", (Type[])null, (Type[])null);
			if (clientsField == null || instanceField == null || initializedGetter == null || sendPacket == null)
			{
				throw new MissingMemberException("LCVR NetworkSystem internals changed");
			}
			ParameterInfo[] parameters = sendPacket.GetParameters();
			handshakeResponse = Enum.Parse(parameters[0].ParameterType, "HandshakeResponse");
			clientInfoType = parameters[2].ParameterType.GetElementType();
			Watchdog.AnnounceVR = true;
		}

		internal static void AnnounceInVR()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			if (!VRSession.InVR)
			{
				return;
			}
			NetworkSystem val = (NetworkSystem)instanceField.GetValue(null);
			if (!((Object)(object)val == (Object)null) && (bool)initializedGetter.Invoke(val, null))
			{
				IDictionary dictionary = (IDictionary)clientsField.GetValue(val);
				if (dictionary.Count != 0)
				{
					Array array = Array.CreateInstance(clientInfoType, dictionary.Count);
					dictionary.Values.CopyTo(array, 0);
					sendPacket.Invoke(val, new object[3]
					{
						handshakeResponse,
						new byte[1] { 1 },
						array
					});
				}
			}
		}
	}
	internal class Watchdog : MonoBehaviour
	{
		internal static bool CheckLeversAndGearSticks;

		internal static bool AnnounceVR;

		private float nextCheck;

		private float nextAnnounce;

		private void Update()
		{
			if (AnnounceVR && Time.unscaledTime >= nextAnnounce)
			{
				nextAnnounce = Time.unscaledTime + 5f;
				try
				{
					EveryoneFixes.AnnounceInVR();
				}
				catch (Exception ex)
				{
					Plugin.ReportOnce("announcing VR to other players", ex);
				}
			}
			if (!CheckLeversAndGearSticks || Time.unscaledTime < nextCheck)
			{
				return;
			}
			nextCheck = Time.unscaledTime + 0.5f;
			try
			{
				EveryoneFixes.ReleaseAbandonedLeversAndGearSticks();
			}
			catch (Exception ex2)
			{
				Plugin.ReportOnce("lever/gear stick watchdog", ex2);
			}
		}
	}
	[BepInPlugin("boneman.lcvrfixes", "Boneman's LCVR Fixes", "2.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "boneman.lcvrfixes";

		public const string Name = "Boneman's LCVR Fixes";

		public const string Version = "2.0.1";

		internal static ManualLogSource Log;

		internal static Harmony Harmony;

		internal static ConfigEntry<bool> RenderEyesSeparatelyToggle;

		internal static ConfigEntry<bool> WristInventoryEnabled;

		internal static ConfigEntry<int> WristInventorySlots;

		internal static ConfigEntry<WristPlacement> WristPlacement;

		internal static ConfigEntry<string> WristSavedPosition;

		internal static ConfigEntry<string> WristSavedRotation;

		internal static ConfigEntry<float> WristGrabHoldSeconds;

		internal static ConfigEntry<bool> FingerHysteresis;

		internal static ConfigEntry<bool> RightHandFingersWhileHolding;

		internal static ConfigEntry<bool> PauseMenuStranding;

		internal static ConfigEntry<bool> PauseRenderScale;

		internal static ConfigEntry<bool> CruiserPausedInput;

		internal static ConfigEntry<bool> AnnounceVR;

		internal static ConfigEntry<bool> ItemErrors;

		internal static ConfigEntry<bool> PlayerIdMixup;

		internal static ConfigEntry<bool> StuckLeverAndGearStick;

		internal static ConfigEntry<bool> MuffleFixes;

		internal static ConfigEntry<bool> LateJoinHands;

		internal static ConfigEntry<bool> SmoothRemoteHands;

		internal static ConfigEntry<float> RemoteHandSmoothing;

		internal static ConfigEntry<float> RemoteFingerSmoothing;

		private static readonly HashSet<string> reported = new HashSet<string>();

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0520: Unknown result type (might be due to invalid IL or missing references)
			//IL_0527: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Harmony = new Harmony("boneman.lcvrfixes");
			bool num = !File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath);
			RenderEyesSeparatelyToggle = ((BaseUnityPlugin)this).Config.Bind<bool>("Rendering", "RenderEyesSeparately", true, "Render each eye as its own pass. Fixes every mod object/shader that floats, wobbles or shows in one eye (cosmetics, scrap, interiors, ship parts) at the cost of extra CPU. Restart the game after changing it: OpenXR has to be set up for it before the game starts.");
			WristInventoryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Wrist inventory", "Enabled", true, "Show your inventory as a strip along your left forearm when you look at it; poke a slot with your right index finger to switch to it.");
			WristInventorySlots = ((BaseUnityPlugin)this).Config.Bind<int>("Wrist inventory", "Slots", 4, "How many inventory slots to show on the strip (4 = the normal slots; ReservedItemSlots' key/lockpicker slots come after these).");
			WristPlacement = ((BaseUnityPlugin)this).Config.Bind<WristPlacement>("Wrist inventory", "Placement", LCVRFixes.WristPlacement.FollowEyes, "FollowEyes: the strip turns around your forearm to face you. Fixed: it stays where you put it. Grab the handle with your right hand to place it (switches to Fixed); poke the red button to go back to FollowEyes.");
			WristSavedPosition = ((BaseUnityPlugin)this).Config.Bind<string>("Wrist inventory", "SavedPosition", "", "Set automatically when you drop the strip after grabbing it. Clear to forget.");
			WristSavedRotation = ((BaseUnityPlugin)this).Config.Bind<string>("Wrist inventory", "SavedRotation", "", "Set automatically when you drop the strip after grabbing it. Clear to forget.");
			WristGrabHoldSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Wrist inventory", "GrabHoldSeconds", 3f, "How long you have to keep your hand closed on the handle (a ring fills up) before the strip comes loose. 0 = grab instantly.");
			FingerHysteresis = ((BaseUnityPlugin)this).Config.Bind<bool>("VR player", "FingerHysteresis", true, "Stop touch buttons (light switches, breakers, car horn, charger) from re-triggering when a tracked finger hovers around LCVR's 75% curl threshold.");
			RightHandFingersWhileHolding = ((BaseUnityPlugin)this).Config.Bind<bool>("VR player", "RightHandFingersWhileHolding", true, "Keep reading right-hand finger curls while holding an item, so finger gestures and what other players see don't freeze.");
			PauseMenuStranding = ((BaseUnityPlugin)this).Config.Bind<bool>("VR player", "PauseMenuStranding", true, "Don't enter LCVR's pause scene when another mod (TooManyEmotes, GeneralImprovements build mode) cancelled the pause menu.");
			PauseRenderScale = ((BaseUnityPlugin)this).Config.Bind<bool>("VR player", "PauseRenderScale", true, "Keep your configured render scale while paused instead of LCVR's forced 1.2 (avoids a stutter every time you pause).");
			CruiserPausedInput = ((BaseUnityPlugin)this).Config.Bind<bool>("VR player", "CruiserPausedInput", true, "Stop the Cruiser from accelerating while the VR driver has the pause menu open or is typing in chat.");
			AnnounceVR = ((BaseUnityPlugin)this).Config.Bind<bool>("VR player", "AnnounceVR", true, "Keep telling other players you're in VR, so they always see your hands and fingers. LCVR sometimes misses you when you join a lobby that's already running. Others only need LCVR for this.");
			ItemErrors = ((BaseUnityPlugin)this).Config.Bind<bool>("Everyone", "ItemErrors", true, "Stop LCVR's per-frame errors and frozen items when held items have missing or unloaded item data (modded scrap, mid-round joins).");
			PlayerIdMixup = ((BaseUnityPlugin)this).Config.Bind<bool>("Everyone", "PlayerIdMixup", true, "Fix LCVR mixing up voice-chat IDs with player slots, which applied VR item offsets / flashlight sync / muffling to the wrong player.");
			StuckLeverAndGearStick = ((BaseUnityPlugin)this).Config.Bind<bool>("Everyone", "StuckLeverAndGearStick", true, "Release the ship lever and Cruiser gear stick if the VR player holding them leaves or disconnects.");
			MuffleFixes = ((BaseUnityPlugin)this).Config.Bind<bool>("Everyone", "MuffleFixes", true, "Muffle (hand over mouth) the right player, clear it when they leave, and unmuffle when grabbing a two-handed item.");
			LateJoinHands = ((BaseUnityPlugin)this).Config.Bind<bool>("Everyone", "LateJoinHands", true, "Start sending VR hand data to players who join mid-round right away instead of after up to 10 seconds.");
			SmoothRemoteHands = ((BaseUnityPlugin)this).Config.Bind<bool>("Remote hands", "SmoothRemoteHands", true, "Smooth other VR players' hands, head and fingers on your screen and remove the backwards body snap when they walk in roomscale.");
			RemoteHandSmoothing = ((BaseUnityPlugin)this).Config.Bind<float>("Remote hands", "HandSmoothing", 18f, "How quickly remote hands follow new data. Higher = snappier, lower = smoother.");
			RemoteFingerSmoothing = ((BaseUnityPlugin)this).Config.Bind<float>("Remote hands", "FingerSmoothing", 15f, "How quickly remote fingers follow new data. Higher = snappier, lower = smoother.");
			ShaderFixer.Bind(((BaseUnityPlugin)this).Config, out var enabled, out var cosmetics, out var items, out var interiors);
			if (num)
			{
				ConfigMigration.FromOldPlugins(((BaseUnityPlugin)this).Config);
			}
			try
			{
				RenderEyesSeparately.Apply();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Couldn't set up per-eye rendering, skipping it: {arg}");
			}
			Apply("finger hysteresis", FingerHysteresis, VRPlayerFixes.ApplyFingerHysteresis);
			Apply("right-hand fingers while holding", RightHandFingersWhileHolding, VRPlayerFixes.ApplyRightHandWhileHolding);
			Apply("pause menu stranding", PauseMenuStranding, VRPlayerFixes.ApplyPauseMenuStranding);
			Apply("pause render scale", PauseRenderScale, VRPlayerFixes.ApplyPauseRenderScale);
			Apply("Cruiser input while paused", CruiserPausedInput, VRPlayerFixes.ApplyCruiserPausedInput);
			Apply("VR announce", AnnounceVR, EveryoneFixes.ApplyAnnounceVR);
			Apply("item errors", ItemErrors, EveryoneFixes.ApplyItemErrors);
			Apply("player ID mix-up", PlayerIdMixup, EveryoneFixes.ApplyPlayerIdMixup);
			Apply("stuck lever and gear stick", StuckLeverAndGearStick, EveryoneFixes.ApplyStuckLeverAndGearStick);
			Apply("muffle fixes", MuffleFixes, EveryoneFixes.ApplyMuffleFixes);
			Apply("late-join hands", LateJoinHands, EveryoneFixes.ApplyLateJoinHands);
			Apply("smooth remote hands", SmoothRemoteHands, RemoteHands.Apply);
			GameObject val = new GameObject("BonemansLCVRFixes");
			Object.DontDestroyOnLoad((Object)(object)val);
			((Object)val).hideFlags = (HideFlags)61;
			val.AddComponent<Watchdog>();
			if (WristInventoryEnabled.Value)
			{
				val.AddComponent<WristInventory>();
			}
			val.AddComponent<ShaderFixer>().Init(Log, enabled, cosmetics, items, interiors);
		}

		private static void Apply(string name, ConfigEntry<bool> toggle, Action apply)
		{
			if (!toggle.Value)
			{
				return;
			}
			try
			{
				apply();
				Log.LogInfo((object)("Applied " + name));
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Couldn't apply {name}, skipping it: {arg}");
			}
		}

		internal static void ReportOnce(string where, Exception ex)
		{
			if (reported.Add(where))
			{
				Log.LogWarning((object)("Suppressed error in " + where + ": " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	internal static class RemoteHands
	{
		private class State
		{
			public bool initialized;

			public Vector3 leftPos;

			public Vector3 rightPos;

			public Vector3 leftPosTarget;

			public Vector3 rightPosTarget;

			public Quaternion leftRot;

			public Quaternion rightRot;

			public Quaternion leftRotTarget;

			public Quaternion rightRotTarget;

			public Quaternion head;

			public Quaternion headTarget;

			public readonly float[] fingers = new float[10];

			public readonly float[] fingersTarget = new float[10];

			public Vector3 rawOffset;

			public Vector3 pending;

			public Vector3 pendingRoot;

			public float pendingTime;
		}

		private static readonly ConditionalWeakTable<VRNetPlayer, State> states = new ConditionalWeakTable<VRNetPlayer, State>();

		private static FieldInfo leftController;

		private static FieldInfo rightController;

		private static FieldInfo cameraEulers;

		private static FieldInfo modelOffset;

		public static void Apply()
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			leftController = AccessTools.Field(typeof(VRNetPlayer), "leftController");
			rightController = AccessTools.Field(typeof(VRNetPlayer), "rightController");
			cameraEulers = AccessTools.Field(typeof(VRNetPlayer), "cameraEulers");
			modelOffset = AccessTools.Field(typeof(VRNetPlayer), "modelOffset");
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(VRNetPlayer), "OnRigDataReceived", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(RemoteHands), "RigReceivedPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(VRNetPlayer), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(RemoteHands), "UpdatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void RigReceivedPostfix(VRNetPlayer __instance)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: 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)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: 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_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				State orCreateValue = states.GetOrCreateValue(__instance);
				Transform val = (Transform)leftController.GetValue(__instance);
				Transform val2 = (Transform)rightController.GetValue(__instance);
				orCreateValue.leftPosTarget = val.localPosition;
				orCreateValue.leftRotTarget = val.localRotation;
				orCreateValue.rightPosTarget = val2.localPosition;
				orCreateValue.rightRotTarget = val2.localRotation;
				orCreateValue.headTarget = Quaternion.Euler((Vector3)cameraEulers.GetValue(__instance));
				ReadFingers(__instance, orCreateValue.fingersTarget);
				Vector3 val3 = (Vector3)modelOffset.GetValue(__instance);
				if (((Vector3)(ref orCreateValue.rawOffset)).magnitude > 0.25f && ((Vector3)(ref val3)).magnitude < ((Vector3)(ref orCreateValue.rawOffset)).magnitude * 0.4f)
				{
					orCreateValue.pending += orCreateValue.rawOffset - val3;
					orCreateValue.pendingRoot = ((Component)__instance).transform.position;
					orCreateValue.pendingTime = Time.time;
				}
				orCreateValue.rawOffset = val3;
				if (!orCreateValue.initialized)
				{
					orCreateValue.initialized = true;
					orCreateValue.leftPos = orCreateValue.leftPosTarget;
					orCreateValue.leftRot = orCreateValue.leftRotTarget;
					orCreateValue.rightPos = orCreateValue.rightPosTarget;
					orCreateValue.rightRot = orCreateValue.rightRotTarget;
					orCreateValue.head = orCreateValue.headTarget;
					orCreateValue.fingersTarget.CopyTo(orCreateValue.fingers, 0);
				}
				else
				{
					val.localPosition = orCreateValue.leftPos;
					val.localRotation = orCreateValue.leftRot;
					val2.localPosition = orCreateValue.rightPos;
					val2.localRotation = orCreateValue.rightRot;
					cameraEulers.SetValue(__instance, ((Quaternion)(ref orCreateValue.head)).eulerAngles);
					WriteFingers(__instance, orCreateValue.fingers);
				}
			}
			catch (Exception ex)
			{
				Plugin.ReportOnce("remote hand smoothing (receive)", ex);
			}
		}

		private static void UpdatePrefix(VRNetPlayer __instance)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_009a: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Expected O, but got Unknown
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: 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_0161: 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_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (states.TryGetValue(__instance, out var value) && value.initialized)
				{
					float deltaTime = Time.deltaTime;
					float num = 1f - Mathf.Exp((0f - Mathf.Max(1f, Plugin.RemoteHandSmoothing.Value)) * deltaTime);
					float num2 = 1f - Mathf.Exp((0f - Mathf.Max(1f, Plugin.RemoteFingerSmoothing.Value)) * deltaTime);
					value.leftPos = Follow(value.leftPos, value.leftPosTarget, num);
					value.rightPos = Follow(value.rightPos, value.rightPosTarget, num);
					value.leftRot = Quaternion.Slerp(value.leftRot, value.leftRotTarget, num);
					value.rightRot = Quaternion.Slerp(value.rightRot, value.rightRotTarget, num);
					value.head = Quaternion.Slerp(value.head, value.headTarget, num);
					for (int i = 0; i < value.fingers.Length; i++)
					{
						value.fingers[i] = Mathf.Lerp(value.fingers[i], value.fingersTarget[i], num2);
					}
					Transform val = (Transform)leftController.GetValue(__instance);
					Transform val2 = (Transform)rightController.GetValue(__instance);
					val.localPosition = value.leftPos;
					val.localRotation = value.leftRot;
					val2.localPosition = value.rightPos;
					val2.localRotation = value.rightRot;
					cameraEulers.SetValue(__instance, ((Quaternion)(ref value.head)).eulerAngles);
					WriteFingers(__instance, value.fingers);
					modelOffset.SetValue(__instance, value.rawOffset + PendingOffset(__instance, value));
				}
			}
			catch (Exception ex)
			{
				Plugin.ReportOnce("remote hand smoothing (update)", ex);
			}
		}

		private static Vector3 Follow(Vector3 current, Vector3 target, float t)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = target - current;
			if (!(((Vector3)(ref val)).sqrMagnitude > 2.25f))
			{
				return Vector3.Lerp(current, target, t);
			}
			return target;
		}

		private static Vector3 PendingOffset(VRNetPlayer player, State state)
		{
			//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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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_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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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)
			//IL_009b: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			if (state.pending == Vector3.zero)
			{
				return Vector3.zero;
			}
			float num = Time.time - state.pendingTime;
			if (num > 1f)
			{
				state.pending = Vector3.zero;
				return Vector3.zero;
			}
			float magnitude = ((Vector3)(ref state.pending)).magnitude;
			Vector3 val = state.pending / magnitude;
			float num2 = Vector3.Dot(((Component)player).transform.position - state.pendingRoot, val);
			Vector3 val2 = state.pending - val * Mathf.Clamp(num2, 0f, magnitude);
			if (num > 0.6f)
			{
				val2 *= Mathf.Clamp01(1f - (num - 0.6f) / 0.4f);
			}
			return val2;
		}

		private static void ReadFingers(VRNetPlayer player, float[] into)
		{
			Read(player.LeftFingerCurler, into, 0);
			Read(player.RightFingerCurler, into, 5);
		}

		private static void WriteFingers(VRNetPlayer player, float[] from)
		{
			Write(player.LeftFingerCurler, from, 0);
			Write(player.RightFingerCurler, from, 5);
		}

		private static void Read(FingerCurler curler, float[] into, int offset)
		{
			if (curler != null)
			{
				into[offset] = curler.thumbFinger.curl;
				into[offset + 1] = curler.indexFinger.curl;
				into[offset + 2] = curler.middleFinger.curl;
				into[offset + 3] = curler.ringFinger.curl;
				into[offset + 4] = curler.pinkyFinger.curl;
			}
		}

		private static void Write(FingerCurler curler, float[] from, int offset)
		{
			if (curler != null)
			{
				curler.thumbFinger.curl = from[offset];
				curler.indexFinger.curl = from[offset + 1];
				curler.middleFinger.curl = from[offset + 2];
				curler.ringFinger.curl = from[offset + 3];
				curler.pinkyFinger.curl = from[offset + 4];
			}
		}
	}
	internal static class RenderEyesSeparately
	{
		private static FieldInfo pipelineSettings;

		public static void Apply()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			pipelineSettings = AccessTools.Field(typeof(HDRenderPipelineAsset), "m_RenderPipelineSettings");
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(Config), "ApplySettings", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(RenderEyesSeparately), "Refresh", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.RenderEyesSeparatelyToggle.SettingChanged += delegate
			{
				Refresh();
			};
			Refresh();
		}

		internal static void Refresh()
		{
			//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_0079: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			if (!VRSession.InVR)
			{
				return;
			}
			RenderPipelineAsset renderPipeline = QualitySettings.renderPipeline;
			HDRenderPipelineAsset val = (HDRenderPipelineAsset)(((object)((renderPipeline is HDRenderPipelineAsset) ? renderPipeline : null)) ?? ((object)/*isinst with value type is only supported in some contexts*/));
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"No HDRP asset found; can't change eye rendering");
				return;
			}
			RenderPipelineSettings val2 = (RenderPipelineSettings)pipelineSettings.GetValue(val);
			bool flag = !Plugin.RenderEyesSeparatelyToggle.Value;
			if (!flag && (int)OpenXRSettings.Instance.renderMode != 0)
			{
				Plugin.Log.LogWarning((object)"Render eyes separately is on, but OpenXR wasn't set up for it (is BepInEx/patchers/BonemansLCVRFixes installed? restart the game after changing the setting). Keeping LCVR's default so both eyes still render.");
				flag = true;
			}
			if (val2.xrSettings.singlePass != flag)
			{
				val2.xrSettings.singlePass = flag;
				pipelineSettings.SetValue(val, val2);
				RebuildPipeline();
				Plugin.Log.LogInfo((object)(flag ? "Rendering both eyes in one pass (LCVR default)" : "Rendering each eye separately: mod shaders show correctly in both eyes"));
			}
		}

		private static void RebuildPipeline()
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(RenderPipelineManager), "CleanupRenderPipeline", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)"Couldn't restart the render pipeline; the eye mode applies after a game restart");
				return;
			}
			methodInfo.Invoke(null, null);
			Plugin.Log.LogInfo((object)"Restarted the render pipeline so the new eye mode applies everywhere");
		}
	}
	internal static class VRPlayerFixes
	{
		private class Latch
		{
			public bool index;

			public bool middle;
		}

		private static readonly ConditionalWeakTable<VRFingerCurler, Latch> latches = new ConditionalWeakTable<VRFingerCurler, Latch>();

		private static MethodInfo updateCurls;

		public static void ApplyFingerHysteresis()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(VRFingerCurler), "UpdateCurls", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(VRPlayerFixes), "HysteresisPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void HysteresisPostfix(VRFingerCurler __instance)
		{
			Latch orCreateValue = latches.GetOrCreateValue(__instance);
			orCreateValue.index = Stabilize(((FingerCurler)__instance).indexFinger, orCreateValue.index);
			orCreateValue.middle = Stabilize(((FingerCurler)__instance).middleFinger, orCreateValue.middle);
		}

		private static bool Stabilize(Finger finger, bool latched)
		{
			if (finger.curl > 0.85f)
			{
				latched = true;
			}
			else if (finger.curl < 0.65f)
			{
				latched = false;
			}
			if (latched && finger.curl <= 0.75f)
			{
				finger.curl = 0.76f;
			}
			else if (!latched && finger.curl > 0.75f)
			{
				finger.curl = 0.74f;
			}
			return latched;
		}

		public static void ApplyRightHandWhileHolding()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			updateCurls = AccessTools.Method(typeof(VRFingerCurler), "UpdateCurls", (Type[])null, (Type[])null);
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(VRPlayer), "LateUpdate", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(VRPlayerFixes), "RightHandPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void RightHandPostfix(VRPlayer __instance)
		{
			try
			{
				VRFingerCurler rightFingerCurler = __instance.RightFingerCurler;
				if (rightFingerCurler != null && rightFingerCurler.Enabled && __instance.PlayerController.isHoldingObject)
				{
					updateCurls.Invoke(rightFingerCurler, null);
				}
			}
			catch (Exception ex)
			{
				Plugin.ReportOnce("right-hand finger update", ex);
			}
		}

		public static void ApplyPauseMenuStranding()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("LCVR.Patches.QuickMenuManagerPatches");
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(type, "AfterOpenPauseMenu", (Type[])null, (Type[])null), new HarmonyMethod(typeof(VRPlayerFixes), "PausePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static bool PausePrefix()
		{
			PlayerControllerB val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.localPlayerController : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val.quickMenuManager == (Object)null)
			{
				return true;
			}
			return val.quickMenuManager.isMenuOpen;
		}

		public static void ApplyPauseRenderScale()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(VRSession), "SwitchToMenuCamera", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(VRPlayerFixes), "RenderScalePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void RenderScalePostfix()
		{
			XRSettings.eyeTextureResolutionScale = Plugin.Config.CameraResolution.Value;
		}

		public static void ApplyCruiserPausedInput()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			Plugin.Harmony.Patch((MethodBase)AccessTools.Method(typeof(VehicleController), "GetVehicleInput", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(VRPlayerFixes), "CruiserPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void CruiserPostfix(VehicleController __instance)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if (VRSession.InVR && __instance.localPlayerInControl)
			{
				PlayerControllerB val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.localPlayerController : null);
				if (!((Object)(object)val == (Object)null) && (((Object)(object)val.quickMenuManager != (Object)null && val.quickMenuManager.isMenuOpen) || val.isTypingChat))
				{
					__instance.moveInputVector = Vector2.zero;
					__instance.drivePedalPressed = false;
					__instance.brakePedalPressed = false;
				}
			}
		}
	}
	public enum WristPlacement
	{
		FollowEyes,
		Fixed
	}
	internal class WristInventory : MonoBehaviour
	{
		private const float SlotSize = 34f;

		private const float SlotSpacing = 40f;

		private const float SurfaceOffset = 0.045f;

		private const float LookAngle = 32f;

		private const float PressDepth = 8f;

		private const float GrabDistance = 0.09f;

		private const float ResetSize = 16f;

		private static readonly FieldInfo throwingObject = AccessTools.Field(typeof(PlayerControllerB), "throwingObject");

		private static readonly FieldInfo timeSinceSwitchingSlots = AccessTools.Field(typeof(PlayerControllerB), "timeSinceSwitchingSlots");

		private static readonly MethodInfo switchToItemSlot = AccessTools.Method(typeof(PlayerControllerB), "SwitchToItemSlot", (Type[])null, (Type[])null);

		private static readonly MethodInfo switchToSlotServerRpc = AccessTools.Method(typeof(PlayerControllerB), "SwitchToSlotServerRpc", (Type[])null, (Type[])null);

		private VRPlayer owner;

		private Canvas canvas;

		private CanvasGroup group;

		private Image[] frames;

		private Image[] icons;

		private Transform fingertip;

		private float visible;

		private bool wasTouching;

		private float cooldownUntil;

		private Image handle;

		private Image reset;

		private bool wasResetTouching;

		private Image ring;

		private float holdTime;

		private bool dragging;

		private bool gripWasClosed;

		private Vector3 dragPosition;

		private Quaternion dragRotation;

		private static Sprite ringSprite;

		private void LateUpdate()
		{
			try
			{
				Tick();
			}
			catch (Exception ex)
			{
				Plugin.ReportOnce("wrist inventory", ex);
				SetShown(shown: false);
			}
		}

		private void Tick()
		{
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: 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)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			VRSession instance = VRSession.Instance;
			VRPlayer val = (((Object)(object)instance != (Object)null) ? instance.LocalPlayer : null);
			if (!VRSession.InVR || (Object)(object)val == (Object)null || (Object)(object)HUDManager.Instance == (Object)null || val.PlayerController.isPlayerDead)
			{
				SetShown(shown: false);
				return;
			}
			if ((Object)(object)owner != (Object)(object)val || (Object)(object)canvas == (Object)null)
			{
				Build(val);
			}
			Bones bones = val.Bones;
			Transform localLeftLowerArm = bones.LocalLeftLowerArm;
			Transform transform = ((Component)instance.MainCamera).transform;
			PlayerControllerB playerController = val.PlayerController;
			bool flag = UpdateDrag(val);
			if (!dragging)
			{
				if (Plugin.WristPlacement.Value == WristPlacement.Fixed && TryGetSavedPose(out var position, out var rotation))
				{
					((Component)canvas).transform.SetPositionAndRotation(localLeftLowerArm.TransformPoint(position), localLeftLowerArm.rotation * rotation);
				}
				else
				{
					PlaceFacingEyes(bones.LocalLeftHand.position, localLeftLowerArm.position, transform.position);
				}
			}
			Vector3 position2 = ((Component)canvas).transform.position;
			int num;
			if (!dragging)
			{
				if (Vector3.Angle(transform.forward, position2 - transform.position) < 32f)
				{
					Vector3 val2 = transform.position - position2;
					num = ((((Vector3)(ref val2)).magnitude < 0.9f) ? 1 : 0);
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 1;
			}
			bool flag2 = (byte)num != 0;
			visible = Mathf.MoveTowards(visible, flag2 ? 1f : 0f, Time.deltaTime * 8f);
			SetShown(visible > 0.01f);
			if (!((Component)canvas).gameObject.activeSelf)
			{
				wasTouching = false;
				wasResetTouching = false;
				return;
			}
			group.alpha = visible;
			((Graphic)handle).color = (dragging ? new Color(0.4f, 1f, 0.5f, 1f) : (flag ? new Color(1f, 0.85f, 0.3f, 1f) : new Color(0.7f, 0.7f, 0.7f, 0.8f)));
			((Component)reset).gameObject.SetActive(Plugin.WristPlacement.Value == WristPlacement.Fixed && !dragging);
			if (dragging)
			{
				wasTouching = false;
				wasResetTouching = false;
				UpdateSlots(playerController, -1);
				return;
			}
			bool flag3 = ((Component)reset).gameObject.activeSelf && FingerTouches(val, ((Graphic)reset).rectTransform.anchoredPosition, 16f);
			((Graphic)reset).color = (flag3 ? new Color(1f, 0.5f, 0.4f, 1f) : new Color(0.85f, 0.3f, 0.25f, 0.85f));
			if (flag3 && !wasResetTouching)
			{
				Plugin.WristPlacement.Value = WristPlacement.FollowEyes;
				Plugin.Log.LogInfo((object)"Wrist inventory placement reset to follow your eyes");
			}
			wasResetTouching = flag3;
			bool touching;
			int num2 = FingerOverSlot(val, out touching);
			UpdateSlots(playerController, num2);
			if (touching && !wasTouching && num2 >= 0 && visible > 0.6f && Time.time >= cooldownUntil && TrySwitch(playerController, num2))
			{
				cooldownUntil = Time.time + 0.4f;
			}
			wasTouching = touching;
		}

		private void PlaceFacingEyes(Vector3 wrist, Vector3 elbow, Vector3 eyes)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_001e: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_002d: 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)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = elbow - wrist;
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			Vector3 val2 = Vector3.Lerp(wrist, elbow, 0.45f);
			val = Vector3.ProjectOnPlane(eyes - val2, normalized);
			Vector3 normalized2 = ((Vector3)(ref val)).normalized;
			((Component)canvas).transform.SetPositionAndRotation(val2 + normalized2 * 0.045f, Quaternion.LookRotation(-normalized2, normalized));
		}

		private bool UpdateDrag(VRPlayer player)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			Transform localRightHand = player.Bones.LocalRightHand;
			float num = ((player.RightFingerCurler != null) ? ((FingerCurler)player.RightFingerCurler).middleFinger.curl : 0f);
			if (dragging)
			{
				if (num <= 0.75f)
				{
					dragging = false;
					gripWasClosed = false;
					SavePose(player.Bones.LocalLeftLowerArm);
				}
				else
				{
					((Component)canvas).transform.SetPositionAndRotation(localRightHand.TransformPoint(dragPosition), localRightHand.rotation * dragRotation);
				}
				((Component)ring).gameObject.SetActive(false);
				return true;
			}
			bool num2 = visible > 0.5f && Vector3.Distance(localRightHand.position, ((Component)handle).transform.position) < 0.09f;
			bool flag = num > 0.75f;
			if (num2 && flag && (holdTime > 0f || !gripWasClosed))
			{
				holdTime += Time.deltaTime;
			}
			else
			{
				holdTime = 0f;
			}
			float num3 = Mathf.Max(0f, Plugin.WristGrabHoldSeconds.Value);
			if (holdTime > 0f && holdTime >= num3)
			{
				holdTime = 0f;
				dragging = true;
				dragPosition = localRightHand.InverseTransformPoint(((Component)canvas).transform.position);
				dragRotation = Quaternion.Inverse(localRightHand.rotation) * ((Component)canvas).transform.rotation;
			}
			((Component)ring).gameObject.SetActive(holdTime > 0f);
			ring.fillAmount = ((num3 > 0f) ? (holdTime / num3) : 1f);
			gripWasClosed = flag;
			return num2;
		}

		private void SavePose(Transform forearm)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//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_002d: 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)
			//IL_0037: 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_0058: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: 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)
			Vector3 val = forearm.InverseTransformPoint(((Component)canvas).transform.position);
			Quaternion val2 = Quaternion.Inverse(forearm.rotation) * ((Component)canvas).transform.rotation;
			Plugin.WristSavedPosition.Value = string.Join(",", F(val.x), F(val.y), F(val.z));
			Plugin.WristSavedRotation.Value = string.Join(",", F(val2.x), F(val2.y), F(val2.z), F(val2.w));
			Plugin.WristPlacement.Value = WristPlacement.Fixed;
			Plugin.Log.LogInfo((object)"Wrist inventory placement saved");
			static string F(float value)
			{
				return value.ToString("R", CultureInfo.InvariantCulture);
			}
		}

		private static bool TryGetSavedPose(out Vector3 position, out Quaternion rotation)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			rotation = Quaternion.identity;
			float[] array = Parse(Plugin.WristSavedPosition.Value, 3);
			float[] array2 = Parse(Plugin.WristSavedRotation.Value, 4);
			if (array == null || array2 == null)
			{
				return false;
			}
			position = new Vector3(array[0], array[1], array[2]);
			Quaternion val = new Quaternion(array2[0], array2[1], array2[2], array2[3]);
			rotation = ((Quaternion)(ref val)).normalized;
			return true;
		}

		private static float[] Parse(string text, int count)
		{
			string[] array = (text ?? "").Split(',');
			if (array.Length != count)
			{
				return null;
			}
			float[] array2 = new float[count];
			for (int i = 0; i < count; i++)
			{
				if (!float.TryParse(array[i], NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i]))
				{
					return null;
				}
			}
			return array2;
		}

		private bool FingerTouches(VRPlayer player, Vector2 position, float size)
		{
			//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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)fingertip == (Object)null || player.RightFingerCurler == null || ((FingerCurler)player.RightFingerCurler).indexFinger.curl > 0.5f)
			{
				return false;
			}
			Vector3 val = ((Component)canvas).transform.InverseTransformPoint(fingertip.position);
			if (Mathf.Abs(val.x - position.x) < size * 0.7f && Mathf.Abs(val.y - position.y) < size * 0.7f && val.z > -8f)
			{
				return val.z < 30f;
			}
			return false;
		}

		private void UpdateSlots(PlayerControllerB controller, int hovered)
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < frames.Length; i++)
			{
				Image val = ((i < HUDManager.Instance.itemSlotIcons.Length) ? HUDManager.Instance.itemSlotIcons[i] : null);
				icons[i].sprite = (((Object)(object)val != (Object)null) ? val.sprite : null);
				((Behaviour)icons[i]).enabled = (Object)(object)val != (Object)null && ((Behaviour)val).enabled && (Object)(object)val.sprite != (Object)null;
				((Graphic)frames[i]).color = ((i == hovered) ? new Color(1f, 0.85f, 0.3f, 1f) : ((i == controller.currentItemSlot) ? new Color(1f, 1f, 1f, 0.95f) : new Color(0.55f, 0.55f, 0.55f, 0.6f)));
			}
		}

		private int FingerOverSlot(VRPlayer player, out bool touching)
		{
			//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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			touching = false;
			if ((Object)(object)fingertip == (Object)null || player.RightFingerCurler == null)
			{
				return -1;
			}
			if (((FingerCurler)player.RightFingerCurler).indexFinger.curl > 0.5f)
			{
				return -1;
			}
			Vector3 val = ((Component)canvas).transform.InverseTransformPoint(fingertip.position);
			if (Mathf.Abs(val.x) > 20.400002f || val.z < -60f || val.z > 30f)
			{
				return -1;
			}
			float num = (float)(-(frames.Length - 1)) * 40f / 2f;
			int num2 = Mathf.RoundToInt((val.y - num) / 40f);
			if (num2 < 0 || num2 >= frames.Length || Mathf.Abs(val.y - (num + (float)num2 * 40f)) > 20.400002f)
			{
				return -1;
			}
			touching = val.z > -8f;
			return num2;
		}

		private static bool TrySwitch(PlayerControllerB player, int slot)
		{
			if (slot == player.currentItemSlot || slot >= player.ItemSlots.Length)
			{
				return false;
			}
			if ((float)timeSinceSwitchingSlots.GetValue(player) < 0.3f || player.isGrabbingObjectAnimation || player.quickMenuManager.isMenuOpen || player.inSpecialInteractAnimation || (bool)throwingObject.GetValue(player) || player.isTypingChat || player.twoHanded || player.activatingItem || player.inTerminalMenu)
			{
				return false;
			}
			if ((player.jetpackControls || player.disablingJetpackControls) && (Object)(object)player.currentlyHeldObjectServer != (Object)null && player.currentlyHeldObjectServer.itemProperties.itemId == 13)
			{
				return false;
			}
			if ((Object)(object)ShipBuildModeManager.Instance != (Object)null)
			{
				ShipBuildModeManager.Instance.CancelBuildMode(true);
			}
			player.playerBodyAnimator.SetBool("GrabValidated", false);
			switchToItemSlot.Invoke(player, new object[2] { slot, null });
			switchToSlotServerRpc.Invoke(player, new object[1] { slot });
			GrabbableObject currentlyHeldObjectServer = player.currentlyHeldObjectServer;
			if ((Object)(object)currentlyHeldObjectServer != (Object)null && (Object)(object)currentlyHeldObjectServer.itemProperties != (Object)null)
			{
				AudioSource component = ((Component)currentlyHeldObjectServer).GetComponent<AudioSource>();
				if (component != null)
				{
					component.PlayOneShot(currentlyHeldObjectServer.itemProperties.grabSFX, 0.6f);
				}
			}
			timeSinceSwitchingSlots.SetValue(player, 0f);
			return true;
		}

		private void Build(VRPlayer player)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: 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_01ba: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)canvas != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)canvas).gameObject);
			}
			owner = player;
			int num = Mathf.Min(new int[3]
			{
				Mathf.Max(1, Plugin.WristInventorySlots.Value),
				player.PlayerController.ItemSlots.Length,
				HUDManager.Instance.itemSlotIcons.Length
			});
			GameObject val = new GameObject("LCVRFixes Wrist Inventory");
			val.layer = LayerMask.NameToLayer("UI");
			canvas = val.AddComponent<Canvas>();
			canvas.renderMode = (RenderMode)2;
			canvas.worldCamera = VRSession.Instance.MainCamera;
			val.transform.localScale = Vector3.one * 0.001f;
			val.GetComponent<RectTransform>().sizeDelta = new Vector2(44f, (float)num * 40f + 10f);
			group = val.AddComponent<CanvasGroup>();
			group.interactable = false;
			group.blocksRaycasts = false;
			frames = (Image[])(object)new Image[num];
			icons = (Image[])(object)new Image[num];
			float num2 = (float)(-(num - 1)) * 40f / 2f;
			for (int i = 0; i < num; i++)
			{
				Sprite sprite = ((i < HUDManager.Instance.itemSlotIconFrames.Length) ? HUDManager.Instance.itemSlotIconFrames[i].sprite : null);
				frames[i] = CreateImage(val.transform, "Slot " + i, new Vector2(0f, num2 + (float)i * 40f), 34f, sprite);
				icons[i] = CreateImage(((Component)frames[i]).transform, "Icon", Vector2.zero, 27.2f, null);
				icons[i].preserveAspect = true;
			}
			handle = CreateImage(val.transform, "Handle", new Vector2(0f, 0f - num2 + 28f), 34f, null);
			((Graphic)handle).rectTransform.sizeDelta = new Vector2(34f, 10f);
			reset = CreateImage(val.transform, "Reset", new Vector2(0f, num2 - 28f), 16f, null);
			val.GetComponent<RectTransform>().sizeDelta = new Vector2(44f, (float)(num + 2) * 40f);
			ring = CreateImage(((Component)handle).transform, "Hold Ring", Vector2.zero, 44f, RingSprite());
			ring.type = (Type)3;
			ring.fillMethod = (FillMethod)4;
			ring.fillOrigin = 2;
			ring.fillClockwise = true;
			ring.fillAmount = 0f;
			((Graphic)ring).color = new Color(0.4f, 1f, 0.5f, 1f);
			((Component)ring).gameObject.SetActive(false);
			dragging = false;
			holdTime = 0f;
			fingertip = FindFingertip(player.Bones.LocalRightHand);
			if ((Object)(object)fingertip == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Couldn't find the right index fingertip; wrist inventory will display but not respond to pokes");
			}
		}

		private static Sprite RingSprite()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ringSprite != (Object)null)
			{
				return ringSprite;
			}
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1
			};
			float num = 63.5f;
			for (int i = 0; i < 128; i++)
			{
				for (int j = 0; j < 128; j++)
				{
					float num2 = Vector2.Distance(new Vector2((float)j, (float)i), new Vector2(num, num));
					float num3 = Mathf.Clamp01(62f - num2) * Mathf.Clamp01(num2 - 50f);
					val.SetPixel(j, i, new Color(1f, 1f, 1f, num3));
				}
			}
			val.Apply();
			ringSprite = Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f));
			return ringSprite;
		}

		private static Image CreateImage(Transform parent, string name, Vector2 position, float size, Sprite sprite)
		{
			//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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name)
			{
				layer = ((Component)parent).gameObject.layer
			};
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchoredPosition = position;
			obj.sizeDelta = new Vector2(size, size);
			Image obj2 = val.AddComponent<Image>();
			obj2.sprite = sprite;
			((Graphic)obj2).raycastTarget = false;
			return obj2;
		}

		private static Transform FindFingertip(Transform hand)
		{
			Transform val = (((Object)(object)hand != (Object)null) ? hand.Find("finger2.R") : null);
			while ((Object)(object)val != (Object)null && val.childCount > 0)
			{
				val = val.GetChild(0);
			}
			return val;
		}

		private void SetShown(bool shown)
		{
			if ((Object)(object)canvas != (Object)null && ((Component)canvas).gameObject.activeSelf != shown)
			{
				((Component)canvas).gameObject.SetActive(shown);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)canvas != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)canvas).gameObject);
			}
		}
	}
}

BepInEx/patchers/BonemansLCVRFixes/BonemansLCVRFixes.Patcher.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BonemansLCVRFixes.Patcher")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f00e3e0edbf530a36e7eb201259842f2ebe46042")]
[assembly: AssemblyProduct("BonemansLCVRFixes.Patcher")]
[assembly: AssemblyTitle("BonemansLCVRFixes.Patcher")]
[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 IndexFingerTracking
{
	public class HandTrackingFeature : OpenXRFeature
	{
		private struct XrHandTrackerCreateInfoEXT
		{
			public int type;

			public IntPtr next;

			public int hand;

			public int handJointSet;
		}

		private struct XrHandJointsLocateInfoEXT
		{
			public int type;

			public IntPtr next;

			public ulong baseSpace;

			public long time;
		}

		private struct XrHandJointLocationsEXT
		{
			public int type;

			public IntPtr next;

			public uint isActive;

			public uint jointCount;

			public IntPtr jointLocations;
		}

		[UnmanagedFunctionPointer(CallingConvention.StdCall)]
		private delegate int GetInstanceProcAddrFn(ulong instance, [MarshalAs(UnmanagedType.LPStr)] string name, out IntPtr function);

		[UnmanagedFunctionPointer(CallingConvention.StdCall)]
		private delegate int CreateHandTrackerFn(ulong session, ref XrHandTrackerCreateInfoEXT info, out ulong tracker);

		[UnmanagedFunctionPointer(CallingConvention.StdCall)]
		private delegate int DestroyHandTrackerFn(ulong tracker);

		[UnmanagedFunctionPointer(CallingConvention.StdCall)]
		private delegate int LocateHandJointsFn(ulong tracker, ref XrHandJointsLocateInfoEXT info, ref XrHandJointLocationsEXT locations);

		[UnmanagedFunctionPointer(CallingConvention.StdCall)]
		private delegate int ConvertPerformanceCounterFn(ulong instance, ref long counter, out long time);

		private const string HandTrackingExtension = "XR_EXT_hand_tracking";

		private const string TimeExtension = "XR_KHR_win32_convert_performance_counter_time";

		private const int XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT = 1000051001;

		private const int XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT = 1000051002;

		private const int XR_TYPE_HAND_JOINT_LOCATIONS_EXT = 1000051003;

		private const int JointCount = 26;

		private const int JointStride = 40;

		private const ulong OrientationValid = 1uL;

		private CreateHandTrackerFn createHandTracker;

		private DestroyHandTrackerFn destroyHandTracker;

		private LocateHandJointsFn locateHandJoints;

		private ConvertPerformanceCounterFn convertTime;

		private ulong xrInstance;

		private readonly ulong[] trackers = new ulong[2];

		private readonly IntPtr[] jointBuffers = new IntPtr[2];

		private readonly byte[] jointBytes = new byte[1040];

		private readonly float[][] curls = new float[2][]
		{
			new float[5],
			new float[5]
		};

		private readonly int[] lastFrame = new int[2] { -1, -1 };

		private readonly bool[] lastResult = new bool[2];

		private bool loggedLocateError;

		[DllImport("kernel32.dll")]
		private static extern bool QueryPerformanceCounter(out long count);

		internal static HandTrackingFeature Create()
		{
			HandTrackingFeature handTrackingFeature = ScriptableObject.CreateInstance<HandTrackingFeature>();
			((Object)handTrackingFeature).hideFlags = (HideFlags)61;
			Traverse.Create((object)handTrackingFeature).Field("nameUi").SetValue((object)"Index Finger Tracking");
			Traverse.Create((object)handTrackingFeature).Field("version").SetValue((object)"1.0.0");
			Traverse.Create((object)handTrackingFeature).Field("company").SetValue((object)"kylew");
			Traverse.Create((object)handTrackingFeature).Field("featureIdInternal").SetValue((object)"kylew.indexfingertracking");
			Traverse.Create((object)handTrackingFeature).Field("openxrExtensionStrings").SetValue((object)"XR_EXT_hand_tracking XR_KHR_win32_convert_performance_counter_time");
			((OpenXRFeature)handTrackingFeature).enabled = true;
			return handTrackingFeature;
		}

		protected override bool OnInstanceCreate(ulong instance)
		{
			xrInstance = instance;
			if (!OpenXRRuntime.IsExtensionEnabled("XR_EXT_hand_tracking"))
			{
				Patcher.Log.LogWarning((object)"The OpenXR runtime doesn't offer hand tracking (XR_EXT_hand_tracking); fingers stay button-driven");
				return true;
			}
			if (!OpenXRRuntime.IsExtensionEnabled("XR_KHR_win32_convert_performance_counter_time"))
			{
				Patcher.Log.LogWarning((object)"The OpenXR runtime doesn't offer XR_KHR_win32_convert_performance_counter_time; fingers stay button-driven");
				return true;
			}
			try
			{
				GetInstanceProcAddrFn delegateForFunctionPointer = Marshal.GetDelegateForFunctionPointer<GetInstanceProcAddrFn>(OpenXRFeature.xrGetInstanceProcAddr);
				createHandTracker = Load<CreateHandTrackerFn>(delegateForFunctionPointer, "xrCreateHandTrackerEXT");
				destroyHandTracker = Load<DestroyHandTrackerFn>(delegateForFunctionPointer, "xrDestroyHandTrackerEXT");
				locateHandJoints = Load<LocateHandJointsFn>(delegateForFunctionPointer, "xrLocateHandJointsEXT");
				convertTime = Load<ConvertPerformanceCounterFn>(delegateForFunctionPointer, "xrConvertWin32PerformanceCounterToTimeKHR");
				Patcher.Log.LogInfo((object)"Hand tracking extension enabled");
			}
			catch (Exception ex)
			{
				Patcher.Log.LogError((object)("Failed to load hand tracking functions: " + ex.Message));
				createHandTracker = null;
			}
			return true;
		}

		private T Load<T>(GetInstanceProcAddrFn getProcAddr, string name) where T : Delegate
		{
			IntPtr function;
			int num = getProcAddr(xrInstance, name, out function);
			if (num < 0 || function == IntPtr.Zero)
			{
				throw new Exception($"{name} unavailable (XrResult {num})");
			}
			return Marshal.GetDelegateForFunctionPointer<T>(function);
		}

		protected override void OnSessionCreate(ulong session)
		{
			if (createHandTracker == null)
			{
				return;
			}
			for (int i = 0; i < 2; i++)
			{
				XrHandTrackerCreateInfoEXT info = new XrHandTrackerCreateInfoEXT
				{
					type = 1000051001,
					hand = i + 1,
					handJointSet = 0
				};
				int num = createHandTracker(session, ref info, out trackers[i]);
				if (num < 0)
				{
					Patcher.Log.LogError((object)string.Format("xrCreateHandTrackerEXT failed for {0} hand (XrResult {1})", (i == 0) ? "left" : "right", num));
					trackers[i] = 0uL;
				}
				else if (jointBuffers[i] == IntPtr.Zero)
				{
					jointBuffers[i] = Marshal.AllocHGlobal(1040);
				}
			}
		}

		protected override void OnSessionDestroy(ulong session)
		{
			for (int i = 0; i < 2; i++)
			{
				if (trackers[i] != 0L)
				{
					destroyHandTracker?.Invoke(trackers[i]);
				}
				trackers[i] = 0uL;
			}
		}

		protected override void OnInstanceDestroy(ulong instance)
		{
			createHandTracker = null;
			locateHandJoints = null;
		}

		internal bool TryGetCurls(int hand, out float[] result)
		{
			result = curls[hand];
			if (lastFrame[hand] == Time.frameCount)
			{
				return lastResult[hand];
			}
			lastFrame[hand] = Time.frameCount;
			lastResult[hand] = Locate(hand);
			return lastResult[hand];
		}

		private bool Locate(int hand)
		{
			if (locateHandJoints == null || trackers[hand] == 0L)
			{
				return false;
			}
			QueryPerformanceCounter(out var count);
			if (convertTime(xrInstance, ref count, out var time) < 0)
			{
				return false;
			}
			XrHandJointsLocateInfoEXT info = new XrHandJointsLocateInfoEXT
			{
				type = 1000051002,
				baseSpace = OpenXRFeature.GetCurrentAppSpace(),
				time = time
			};
			XrHandJointLocationsEXT locations = new XrHandJointLocationsEXT
			{
				type = 1000051003,
				jointCount = 26u,
				jointLocations = jointBuffers[hand]
			};
			int num = locateHandJoints(trackers[hand], ref info, ref locations);
			if (num < 0)
			{
				if (!loggedLocateError)
				{
					loggedLocateError = true;
					Patcher.Log.LogWarning((object)$"xrLocateHandJointsEXT failed (XrResult {num})");
				}
				return false;
			}
			if (locations.isActive == 0)
			{
				return false;
			}
			Marshal.Copy(jointBuffers[hand], jointBytes, 0, jointBytes.Length);
			if (!Bend(2, 2, out var degrees))
			{
				return false;
			}
			float[] array = curls[hand];
			array[0] = Mathf.Clamp01(degrees / Mathf.Max(1f, Patcher.ThumbCurlAngle.Value));
			int[] array2 = new int[4] { 6, 11, 16, 21 };
			for (int i = 0; i < array2.Length; i++)
			{
				if (!Bend(array2[i], 3, out var degrees2))
				{
					return false;
				}
				array[i + 1] = Mathf.Clamp01(degrees2 / Mathf.Max(1f, Patcher.FingerCurlAngle.Value));
			}
			return true;
		}

		private bool Bend(int firstJoint, int segments, out float degrees)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			degrees = 0f;
			for (int i = firstJoint; i < firstJoint + segments; i++)
			{
				if (!TryOrientation(i, out var rotation) || !TryOrientation(i + 1, out var rotation2))
				{
					return false;
				}
				degrees += Quaternion.Angle(rotation, rotation2);
			}
			return true;
		}

		private bool TryOrientation(int joint, out Quaternion rotation)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			int num = joint * 40;
			ulong num2 = BitConverter.ToUInt64(jointBytes, num);
			rotation = new Quaternion(BitConverter.ToSingle(jointBytes, num + 8), BitConverter.ToSingle(jointBytes, num + 12), BitConverter.ToSingle(jointBytes, num + 16), BitConverter.ToSingle(jointBytes, num + 20));
			return (num2 & 1) != 0;
		}
	}
	internal static class LcvrHooks
	{
		private static FieldInfo loaderFeatures;

		private static FieldInfo settingsFeatures;

		private static FieldInfo handField;

		private static FieldInfo forceFistField;

		private static FieldInfo[] fingerFields;

		private static FieldInfo curlField;

		private static FieldInfo thumbCurlField;

		private static HandTrackingFeature feature;

		private static bool loggedActive;

		public static void Apply(Assembly lcvr)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Expected O, but got Unknown
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Expected O, but got Unknown
			Type type = lcvr.GetType("LCVR.OpenXR+Loader", throwOnError: true);
			Harmony val = new Harmony("kylew.indexfingertracking");
			val.Patch((MethodBase)AccessTools.Method(type, "InitializeScripts", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(LcvrHooks), "SetRenderMode", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			if (!Patcher.Enabled.Value)
			{
				Patcher.Log.LogInfo((object)"Hooked LCVR (render mode only)");
				return;
			}
			Type type2 = lcvr.GetType("LCVR.Input.VRFingerCurler", throwOnError: true);
			Type baseCurler = type2.BaseType;
			loaderFeatures = AccessTools.Field(type, "s_Features") ?? throw new MissingFieldException("LCVR.OpenXR.Loader.s_Features");
			settingsFeatures = AccessTools.Field(typeof(OpenXRSettings), "features") ?? throw new MissingFieldException("OpenXRSettings.features");
			handField = AccessTools.Field(type2, "hand") ?? throw new MissingFieldException("VRFingerCurler.hand");
			forceFistField = AccessTools.Field(baseCurler, "forceFist") ?? throw new MissingFieldException("FingerCurler.forceFist");
			fingerFields = new string[5] { "thumbFinger", "indexFinger", "middleFinger", "ringFinger", "pinkyFinger" }.Select((string name) => AccessTools.Field(baseCurler, name) ?? throw new MissingFieldException("FingerCurler." + name)).ToArray();
			thumbCurlField = AccessTools.Field(fingerFields[0].FieldType, "curl");
			curlField = AccessTools.Field(fingerFields[1].FieldType, "curl");
			val.Patch((MethodBase)AccessTools.Method(type, "InitializeScripts", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(LcvrHooks), "AddFeature", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(type2, "UpdateCurls", (Type[])null, (Type[])null), new HarmonyMethod(typeof(LcvrHooks), "UpdateCurlsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Patcher.Log.LogInfo((object)"Hooked LCVR");
		}

		private static void SetRenderMode()
		{
			if (Patcher.EyesSeparately)
			{
				OpenXRSettings.Instance.renderMode = (RenderMode)0;
				Patcher.Log.LogInfo((object)"OpenXR set to one image per eye (per-eye rendering is on)");
			}
		}

		private static void AddFeature()
		{
			try
			{
				OpenXRFeature[] source = (OpenXRFeature[])loaderFeatures.GetValue(null);
				if (!source.Any((OpenXRFeature f) => f is HandTrackingFeature))
				{
					if ((Object)(object)feature == (Object)null)
					{
						feature = HandTrackingFeature.Create();
					}
					OpenXRFeature[] value = source.Append((OpenXRFeature)(object)feature).ToArray();
					loaderFeatures.SetValue(null, value);
					settingsFeatures.SetValue(OpenXRSettings.Instance, value);
					Patcher.Log.LogInfo((object)"Registered hand tracking OpenXR feature");
				}
			}
			catch (Exception arg)
			{
				Patcher.Log.LogError((object)$"Failed to register hand tracking feature: {arg}");
			}
		}

		private static bool UpdateCurlsPrefix(object __instance)
		{
			try
			{
				if ((Object)(object)feature == (Object)null || (bool)forceFistField.GetValue(__instance))
				{
					return true;
				}
				int hand = ((!((string)handField.GetValue(__instance) == "L")) ? 1 : 0);
				if (!feature.TryGetCurls(hand, out var result))
				{
					return true;
				}
				if (!loggedActive)
				{
					loggedActive = true;
					Patcher.Log.LogInfo((object)"Receiving finger tracking data, fingers now follow your hands");
				}
				float num = Mathf.Clamp(Patcher.Smoothing.Value, 0f, 0.95f);
				for (int i = 0; i < fingerFields.Length; i++)
				{
					object value = fingerFields[i].GetValue(__instance);
					FieldInfo obj = ((i == 0) ? thumbCurlField : curlField);
					float num2 = (float)obj.GetValue(value);
					obj.SetValue(value, Mathf.Lerp(result[i], num2, num));
				}
				return false;
			}
			catch (Exception ex)
			{
				Patcher.Log.LogError((object)("Finger tracking update failed, falling back to buttons: " + ex.Message));
				feature = null;
				return true;
			}
		}
	}
	public static class Patcher
	{
		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> FingerCurlAngle;

		internal static ConfigEntry<float> ThumbCurlAngle;

		internal static ConfigEntry<float> Smoothing;

		internal static bool EyesSeparately;

		public static IEnumerable<string> TargetDLLs { get; } = Array.Empty<string>();

		public static void Initialize()
		{
			//IL_0067: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			Log = Logger.CreateLogSource("Boneman's LCVR Fixes (patcher)");
			string text = Path.Combine(Paths.ConfigPath, "boneman.lcvrfixes.fingertracking.cfg");
			string text2 = Path.Combine(Paths.ConfigPath, "kylew.indexfingertracking.cfg");
			if (!File.Exists(text) && File.Exists(text2))
			{
				try
				{
					File.Copy(text2, text);
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Couldn't copy old finger tracking settings: " + ex.Message));
				}
			}
			ConfigFile val = new ConfigFile(text, true);
			Enabled = val.Bind<bool>("General", "Enabled", true, "Drive LCVR's fingers from real finger tracking (Valve Index controllers via SteamVR) instead of buttons.");
			FingerCurlAngle = val.Bind<float>("Tuning", "FingerCurlAngle", 220f, "Total joint bend (degrees) that counts as a fully curled finger. Lower = fingers curl sooner.");
			ThumbCurlAngle = val.Bind<float>("Tuning", "ThumbCurlAngle", 110f, "Total joint bend (degrees) that counts as a fully curled thumb.");
			Smoothing = val.Bind<float>("Tuning", "Smoothing", 0.5f, "0 = raw tracking, closer to 1 = smoother but laggier fingers.");
			EyesSeparately = ReadPluginBool("Rendering", "RenderEyesSeparately", fallback: true);
			if (!Enabled.Value)
			{
				Log.LogInfo((object)"Finger tracking disabled by config");
			}
			if (Enabled.Value || EyesSeparately)
			{
				AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
			}
		}

		private static bool ReadPluginBool(string section, string key, bool fallback)
		{
			try
			{
				string path = Path.Combine(Paths.ConfigPath, "boneman.lcvrfixes.cfg");
				if (!File.Exists(path))
				{
					return fallback;
				}
				string text = "";
				string[] array = File.ReadAllLines(path);
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = array[i].Trim();
					bool result;
					if (text2.StartsWith("[") && text2.EndsWith("]"))
					{
						text = text2.Substring(1, text2.Length - 2).Trim();
					}
					else if (text == section && text2.StartsWith(key) && text2.Contains("="))
					{
						return bool.TryParse(text2.Substring(text2.IndexOf('=') + 1).Trim(), out result) ? result : fallback;
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Couldn't read " + key + " from boneman.lcvrfixes.cfg: " + ex.Message));
			}
			return fallback;
		}

		public static void Patch(AssemblyDefinition assembly)
		{
		}

		private static void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
		{
			try
			{
				if (!(args.LoadedAssembly.GetName().Name != "LCVR"))
				{
					AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
					typeof(Patcher).Assembly.GetType("IndexFingerTracking.LcvrHooks", throwOnError: true).GetMethod("Apply", BindingFlags.Static | BindingFlags.Public).Invoke(null, new object[1] { args.LoadedAssembly });
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to hook LCVR, finger tracking disabled: {arg}");
			}
		}
	}
}