Decompiled source of Invisible Touch v1.0.0

Mods/InvisibleTouch.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib;
using BoneLib.BoneMenu;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Interaction;
using Il2CppSLZ.VRMK;
using Il2CppSystem.Collections.Generic;
using InvisibleTouch;
using LabFusion.Entities;
using LabFusion.Marrow.Extenders;
using LabFusion.Network;
using LabFusion.Player;
using LabFusion.SDK.Metadata;
using LabFusion.SDK.Modules;
using LabFusion.Utilities;
using MelonLoader;
using MelonLoader.Preferences;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("InvisibleTouch")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: MelonInfo(typeof(Main), "Invisible Touch", "1.0.0", "MyFirstTeam", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace InvisibleTouch;

public enum ControllerBinding
{
	LeftStickClick,
	RightStickClick,
	Disabled
}
public enum HapticPattern
{
	OneOnTwoOff,
	TwoOnOneOff,
	SinglePulse,
	DoublePulse
}
public enum HapticTarget
{
	Auto,
	Left,
	Right,
	Both
}
public static class InvisibilityManager
{
	private sealed class RemoteState
	{
		public bool Invisible;

		public ItemHidingMode ItemHiding = ItemHidingMode.HeldAndHolstered;
	}

	private sealed class RemoteVisualHide
	{
		public int RigId;

		public readonly List<Renderer> DisabledRenderers = new List<Renderer>();
	}

	private sealed class RemoteItemHide
	{
		public byte SmallId;

		public readonly List<Renderer> DisabledRenderers = new List<Renderer>();
	}

	private sealed class HandMeshSwap
	{
		public SkinnedMeshRenderer Renderer;

		public Mesh OriginalMesh;

		public Mesh HandMesh;

		public int RendererId;
	}

	public const string MetadataKey = "InvisibleTouch_IsInvisible";

	internal const string ItemHidingMetadataKey = "InvisibleTouch_ItemHiding";

	private const float ReconcileIntervalSeconds = 0.5f;

	private const float HapticPulseGapSeconds = 0.09f;

	private static int _cooldownSeconds = 5;

	private static float _hapticStrength = 1f;

	private static readonly Dictionary<byte, RemoteState> _remoteStates = new Dictionary<byte, RemoteState>();

	private static readonly Dictionary<byte, RemoteVisualHide> _remoteVisualHides = new Dictionary<byte, RemoteVisualHide>();

	private static readonly Dictionary<ushort, RemoteItemHide> _remoteItemHides = new Dictionary<ushort, RemoteItemHide>();

	private static readonly List<Renderer> _hiddenSelfRenderers = new List<Renderer>();

	private static readonly List<HandMeshSwap> _handMeshSwaps = new List<HandMeshSwap>();

	private static readonly Dictionary<string, Mesh> _handMeshCache = new Dictionary<string, Mesh>();

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

	private static bool _fusionHooksInitialized = false;

	private static bool _previousBindingPressed = false;

	private static float _nextReconcileTime = 0f;

	private static float _lastToggleTime = float.NegativeInfinity;

	private static float _nextToggleAllowedTime = 0f;

	private static bool _reconcileWarningLogged = false;

	private static bool _itemWarningLogged = false;

	private static bool _visualHideWarningLogged = false;

	private static bool _hapticsWarningLogged = false;

	private static bool _handMeshWarningLogged = false;

	private static string _lastHandsOnlyLogSignature = null;

	public static bool IsInvisible { get; private set; }

	public static SelfViewMode SelfView { get; set; } = SelfViewMode.FullBody;

	public static bool ShowSelf
	{
		get
		{
			return SelfView != SelfViewMode.Hidden;
		}
		set
		{
			SelfView = ((!value) ? SelfViewMode.Hidden : SelfViewMode.FullBody);
		}
	}

	public static ControllerBinding Binding { get; set; } = ControllerBinding.LeftStickClick;

	public static ItemHidingMode ItemHiding { get; set; } = ItemHidingMode.HeldAndHolstered;

	public static int CooldownSeconds
	{
		get
		{
			return _cooldownSeconds;
		}
		set
		{
			_cooldownSeconds = Mathf.Clamp(value, 0, 60);
		}
	}

	public static bool HapticsEnabled { get; set; } = true;

	public static HapticPattern HapticPattern { get; set; } = HapticPattern.OneOnTwoOff;

	public static HapticTarget HapticTarget { get; set; } = HapticTarget.Auto;

	public static float HapticStrength
	{
		get
		{
			return _hapticStrength;
		}
		set
		{
			_hapticStrength = ((float.IsNaN(value) || float.IsInfinity(value)) ? 1f : Mathf.Clamp(value, 0.25f, 2f));
		}
	}

	internal static Instance Log { get; set; }

	public static event Action<bool> OnLocalInvisibleChanged;

	public static void Toggle()
	{
		SetInvisible(!IsInvisible, "toggle");
	}

	private static void ToggleFromController()
	{
		SetInvisible(!IsInvisible, "controller", fromController: true);
	}

	public static void SetInvisible(bool value, string source = null, bool fromController = false)
	{
		if (IsInvisible == value)
		{
			return;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (realtimeSinceStartup < _nextToggleAllowedTime)
		{
			InvisibilityManager.OnLocalInvisibleChanged?.Invoke(IsInvisible);
			Instance log = Log;
			if (log != null)
			{
				log.Msg($"Invisibility toggle blocked by cooldown ({_nextToggleAllowedTime - realtimeSinceStartup:0.0}s left).");
			}
			return;
		}
		_lastToggleTime = realtimeSinceStartup;
		_nextToggleAllowedTime = realtimeSinceStartup + (float)CooldownSeconds;
		IsInvisible = value;
		PublishLocalState();
		ApplySelfVisibility();
		PlayToggleHaptics(value, fromController);
		InvisibilityManager.OnLocalInvisibleChanged?.Invoke(value);
		Instance log2 = Log;
		if (log2 != null)
		{
			log2.Msg("Invisibility " + (value ? "ON" : "OFF") + ((source != null) ? (" (" + source + ")") : string.Empty) + (NetworkInfo.HasServer ? string.Empty : " - not in a server, state will apply on join"));
		}
	}

	public static void OnSelfViewChanged()
	{
		ApplySelfVisibility();
	}

	public static void OnShowSelfChanged()
	{
		OnSelfViewChanged();
	}

	public static void OnCooldownChanged()
	{
		if (float.IsNegativeInfinity(_lastToggleTime))
		{
			_nextToggleAllowedTime = 0f;
		}
		else
		{
			_nextToggleAllowedTime = _lastToggleTime + (float)CooldownSeconds;
		}
	}

	public static void OnItemHidingChanged()
	{
		PublishLocalState();
	}

	private static void PublishLocalState()
	{
		TryPublishMetadata("InvisibleTouch_IsInvisible", IsInvisible.ToString());
		TryPublishMetadata("InvisibleTouch_ItemHiding", ItemHiding.ToString());
	}

	private static void TryPublishMetadata(string key, string value)
	{
		try
		{
			PlayerMetadata metadata = LocalPlayer.Metadata;
			if (metadata != null)
			{
				NetworkMetadata metadata2 = metadata.Metadata;
				if (metadata2 != null)
				{
					metadata2.TrySetMetadata(key, value);
				}
			}
		}
		catch (Exception ex)
		{
			Instance log = Log;
			if (log != null)
			{
				log.Warning("Failed to publish invisibility metadata '" + key + "': " + ex.Message);
			}
		}
	}

	internal static void InitializeFusionHooks()
	{
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Expected O, but got Unknown
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Expected O, but got Unknown
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Expected O, but got Unknown
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Expected O, but got Unknown
		if (!_fusionHooksInitialized)
		{
			_fusionHooksInitialized = true;
			PlayerID.OnMetadataChangedEvent += OnRemoteMetadataChanged;
			PlayerID.OnMetadataRemovedEvent += OnRemoteMetadataRemoved;
			NetworkPlayer.OnNetworkPlayerRegistered += OnNetworkPlayerRegistered;
			NetworkPlayer.OnNetworkRigCreated += OnNetworkRigCreated;
			LocalPlayer.OnApplyInitialMetadata += PublishLocalState;
			MultiplayerHooking.OnPlayerLeft += new PlayerUpdate(OnPlayerLeft);
			MultiplayerHooking.OnJoinedServer += new ServerEvent(OnJoinedServer);
			MultiplayerHooking.OnDisconnected += new ServerEvent(OnDisconnected);
			MultiplayerHooking.OnMainSceneInitialized += new UpdateEvent(OnMainSceneInitialized);
			PublishLocalState();
		}
	}

	private static void OnRemoteMetadataChanged(PlayerID playerId, string key, string value)
	{
		if (playerId != null && !playerId.IsMe && IsOurMetadataKey(key))
		{
			RemoteState orCreateRemoteState = GetOrCreateRemoteState(playerId.SmallID);
			ItemHidingMode result2;
			if (key == "InvisibleTouch_IsInvisible")
			{
				orCreateRemoteState.Invisible = bool.TryParse(value, out var result) && result;
			}
			else if (key == "InvisibleTouch_ItemHiding" && Enum.TryParse<ItemHidingMode>(value, out result2) && Enum.IsDefined(typeof(ItemHidingMode), result2))
			{
				orCreateRemoteState.ItemHiding = result2;
			}
			NetworkPlayer player = default(NetworkPlayer);
			if (NetworkPlayerManager.TryGetPlayer(playerId.SmallID, ref player))
			{
				ApplyRemote(player, orCreateRemoteState);
			}
		}
	}

	private static void OnRemoteMetadataRemoved(PlayerID playerId, string key, string value)
	{
		if (playerId != null && !playerId.IsMe && IsOurMetadataKey(key))
		{
			RemoteState orCreateRemoteState = GetOrCreateRemoteState(playerId.SmallID);
			if (key == "InvisibleTouch_IsInvisible")
			{
				orCreateRemoteState.Invisible = false;
			}
			else if (key == "InvisibleTouch_ItemHiding")
			{
				orCreateRemoteState.ItemHiding = ItemHidingMode.HeldAndHolstered;
			}
			NetworkPlayer player = default(NetworkPlayer);
			if (NetworkPlayerManager.TryGetPlayer(playerId.SmallID, ref player))
			{
				ApplyRemote(player, orCreateRemoteState);
			}
		}
	}

	private static bool IsOurMetadataKey(string key)
	{
		if (!(key == "InvisibleTouch_IsInvisible"))
		{
			return key == "InvisibleTouch_ItemHiding";
		}
		return true;
	}

	private static RemoteState GetOrCreateRemoteState(byte smallId)
	{
		if (!_remoteStates.TryGetValue(smallId, out var value))
		{
			value = new RemoteState();
			_remoteStates[smallId] = value;
		}
		return value;
	}

	private static void OnNetworkPlayerRegistered(NetworkPlayer player)
	{
		if (((player != null) ? player.PlayerID : null) != null && player.NetworkEntity != null && !player.NetworkEntity.IsOwner && TryReadDesired(player, out var state))
		{
			_remoteStates[player.PlayerID.SmallID] = state;
			ApplyRemote(player, state);
		}
	}

	private static void OnNetworkRigCreated(NetworkPlayer player, RigManager rigManager)
	{
		if (((player != null) ? player.PlayerID : null) != null && player.NetworkEntity != null && !player.NetworkEntity.IsOwner)
		{
			RemoteState value;
			if (TryReadDesired(player, out var state))
			{
				_remoteStates[player.PlayerID.SmallID] = state;
				ApplyRemote(player, state);
			}
			else if (_remoteStates.TryGetValue(player.PlayerID.SmallID, out value))
			{
				ApplyRemote(player, value);
			}
		}
	}

	private static void OnJoinedServer()
	{
		PublishLocalState();
	}

	private static void OnMainSceneInitialized()
	{
		if (IsInvisible)
		{
			PublishLocalState();
			ApplySelfVisibility();
		}
	}

	private static void OnPlayerLeft(PlayerID playerId)
	{
		if (playerId != null)
		{
			_remoteStates.Remove(playerId.SmallID);
			_remoteVisualHides.Remove(playerId.SmallID);
			RestoreRemoteItemHidesForPlayer(playerId.SmallID);
		}
	}

	private static void OnDisconnected()
	{
		_remoteStates.Clear();
		_remoteVisualHides.Clear();
		RestoreAllRemoteItemHides();
	}

	private static bool TryReadDesired(NetworkPlayer player, out RemoteState state)
	{
		state = new RemoteState();
		PlayerID playerID = player.PlayerID;
		object obj;
		if (playerID == null)
		{
			obj = null;
		}
		else
		{
			PlayerMetadata metadata = playerID.Metadata;
			obj = ((metadata != null) ? metadata.Metadata : null);
		}
		NetworkMetadata val = (NetworkMetadata)obj;
		string text = ((val != null) ? val.GetMetadata("InvisibleTouch_IsInvisible") : null);
		if (text == null)
		{
			return false;
		}
		state.Invisible = bool.TryParse(text, out var result) && result;
		if (Enum.TryParse<ItemHidingMode>((val != null) ? val.GetMetadata("InvisibleTouch_ItemHiding") : null, out var result2) && Enum.IsDefined(typeof(ItemHidingMode), result2))
		{
			state.ItemHiding = result2;
		}
		return true;
	}

	private static void ApplyRemote(NetworkPlayer player, RemoteState state)
	{
		if (((player != null) ? player.PlayerID : null) != null && state != null)
		{
			if (!state.Invisible)
			{
				RestoreRemoteVisualHide(player);
			}
			else if (!player.ForceHide)
			{
				ApplyRemoteVisualHide(player);
			}
			else
			{
				RestoreRemoteVisualHide(player);
			}
		}
	}

	private static void ApplyRemoteVisualHide(NetworkPlayer player)
	{
		try
		{
			if (((player != null) ? player.PlayerID : null) == null || !player.HasRig)
			{
				return;
			}
			RigRefs rigRefs = player.RigRefs;
			RigManager val = ((rigRefs != null) ? rigRefs.RigManager : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val.physicsRig == (Object)null)
			{
				return;
			}
			byte smallID = player.PlayerID.SmallID;
			int instanceID = ((Object)val).GetInstanceID();
			if (!_remoteVisualHides.TryGetValue(smallID, out var value) || value.RigId != instanceID)
			{
				value = new RemoteVisualHide
				{
					RigId = instanceID
				};
				_remoteVisualHides[smallID] = value;
			}
			try
			{
				if (player.HeadUI != null)
				{
					player.HeadUI.Visible = false;
				}
			}
			catch
			{
			}
			HashSet<int> hashSet = new HashSet<int>();
			foreach (Renderer disabledRenderer in value.DisabledRenderers)
			{
				int num = SafeInstanceId((Object)(object)disabledRenderer);
				if (num != int.MinValue)
				{
					hashSet.Add(num);
				}
			}
			MarrowEntity marrowEntity = val.physicsRig.marrowEntity;
			List<Component> list = new List<Component> { (Component)(object)val.physicsRig };
			if ((Object)(object)val.avatar != (Object)null)
			{
				list.Add((Component)(object)val.avatar);
			}
			foreach (Component item in list)
			{
				bool flag = (Object)(object)val.avatar != (Object)null && SafeInstanceId((Object)(object)item) == SafeInstanceId((Object)(object)val.avatar);
				foreach (Renderer componentsInChild in item.GetComponentsInChildren<Renderer>(true))
				{
					if ((Object)(object)componentsInChild == (Object)null || !componentsInChild.enabled)
					{
						continue;
					}
					int instanceID2 = ((Object)componentsInChild).GetInstanceID();
					if (flag || IsRigOwnedComponent((Component)(object)componentsInChild, marrowEntity))
					{
						if (!hashSet.Contains(instanceID2))
						{
							value.DisabledRenderers.Add(componentsInChild);
							hashSet.Add(instanceID2);
						}
						componentsInChild.enabled = false;
					}
				}
			}
		}
		catch (Exception ex)
		{
			if (!_visualHideWarningLogged)
			{
				_visualHideWarningLogged = true;
				Instance log = Log;
				if (log != null)
				{
					log.Warning("Remote visual hide failed (logged once): " + ex.Message);
				}
			}
		}
	}

	private static void RestoreRemoteVisualHide(NetworkPlayer player)
	{
		if (((player != null) ? player.PlayerID : null) == null || !_remoteVisualHides.Remove(player.PlayerID.SmallID, out var value))
		{
			return;
		}
		foreach (Renderer disabledRenderer in value.DisabledRenderers)
		{
			try
			{
				if ((Object)(object)disabledRenderer != (Object)null)
				{
					disabledRenderer.enabled = true;
				}
			}
			catch
			{
			}
		}
		try
		{
			if (player.HeadUI != null && !player.IsCulled && !player.ForceHide)
			{
				player.HeadUI.Visible = true;
			}
		}
		catch
		{
		}
	}

	private static void RestoreAllRemoteVisualHides()
	{
		try
		{
			foreach (NetworkPlayer item in new List<NetworkPlayer>(NetworkPlayer.Players))
			{
				RestoreRemoteVisualHide(item);
			}
		}
		catch
		{
		}
		_remoteVisualHides.Clear();
	}

	private static bool IsRigOwnedComponent(Component component, MarrowEntity rigEntity)
	{
		if ((Object)(object)component == (Object)null || (Object)(object)rigEntity == (Object)null)
		{
			return true;
		}
		try
		{
			MarrowEntity componentInParent = component.GetComponentInParent<MarrowEntity>();
			return (Object)(object)componentInParent == (Object)null || SafeInstanceId((Object)(object)componentInParent) == SafeInstanceId((Object)(object)rigEntity);
		}
		catch
		{
			return true;
		}
	}

	private static void Reconcile()
	{
		try
		{
			foreach (NetworkPlayer item in new List<NetworkPlayer>(NetworkPlayer.Players))
			{
				if (((item != null) ? item.PlayerID : null) != null && item.NetworkEntity != null && !item.NetworkEntity.IsOwner)
				{
					RemoteState value;
					if (TryReadDesired(item, out var state))
					{
						_remoteStates[item.PlayerID.SmallID] = state;
						ApplyRemote(item, state);
					}
					else if (_remoteStates.TryGetValue(item.PlayerID.SmallID, out value))
					{
						ApplyRemote(item, value);
					}
				}
			}
		}
		catch (Exception value2)
		{
			if (!_reconcileWarningLogged)
			{
				_reconcileWarningLogged = true;
				Instance log = Log;
				if (log != null)
				{
					log.Warning($"Reconcile failed (logged once): {value2}");
				}
			}
		}
		if (IsInvisible && SelfView != SelfViewMode.FullBody)
		{
			HideSelfRenderers(SelfView == SelfViewMode.HandsOnly);
		}
		ReconcileRemoteItemHides();
	}

	private static void CollectHeldProps(RigManager rigManager, HashSet<ushort> target)
	{
		PhysicsRig val = ((rigManager != null) ? rigManager.physicsRig : null);
		if (!((Object)(object)val == (Object)null))
		{
			AddHeldProp(val.leftHand, target);
			AddHeldProp(val.rightHand, target);
		}
	}

	private static void AddHeldProp(Hand hand, HashSet<ushort> target)
	{
		try
		{
			object obj;
			if (hand == null)
			{
				obj = null;
			}
			else
			{
				HandReciever attachedReceiver = hand.AttachedReceiver;
				obj = ((attachedReceiver != null) ? ((Il2CppObjectBase)attachedReceiver).TryCast<Grip>() : null);
			}
			Grip val = (Grip)obj;
			NetworkEntity entity = default(NetworkEntity);
			if ((Object)(object)val != (Object)null && GripExtender.Cache.TryGet(val, ref entity))
			{
				AddPropEntity(entity, target);
			}
		}
		catch
		{
		}
	}

	private static void CollectHolsteredProps(NetworkPlayer player, RigManager rigManager, HashSet<ushort> target)
	{
		List<InventorySlotReceiver> list = new List<InventorySlotReceiver>();
		try
		{
			object obj;
			if (player == null)
			{
				obj = null;
			}
			else
			{
				NetworkEntity networkEntity = player.NetworkEntity;
				obj = ((networkEntity != null) ? networkEntity.GetExtender<InventorySlotReceiverExtender>() : null);
			}
			InventorySlotReceiverExtender val = (InventorySlotReceiverExtender)obj;
			if (((EntityComponentArrayExtender<InventorySlotReceiver>)(object)val)?.Components != null)
			{
				list.AddRange(((EntityComponentArrayExtender<InventorySlotReceiver>)(object)val).Components);
			}
		}
		catch
		{
		}
		if (list.Count == 0)
		{
			try
			{
				if ((Object)(object)rigManager != (Object)null)
				{
					list.AddRange((IEnumerable<InventorySlotReceiver>)((Component)rigManager).GetComponentsInChildren<InventorySlotReceiver>(true));
				}
			}
			catch
			{
			}
		}
		NetworkEntity entity = default(NetworkEntity);
		foreach (InventorySlotReceiver item in list)
		{
			try
			{
				WeaponSlot val2 = ((item != null) ? item._slottedWeapon : null);
				if ((Object)(object)val2 != (Object)null && WeaponSlotExtender.Cache.TryGet(val2, ref entity))
				{
					AddPropEntity(entity, target);
				}
			}
			catch
			{
			}
		}
	}

	private static void AddPropEntity(NetworkEntity entity, HashSet<ushort> target)
	{
		try
		{
			if (entity != null && entity.IsRegistered && entity.GetExtender<NetworkProp>() != null)
			{
				target.Add(entity.ID);
			}
		}
		catch
		{
		}
	}

	private static void ExpandCarriedProps(HashSet<ushort> target)
	{
		if (target.Count == 0)
		{
			return;
		}
		NetworkEntity entity2 = default(NetworkEntity);
		foreach (ushort item in new List<ushort>(target))
		{
			try
			{
				NetworkEntity entity = NetworkEntityManager.IDManager.RegisteredEntities.GetEntity(item);
				AmmoSocket obj = ((entity == null) ? null : ((EntityComponentExtender<AmmoSocket>)(object)entity.GetExtender<AmmoSocketExtender>())?.Component);
				object obj2;
				if (obj == null)
				{
					obj2 = null;
				}
				else
				{
					AmmoPlug magazinePlug = obj._magazinePlug;
					obj2 = ((magazinePlug != null) ? magazinePlug.magazine : null);
				}
				Magazine val = (Magazine)obj2;
				if ((Object)(object)val != (Object)null && MagazineExtender.Cache.TryGet(val, ref entity2))
				{
					AddPropEntity(entity2, target);
				}
			}
			catch
			{
			}
		}
		List<Transform> list = new List<Transform>();
		HashSet<int> hashSet = new HashSet<int>();
		foreach (ushort item2 in new List<ushort>(target))
		{
			try
			{
				NetworkEntity entity3 = NetworkEntityManager.IDManager.RegisteredEntities.GetEntity(item2);
				object obj4;
				if (entity3 == null)
				{
					obj4 = null;
				}
				else
				{
					NetworkProp extender = entity3.GetExtender<NetworkProp>();
					if (extender == null)
					{
						obj4 = null;
					}
					else
					{
						MarrowEntity marrowEntity = extender.MarrowEntity;
						obj4 = ((marrowEntity != null) ? ((Component)marrowEntity).transform : null);
					}
				}
				Transform val2 = (Transform)obj4;
				if (!((Object)(object)val2 == (Object)null))
				{
					list.Add(val2);
					hashSet.Add(((Object)val2).GetInstanceID());
				}
			}
			catch
			{
			}
		}
		if (list.Count == 0)
		{
			return;
		}
		foreach (NetworkEntity item3 in new List<NetworkEntity>(NetworkEntityManager.IDManager.RegisteredEntities.IDEntityLookup.Values))
		{
			try
			{
				if (item3 == null || target.Contains(item3.ID))
				{
					continue;
				}
				NetworkProp extender2 = item3.GetExtender<NetworkProp>();
				object obj6;
				if (extender2 == null)
				{
					obj6 = null;
				}
				else
				{
					MarrowEntity marrowEntity2 = extender2.MarrowEntity;
					obj6 = ((marrowEntity2 != null) ? ((Component)marrowEntity2).transform : null);
				}
				Transform val3 = (Transform)obj6;
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				bool flag = IsDescendantOfAny(val3, hashSet);
				bool flag2 = false;
				if (!flag)
				{
					HashSet<int> rootIds = new HashSet<int> { ((Object)val3).GetInstanceID() };
					foreach (Transform item4 in list)
					{
						if (IsDescendantOfAny(item4, rootIds))
						{
							flag2 = true;
							break;
						}
					}
				}
				if (flag || flag2)
				{
					target.Add(item3.ID);
				}
			}
			catch
			{
			}
		}
	}

	private static void ReconcileRemoteItemHides()
	{
		Dictionary<ushort, byte> dictionary = new Dictionary<ushort, byte>();
		try
		{
			RigManager rigManager = Player.RigManager;
			foreach (NetworkPlayer item in new List<NetworkPlayer>(NetworkPlayer.Players))
			{
				if (((item != null) ? item.PlayerID : null) == null || item.NetworkEntity == null || item.NetworkEntity.IsOwner || !item.HasRig || !_remoteStates.TryGetValue(item.PlayerID.SmallID, out var value) || !value.Invisible || value.ItemHiding == ItemHidingMode.Off)
				{
					continue;
				}
				RigRefs rigRefs = item.RigRefs;
				RigManager rigManager2 = ((rigRefs != null) ? rigRefs.RigManager : null);
				HashSet<ushort> hashSet = new HashSet<ushort>();
				CollectHeldProps(rigManager2, hashSet);
				if (value.ItemHiding == ItemHidingMode.HeldAndHolstered)
				{
					CollectHolsteredProps(item, rigManager2, hashSet);
				}
				ExpandCarriedProps(hashSet);
				foreach (ushort item2 in hashSet)
				{
					NetworkEntity entity = NetworkEntityManager.IDManager.RegisteredEntities.GetEntity(item2);
					if (entity != null && !IsHeldByLocalPlayer(entity, rigManager))
					{
						dictionary[item2] = item.PlayerID.SmallID;
					}
				}
			}
		}
		catch (Exception ex)
		{
			if (!_itemWarningLogged)
			{
				_itemWarningLogged = true;
				Instance log = Log;
				if (log != null)
				{
					log.Warning("Remote item scan failed (logged once): " + ex.Message);
				}
			}
		}
		foreach (ushort item3 in new List<ushort>(_remoteItemHides.Keys))
		{
			if (!dictionary.TryGetValue(item3, out var value2) || _remoteItemHides[item3].SmallId != value2)
			{
				RestoreRemoteItemHide(item3);
			}
		}
		foreach (KeyValuePair<ushort, byte> item4 in dictionary)
		{
			ApplyRemoteItemHide(item4.Key, item4.Value);
		}
	}

	private static bool IsHeldByLocalPlayer(NetworkEntity entity, RigManager localRig)
	{
		if (entity == null || (Object)(object)localRig == (Object)null)
		{
			return false;
		}
		try
		{
			GripExtender extender = entity.GetExtender<GripExtender>();
			return extender != null && extender.IsHeldBy(localRig);
		}
		catch
		{
			return false;
		}
	}

	private static void ApplyRemoteItemHide(ushort entityId, byte smallId)
	{
		try
		{
			NetworkEntity entity = NetworkEntityManager.IDManager.RegisteredEntities.GetEntity(entityId);
			object obj;
			if (entity == null)
			{
				obj = null;
			}
			else
			{
				NetworkProp extender = entity.GetExtender<NetworkProp>();
				obj = ((extender != null) ? extender.MarrowEntity : null);
			}
			MarrowEntity val = (MarrowEntity)obj;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if (!_remoteItemHides.TryGetValue(entityId, out var value))
			{
				value = new RemoteItemHide();
				_remoteItemHides[entityId] = value;
			}
			value.SmallId = smallId;
			HashSet<int> hashSet = new HashSet<int>();
			foreach (Renderer disabledRenderer in value.DisabledRenderers)
			{
				int num = SafeInstanceId((Object)(object)disabledRenderer);
				if (num != int.MinValue)
				{
					hashSet.Add(num);
				}
			}
			foreach (Renderer componentsInChild in ((Component)val).GetComponentsInChildren<Renderer>(true))
			{
				if (!((Object)(object)componentsInChild == (Object)null) && componentsInChild.enabled)
				{
					int instanceID = ((Object)componentsInChild).GetInstanceID();
					if (!hashSet.Contains(instanceID))
					{
						value.DisabledRenderers.Add(componentsInChild);
						hashSet.Add(instanceID);
					}
					componentsInChild.enabled = false;
				}
			}
		}
		catch
		{
		}
	}

	private static void RestoreRemoteItemHide(ushort entityId)
	{
		if (!_remoteItemHides.Remove(entityId, out var value))
		{
			return;
		}
		foreach (Renderer disabledRenderer in value.DisabledRenderers)
		{
			try
			{
				if ((Object)(object)disabledRenderer != (Object)null)
				{
					disabledRenderer.enabled = true;
				}
			}
			catch
			{
			}
		}
	}

	private static void RestoreRemoteItemHidesForPlayer(byte smallId)
	{
		foreach (ushort item in new List<ushort>(_remoteItemHides.Keys))
		{
			if (_remoteItemHides[item].SmallId == smallId)
			{
				RestoreRemoteItemHide(item);
			}
		}
	}

	private static void RestoreAllRemoteItemHides()
	{
		foreach (ushort item in new List<ushort>(_remoteItemHides.Keys))
		{
			RestoreRemoteItemHide(item);
		}
	}

	private static void ApplySelfVisibility()
	{
		RestoreSelfRenderers();
		if (IsInvisible && SelfView != SelfViewMode.FullBody)
		{
			HideSelfRenderers(SelfView == SelfViewMode.HandsOnly);
		}
	}

	private static void HideSelfRenderers(bool handsOnly = false)
	{
		try
		{
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null)
			{
				return;
			}
			List<Component> list = new List<Component>();
			if ((Object)(object)rigManager.physicsRig != (Object)null)
			{
				list.Add((Component)(object)rigManager.physicsRig);
			}
			if ((Object)(object)rigManager.avatar != (Object)null)
			{
				list.Add((Component)(object)rigManager.avatar);
			}
			HashSet<int> hashSet = null;
			if (handsOnly)
			{
				HashSet<int> handRootIds = CollectHandRootIds(rigManager);
				hashSet = CollectHandRendererIds(rigManager, list, handRootIds);
				ApplyHandOnlyMeshSwaps(list, handRootIds, hashSet);
			}
			foreach (Component item in list)
			{
				foreach (Renderer componentsInChild in item.GetComponentsInChildren<Renderer>(true))
				{
					if ((Object)(object)componentsInChild == (Object)null || !componentsInChild.enabled)
					{
						continue;
					}
					int instanceID = ((Object)componentsInChild).GetInstanceID();
					if (hashSet != null && hashSet.Contains(instanceID))
					{
						continue;
					}
					bool flag = false;
					foreach (Renderer hiddenSelfRenderer in _hiddenSelfRenderers)
					{
						if ((Object)(object)hiddenSelfRenderer != (Object)null && ((Object)hiddenSelfRenderer).GetInstanceID() == instanceID)
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						_hiddenSelfRenderers.Add(componentsInChild);
					}
					componentsInChild.enabled = false;
				}
			}
			if (handsOnly)
			{
				LogHandsOnlyResult(rigManager, hashSet?.Count ?? 0);
			}
		}
		catch (Exception ex)
		{
			Instance log = Log;
			if (log != null)
			{
				log.Warning("HideSelf failed: " + ex.Message);
			}
		}
	}

	private static HashSet<int> CollectHandRendererIds(RigManager rigManager, List<Component> roots, HashSet<int> handRootIds)
	{
		HashSet<int> hashSet = new HashSet<int>();
		foreach (Component root in roots)
		{
			if ((Object)(object)root == (Object)null)
			{
				continue;
			}
			try
			{
				foreach (Renderer componentsInChild in root.GetComponentsInChildren<Renderer>(true))
				{
					if ((Object)(object)componentsInChild != (Object)null && IsDescendantOfAny(((Component)componentsInChild).transform, handRootIds))
					{
						hashSet.Add(((Object)componentsInChild).GetInstanceID());
					}
				}
			}
			catch
			{
			}
			try
			{
				foreach (SkinnedMeshRenderer componentsInChild2 in root.GetComponentsInChildren<SkinnedMeshRenderer>(true))
				{
					if ((Object)(object)componentsInChild2 != (Object)null && IsHandOnlySkinnedRenderer(componentsInChild2, handRootIds))
					{
						hashSet.Add(((Object)componentsInChild2).GetInstanceID());
					}
				}
			}
			catch
			{
			}
		}
		return hashSet;
	}

	private static void ApplyHandOnlyMeshSwaps(List<Component> roots, HashSet<int> handRootIds, HashSet<int> handRendererIds)
	{
		HashSet<int> hashSet = new HashSet<int>();
		foreach (Component root in roots)
		{
			if ((Object)(object)root == (Object)null)
			{
				continue;
			}
			try
			{
				foreach (SkinnedMeshRenderer componentsInChild in root.GetComponentsInChildren<SkinnedMeshRenderer>(true))
				{
					if ((Object)(object)componentsInChild == (Object)null)
					{
						continue;
					}
					int instanceID;
					try
					{
						instanceID = ((Object)componentsInChild).GetInstanceID();
					}
					catch
					{
						continue;
					}
					if (!hashSet.Add(instanceID) || handRendererIds.Contains(instanceID))
					{
						continue;
					}
					try
					{
						if (!((Renderer)componentsInChild).enabled)
						{
							continue;
						}
					}
					catch
					{
						continue;
					}
					HandMeshSwap handMeshSwap = null;
					foreach (HandMeshSwap handMeshSwap2 in _handMeshSwaps)
					{
						if (handMeshSwap2.RendererId == instanceID)
						{
							handMeshSwap = handMeshSwap2;
							break;
						}
					}
					Mesh val = null;
					try
					{
						val = componentsInChild.sharedMesh;
					}
					catch
					{
					}
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Mesh originalMesh = val;
					if (handMeshSwap != null)
					{
						int num = SafeInstanceId((Object)(object)val);
						int num2 = SafeInstanceId((Object)(object)handMeshSwap.HandMesh);
						int num3 = SafeInstanceId((Object)(object)handMeshSwap.OriginalMesh);
						if (num != int.MinValue && num == num2)
						{
							handRendererIds.Add(instanceID);
							continue;
						}
						if (num != int.MinValue && num == num3)
						{
							originalMesh = handMeshSwap.OriginalMesh;
						}
						else
						{
							_handMeshSwaps.Remove(handMeshSwap);
							handMeshSwap = null;
							originalMesh = val;
						}
					}
					if (TryGetOrCreateHandOnlyMesh(componentsInChild, originalMesh, handRootIds, out var handMesh) && !((Object)(object)handMesh == (Object)null))
					{
						try
						{
							componentsInChild.sharedMesh = handMesh;
						}
						catch
						{
							continue;
						}
						if (handMeshSwap == null)
						{
							handMeshSwap = new HandMeshSwap
							{
								RendererId = instanceID
							};
							_handMeshSwaps.Add(handMeshSwap);
						}
						handMeshSwap.Renderer = componentsInChild;
						handMeshSwap.OriginalMesh = originalMesh;
						handMeshSwap.HandMesh = handMesh;
						handRendererIds.Add(instanceID);
					}
				}
			}
			catch
			{
			}
		}
	}

	private static bool TryGetOrCreateHandOnlyMesh(SkinnedMeshRenderer renderer, Mesh originalMesh, HashSet<int> handRootIds, out Mesh handMesh)
	{
		//IL_0176: 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)
		handMesh = null;
		if ((Object)(object)renderer == (Object)null || (Object)(object)originalMesh == (Object)null)
		{
			return false;
		}
		List<bool> list = new List<bool>();
		List<int> list2 = new List<int>();
		try
		{
			Il2CppReferenceArray<Transform> bones = renderer.bones;
			if (bones == null)
			{
				return false;
			}
			foreach (Transform item in (Il2CppArrayBase<Transform>)(object)bones)
			{
				bool flag = (Object)(object)item != (Object)null && IsHandBone(item, handRootIds);
				if (flag)
				{
					list2.Add(list.Count);
				}
				list.Add(flag);
			}
		}
		catch
		{
			return false;
		}
		if (list2.Count == 0)
		{
			return false;
		}
		string text = SafeInstanceId((Object)(object)originalMesh) + ":" + string.Join(",", list2);
		if (_handMeshCache.TryGetValue(text, out handMesh))
		{
			return (Object)(object)handMesh != (Object)null;
		}
		if (_handMeshFailures.Contains(text))
		{
			return false;
		}
		Mesh val = null;
		try
		{
			val = Object.Instantiate<Mesh>(originalMesh);
			if ((Object)(object)val == (Object)null)
			{
				_handMeshFailures.Add(text);
				return false;
			}
			((Object)val).name = ((Object)originalMesh).name + "_InvisibleTouch_HandsOnly";
			Il2CppStructArray<BoneWeight> boneWeights = val.boneWeights;
			if (boneWeights == null || ((Il2CppArrayBase<BoneWeight>)(object)boneWeights).Length == 0)
			{
				Object.Destroy((Object)(object)val);
				_handMeshFailures.Add(text);
				return false;
			}
			bool[] array = new bool[((Il2CppArrayBase<BoneWeight>)(object)boneWeights).Length];
			int num = 0;
			for (int i = 0; i < ((Il2CppArrayBase<BoneWeight>)(object)boneWeights).Length; i++)
			{
				BoneWeight val2 = ((Il2CppArrayBase<BoneWeight>)(object)boneWeights)[i];
				if (HandBoneWeight(((BoneWeight)(ref val2)).boneIndex0, ((BoneWeight)(ref val2)).weight0, list) + HandBoneWeight(((BoneWeight)(ref val2)).boneIndex1, ((BoneWeight)(ref val2)).weight1, list) + HandBoneWeight(((BoneWeight)(ref val2)).boneIndex2, ((BoneWeight)(ref val2)).weight2, list) + HandBoneWeight(((BoneWeight)(ref val2)).boneIndex3, ((BoneWeight)(ref val2)).weight3, list) >= 0.5f)
				{
					array[i] = true;
					num++;
				}
			}
			if (num == 0)
			{
				Object.Destroy((Object)(object)val);
				_handMeshFailures.Add(text);
				return false;
			}
			int num2 = 0;
			int subMeshCount = val.subMeshCount;
			for (int j = 0; j < subMeshCount; j++)
			{
				List<int> val3 = new List<int>();
				val.GetTriangles(val3, j);
				List<int> val4 = new List<int>();
				for (int k = 0; k + 2 < val3.Count; k += 3)
				{
					int num3 = val3[k];
					int num4 = val3[k + 1];
					int num5 = val3[k + 2];
					if (IsHandVertex(num3, array) && IsHandVertex(num4, array) && IsHandVertex(num5, array))
					{
						val4.Add(num3);
						val4.Add(num4);
						val4.Add(num5);
						num2++;
					}
				}
				val.SetTriangles(val4, j);
			}
			if (num2 == 0)
			{
				Object.Destroy((Object)(object)val);
				_handMeshFailures.Add(text);
				return false;
			}
			_handMeshCache[text] = val;
			handMesh = val;
			return true;
		}
		catch (Exception ex)
		{
			if ((Object)(object)val != (Object)null)
			{
				try
				{
					Object.Destroy((Object)(object)val);
				}
				catch
				{
				}
			}
			_handMeshFailures.Add(text);
			if (!_handMeshWarningLogged)
			{
				_handMeshWarningLogged = true;
				Instance log = Log;
				if (log != null)
				{
					log.Warning("Self View Hands Only mesh filtering failed (logged once): " + ex.Message);
				}
			}
			return false;
		}
	}

	private static float HandBoneWeight(int boneIndex, float weight, List<bool> handBoneFlags)
	{
		if (boneIndex < 0 || boneIndex >= handBoneFlags.Count || !handBoneFlags[boneIndex])
		{
			return 0f;
		}
		return weight;
	}

	private static bool IsHandVertex(int vertexIndex, bool[] vertexIsHand)
	{
		if (vertexIndex >= 0 && vertexIndex < vertexIsHand.Length)
		{
			return vertexIsHand[vertexIndex];
		}
		return false;
	}

	private static int SafeInstanceId(Object obj)
	{
		if (obj == (Object)null)
		{
			return int.MinValue;
		}
		try
		{
			return obj.GetInstanceID();
		}
		catch
		{
			return int.MinValue;
		}
	}

	private static HashSet<int> CollectHandRootIds(RigManager rigManager)
	{
		HashSet<int> hashSet = new HashSet<int>();
		try
		{
			PhysicsRig physicsRig = rigManager.physicsRig;
			if ((Object)(object)physicsRig != (Object)null)
			{
				AddPhysicsHandRoots(hashSet, physicsRig.leftHand);
				AddPhysicsHandRoots(hashSet, physicsRig.rightHand);
			}
		}
		catch
		{
		}
		try
		{
			Avatar avatar = rigManager.avatar;
			if ((Object)(object)avatar != (Object)null)
			{
				AddHandRoot(hashSet, avatar.wristLf);
				AddHandRoot(hashSet, avatar.wristRt);
				AddHandRoot(hashSet, avatar.carpalLf);
				AddHandRoot(hashSet, avatar.carpalRt);
				try
				{
					ArtTransforms artTransforms = avatar.artTransforms;
					if (artTransforms != null)
					{
						AddHandRoot(hashSet, artTransforms.leftHand);
						AddHandRoot(hashSet, artTransforms.rightHand);
					}
				}
				catch
				{
				}
			}
		}
		catch
		{
		}
		try
		{
			ArtRig componentInChildren = ((Component)rigManager).GetComponentInChildren<ArtRig>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				AddHandRoot(hashSet, componentInChildren.artHandLf);
				AddHandRoot(hashSet, componentInChildren.artHandRt);
				AddHandRoot(hashSet, componentInChildren.artWristLf);
				AddHandRoot(hashSet, componentInChildren.artWristRt);
				AddHandRoot(hashSet, componentInChildren.artCarpalLf);
				AddHandRoot(hashSet, componentInChildren.artCarpalRt);
			}
		}
		catch
		{
		}
		try
		{
			foreach (HandPoseAnimator componentsInChild in ((Component)rigManager).GetComponentsInChildren<HandPoseAnimator>(true))
			{
				if (!((Object)(object)componentsInChild == (Object)null))
				{
					AddHandRoot(hashSet, ((Component)componentsInChild).transform);
					try
					{
						AddHandRoot(hashSet, componentsInChild.hand2Art);
					}
					catch
					{
					}
					try
					{
						AddHandRoot(hashSet, componentsInChild.handle);
					}
					catch
					{
					}
				}
			}
		}
		catch
		{
		}
		return hashSet;
	}

	private static void AddPhysicsHandRoots(HashSet<int> rootIds, Hand hand)
	{
		if ((Object)(object)hand == (Object)null)
		{
			return;
		}
		AddHandRoot(rootIds, ((Component)hand).transform);
		try
		{
			PhysHand physHand = hand.physHand;
			AddHandRoot(rootIds, (physHand != null) ? ((Component)physHand).transform : null);
		}
		catch
		{
		}
		try
		{
			SkeletonHand skeleton = hand.Skeleton;
			AddHandRoot(rootIds, (skeleton != null) ? ((Component)skeleton).transform : null);
		}
		catch
		{
		}
	}

	private static void AddHandRoot(HashSet<int> rootIds, Transform transform)
	{
		if ((Object)(object)transform != (Object)null)
		{
			rootIds.Add(((Object)transform).GetInstanceID());
		}
	}

	private static bool IsDescendantOfAny(Transform transform, HashSet<int> rootIds)
	{
		if ((Object)(object)transform == (Object)null || rootIds == null || rootIds.Count == 0)
		{
			return false;
		}
		Transform val = transform;
		while ((Object)(object)val != (Object)null)
		{
			if (rootIds.Contains(((Object)val).GetInstanceID()))
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private static bool IsHandOnlySkinnedRenderer(SkinnedMeshRenderer renderer, HashSet<int> handRootIds)
	{
		if (IsDescendantOfAny(((Component)renderer).transform, handRootIds))
		{
			return true;
		}
		try
		{
			GameObject gameObject = ((Component)renderer).gameObject;
			string text = ((gameObject != null) ? ((Object)gameObject).name : null);
			if (!string.IsNullOrEmpty(text))
			{
				string text2 = text.ToLowerInvariant();
				bool num = text2.Contains("hand") || text2.Contains("glove") || text2.Contains("finger");
				bool flag = text2.Contains("body") || text2.Contains("avatar") || text2.Contains("full");
				if (num && !flag)
				{
					return true;
				}
			}
		}
		catch
		{
		}
		try
		{
			Il2CppReferenceArray<Transform> bones = renderer.bones;
			if (bones == null)
			{
				return false;
			}
			Transform val = null;
			int num2 = int.MinValue;
			try
			{
				val = renderer.rootBone;
				if ((Object)(object)val != (Object)null)
				{
					num2 = ((Object)val).GetInstanceID();
				}
			}
			catch
			{
			}
			int num3 = 0;
			int num4 = 0;
			foreach (Transform item in (Il2CppArrayBase<Transform>)(object)bones)
			{
				if (!((Object)(object)item == (Object)null) && ((Object)item).GetInstanceID() != num2)
				{
					num3++;
					if (IsHandBone(item, handRootIds))
					{
						num4++;
					}
				}
			}
			if (num3 == 0)
			{
				return (Object)(object)val != (Object)null && IsHandBone(val, handRootIds);
			}
			if (num4 == 0)
			{
				return false;
			}
			int num5 = num3 - num4;
			return num5 == 0 || (num4 >= 4 && num5 <= 3 && (float)num4 / (float)num3 >= 0.75f);
		}
		catch
		{
			return false;
		}
	}

	private static bool IsHandBone(Transform bone, HashSet<int> handRootIds)
	{
		if ((Object)(object)bone == (Object)null)
		{
			return false;
		}
		if (IsDescendantOfAny(bone, handRootIds))
		{
			return true;
		}
		string name = ((Object)bone).name;
		if (string.IsNullOrEmpty(name))
		{
			return false;
		}
		string text = name.ToLowerInvariant();
		if (!text.Contains("hand") && !text.Contains("finger") && !text.Contains("thumb") && !text.Contains("index") && !text.Contains("middle") && !text.Contains("ring") && !text.Contains("pinky") && !text.Contains("little") && !text.Contains("palm") && !text.Contains("digit") && !text.Contains("wrist") && !text.Contains("carpal"))
		{
			return text.Contains("metacarpal");
		}
		return true;
	}

	private static void LogHandsOnlyResult(RigManager rigManager, int preservedRendererCount)
	{
		int value = (((Object)(object)rigManager != (Object)null) ? ((Object)rigManager).GetInstanceID() : 0);
		int value2 = 0;
		try
		{
			if ((Object)(object)((rigManager != null) ? rigManager.avatar : null) != (Object)null)
			{
				value2 = ((Object)rigManager.avatar).GetInstanceID();
			}
		}
		catch
		{
		}
		string text = $"{value}:{value2}:{preservedRendererCount}";
		if (text == _lastHandsOnlyLogSignature)
		{
			return;
		}
		_lastHandsOnlyLogSignature = text;
		if (preservedRendererCount > 0)
		{
			Instance log = Log;
			if (log != null)
			{
				log.Msg($"Self View Hands Only: keeping {preservedRendererCount} hand renderer(s) visible.");
			}
		}
		else
		{
			Instance log2 = Log;
			if (log2 != null)
			{
				log2.Warning("Self View Hands Only: no usable hand renderers or hand-weighted mesh geometry were found on this avatar, so the local body is fully hidden.");
			}
		}
	}

	private static void RestoreHandMeshSwaps()
	{
		foreach (HandMeshSwap handMeshSwap in _handMeshSwaps)
		{
			try
			{
				if (!((Object)(object)handMeshSwap.Renderer == (Object)null) && !((Object)(object)handMeshSwap.OriginalMesh == (Object)null) && !((Object)(object)handMeshSwap.HandMesh == (Object)null) && SafeInstanceId((Object)(object)handMeshSwap.Renderer.sharedMesh) == SafeInstanceId((Object)(object)handMeshSwap.HandMesh))
				{
					handMeshSwap.Renderer.sharedMesh = handMeshSwap.OriginalMesh;
				}
			}
			catch
			{
			}
		}
		_handMeshSwaps.Clear();
	}

	private static void DestroyCachedHandMeshes()
	{
		foreach (Mesh value in _handMeshCache.Values)
		{
			try
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			catch
			{
			}
		}
		_handMeshCache.Clear();
		_handMeshFailures.Clear();
	}

	private static void RestoreSelfRenderers()
	{
		RestoreHandMeshSwaps();
		foreach (Renderer hiddenSelfRenderer in _hiddenSelfRenderers)
		{
			try
			{
				if ((Object)(object)hiddenSelfRenderer != (Object)null)
				{
					hiddenSelfRenderer.enabled = true;
				}
			}
			catch
			{
			}
		}
		_hiddenSelfRenderers.Clear();
	}

	private static void PlayToggleHaptics(bool invisible, bool fromController)
	{
		if (!HapticsEnabled)
		{
			return;
		}
		int pulses = HapticPattern switch
		{
			HapticPattern.OneOnTwoOff => invisible ? 1 : 2, 
			HapticPattern.TwoOnOneOff => (!invisible) ? 1 : 2, 
			HapticPattern.SinglePulse => 1, 
			HapticPattern.DoublePulse => 2, 
			_ => invisible ? 1 : 2, 
		};
		float initialDelay = (fromController ? 0f : 0.15f);
		foreach (BaseController hapticController in GetHapticControllers(fromController))
		{
			PlayHapticPulses(hapticController, pulses, initialDelay);
		}
	}

	private static List<BaseController> GetHapticControllers(bool fromController)
	{
		List<BaseController> list = new List<BaseController>();
		try
		{
			switch (HapticTarget)
			{
			case HapticTarget.Left:
				AddHapticController(list, Player.LeftController);
				return list;
			case HapticTarget.Right:
				AddHapticController(list, Player.RightController);
				return list;
			case HapticTarget.Both:
				AddHapticController(list, Player.LeftController);
				AddHapticController(list, Player.RightController);
				return list;
			}
		}
		catch
		{
		}
		if (fromController && Binding != ControllerBinding.Disabled)
		{
			try
			{
				AddHapticController(list, (Binding == ControllerBinding.LeftStickClick) ? Player.LeftController : Player.RightController);
			}
			catch
			{
			}
			if (list.Count > 0)
			{
				return list;
			}
		}
		try
		{
			AddHapticController(list, Player.LeftController);
		}
		catch
		{
		}
		try
		{
			AddHapticController(list, Player.RightController);
		}
		catch
		{
		}
		return list;
	}

	private static void AddHapticController(List<BaseController> controllers, BaseController controller)
	{
		if ((Object)(object)controller == (Object)null)
		{
			return;
		}
		int instanceID = ((Object)controller).GetInstanceID();
		foreach (BaseController controller2 in controllers)
		{
			if ((Object)(object)controller2 != (Object)null && ((Object)controller2).GetInstanceID() == instanceID)
			{
				return;
			}
		}
		controllers.Add(controller);
	}

	private static void PlayHapticPulses(BaseController controller, int pulses, float initialDelay)
	{
		if ((Object)(object)controller == (Object)null || pulses <= 0)
		{
			return;
		}
		try
		{
			float num = 0.08f;
			float num2 = 120f;
			float num3 = 0.8f;
			Haptor val = null;
			try
			{
				val = controller.haptor;
			}
			catch
			{
			}
			if ((Object)(object)val != (Object)null)
			{
				try
				{
					if (val.hap_tap_duration > 0f)
					{
						num = Mathf.Clamp(val.hap_tap_duration, 0.03f, 0.2f);
					}
					if (val.hap_tap_frequency > 0f)
					{
						num2 = Mathf.Clamp(val.hap_tap_frequency, 20f, 320f);
					}
					if (val.hap_tap_amplitude > 0f)
					{
						num3 = Mathf.Clamp(val.hap_tap_amplitude, 0.1f, 1f);
					}
				}
				catch
				{
				}
			}
			num3 = Mathf.Clamp(num3 * HapticStrength, 0.05f, 1f);
			for (int i = 0; i < pulses; i++)
			{
				float num4 = initialDelay + (float)i * (num + 0.09f);
				if ((Object)(object)val != (Object)null)
				{
					val.SENDHAPTIC(num4, num, num2, num3);
				}
				else
				{
					controller.HapticAction(num4, num, num2, num3);
				}
			}
		}
		catch (Exception ex)
		{
			if (!_hapticsWarningLogged)
			{
				_hapticsWarningLogged = true;
				Instance log = Log;
				if (log != null)
				{
					log.Warning("Controller haptics failed (logged once): " + ex.Message);
				}
			}
		}
	}

	internal static void Shutdown()
	{
		RestoreSelfRenderers();
		RestoreAllRemoteVisualHides();
		RestoreAllRemoteItemHides();
		DestroyCachedHandMeshes();
	}

	internal static void OnUpdate()
	{
		PollControllerInput();
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (realtimeSinceStartup >= _nextReconcileTime)
		{
			_nextReconcileTime = realtimeSinceStartup + 0.5f;
			Reconcile();
		}
	}

	private static void PollControllerInput()
	{
		bool num = IsBindingPressed();
		if (num && !_previousBindingPressed)
		{
			ToggleFromController();
		}
		_previousBindingPressed = num;
	}

	private static bool IsBindingPressed()
	{
		if (Binding == ControllerBinding.Disabled)
		{
			return false;
		}
		try
		{
			BaseController val = ((Binding == ControllerBinding.LeftStickClick) ? Player.LeftController : Player.RightController);
			return (Object)(object)val != (Object)null && val.GetThumbStick();
		}
		catch
		{
			return false;
		}
	}
}
public sealed class InvisibilityModule : Module
{
	public override string Name => "Invisible Touch";

	public override string Author => "MyFirstTeam";

	public override Version Version => new Version(1, 0, 0);

	public override ConsoleColor Color => ConsoleColor.Cyan;

	protected override void OnModuleRegistered()
	{
		InvisibilityManager.InitializeFusionHooks();
	}

	protected override void OnModuleUnregistered()
	{
		InvisibilityManager.Shutdown();
	}
}
public enum ItemHidingMode
{
	Off,
	HeldOnly,
	HeldAndHolstered
}
public sealed class Main : MelonMod
{
	private MelonPreferences_Category _prefsCategory;

	private MelonPreferences_Entry<bool> _prefShowSelf;

	private MelonPreferences_Entry<string> _prefSelfView;

	private MelonPreferences_Entry<string> _prefBinding;

	private MelonPreferences_Entry<string> _prefItemHiding;

	private MelonPreferences_Entry<int> _prefCooldownSeconds;

	private MelonPreferences_Entry<bool> _prefHapticsEnabled;

	private MelonPreferences_Entry<string> _prefHapticPattern;

	private MelonPreferences_Entry<string> _prefHapticTarget;

	private MelonPreferences_Entry<float> _prefHapticStrength;

	private Page _mainPage;

	private Page _settingsPage;

	private BoolElement _invisibleElement;

	private FunctionElement _toggleElement;

	private FunctionElement _selfViewElement;

	private FunctionElement _itemHidingElement;

	private FunctionElement _bindingElement;

	private IntElement _cooldownElement;

	private BoolElement _hapticsEnabledElement;

	private FunctionElement _hapticPatternElement;

	private FunctionElement _hapticTargetElement;

	private FloatElement _hapticStrengthElement;

	private bool _menuCreated;

	public override void OnInitializeMelon()
	{
		InvisibilityManager.Log = ((MelonBase)this).LoggerInstance;
		CreatePreferences();
		TryCreateMenu();
		InvisibilityManager.OnLocalInvisibleChanged += delegate(bool value2)
		{
			if (_invisibleElement != null)
			{
				_invisibleElement.Value = value2;
			}
		};
		try
		{
			ModuleManager.RegisterModule<InvisibilityModule>();
		}
		catch (Exception value)
		{
			((MelonBase)this).LoggerInstance.Error($"Failed to register the Fusion module. Is LabFusion installed and loaded? {value}");
		}
		((MelonBase)this).LoggerInstance.Msg("Invisible Touch loaded. BoneMenu -> Invisible Touch");
	}

	public override void OnUpdate()
	{
		if (!_menuCreated)
		{
			TryCreateMenu();
		}
		InvisibilityManager.OnUpdate();
	}

	public override void OnDeinitializeMelon()
	{
		InvisibilityManager.Shutdown();
		MelonPreferences.Save();
	}

	private void CreatePreferences()
	{
		_prefsCategory = MelonPreferences.CreateCategory("InvisibleTouch");
		_prefShowSelf = _prefsCategory.CreateEntry<bool>("ShowSelf", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		SelfViewMode selfView = ((!_prefShowSelf.Value) ? SelfViewMode.Hidden : SelfViewMode.FullBody);
		_prefSelfView = _prefsCategory.CreateEntry<string>("SelfView", selfView.ToString(), (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		_prefBinding = _prefsCategory.CreateEntry<string>("ToggleButton", "LeftStickClick", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		_prefItemHiding = _prefsCategory.CreateEntry<string>("ItemHiding", "HeldAndHolstered", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		_prefCooldownSeconds = _prefsCategory.CreateEntry<int>("CooldownSeconds", 5, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		_prefHapticsEnabled = _prefsCategory.CreateEntry<bool>("HapticsEnabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		_prefHapticPattern = _prefsCategory.CreateEntry<string>("HapticPattern", "OneOnTwoOff", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		_prefHapticTarget = _prefsCategory.CreateEntry<string>("HapticTarget", "Auto", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		_prefHapticStrength = _prefsCategory.CreateEntry<float>("HapticStrength", 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
		if (Enum.TryParse<SelfViewMode>(_prefSelfView.Value, out var result) && Enum.IsDefined(typeof(SelfViewMode), result))
		{
			InvisibilityManager.SelfView = result;
		}
		else
		{
			InvisibilityManager.SelfView = selfView;
			_prefSelfView.Value = InvisibilityManager.SelfView.ToString();
		}
		if (Enum.TryParse<ControllerBinding>(_prefBinding.Value, out var result2) && Enum.IsDefined(typeof(ControllerBinding), result2))
		{
			InvisibilityManager.Binding = result2;
		}
		else
		{
			InvisibilityManager.Binding = ControllerBinding.LeftStickClick;
			_prefBinding.Value = InvisibilityManager.Binding.ToString();
		}
		InvisibilityManager.ItemHiding = ParseEnumPreference(_prefItemHiding, ItemHidingMode.HeldAndHolstered);
		InvisibilityManager.CooldownSeconds = _prefCooldownSeconds.Value;
		_prefCooldownSeconds.Value = InvisibilityManager.CooldownSeconds;
		InvisibilityManager.HapticsEnabled = _prefHapticsEnabled.Value;
		InvisibilityManager.HapticPattern = ParseEnumPreference(_prefHapticPattern, HapticPattern.OneOnTwoOff);
		InvisibilityManager.HapticTarget = ParseEnumPreference(_prefHapticTarget, HapticTarget.Auto);
		InvisibilityManager.HapticStrength = Mathf.Round(_prefHapticStrength.Value * 4f) / 4f;
		_prefHapticStrength.Value = InvisibilityManager.HapticStrength;
		MelonPreferences.Save();
	}

	private static T ParseEnumPreference<T>(MelonPreferences_Entry<string> preference, T fallback) where T : struct, Enum
	{
		if (Enum.TryParse<T>(preference.Value, out var result) && Enum.IsDefined(typeof(T), result))
		{
			return result;
		}
		preference.Value = fallback.ToString();
		return fallback;
	}

	private void SavePreferences()
	{
		_prefSelfView.Value = InvisibilityManager.SelfView.ToString();
		_prefShowSelf.Value = InvisibilityManager.SelfView != SelfViewMode.Hidden;
		_prefBinding.Value = InvisibilityManager.Binding.ToString();
		_prefItemHiding.Value = InvisibilityManager.ItemHiding.ToString();
		_prefCooldownSeconds.Value = InvisibilityManager.CooldownSeconds;
		_prefHapticsEnabled.Value = InvisibilityManager.HapticsEnabled;
		_prefHapticPattern.Value = InvisibilityManager.HapticPattern.ToString();
		_prefHapticTarget.Value = InvisibilityManager.HapticTarget.ToString();
		_prefHapticStrength.Value = InvisibilityManager.HapticStrength;
		MelonPreferences.Save();
	}

	private static T NextEnum<T>(T value) where T : struct, Enum
	{
		T[] array = (T[])Enum.GetValues(typeof(T));
		int num = Array.IndexOf(array, value);
		return array[(num + 1) % array.Length];
	}

	private void TryCreateMenu()
	{
		//IL_001b: 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_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: 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_00ff: 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_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: Unknown result type (might be due to invalid IL or missing references)
		//IL_0206: Unknown result type (might be due to invalid IL or missing references)
		if (!_menuCreated && Page.Root != null)
		{
			_mainPage = Page.Root.CreatePage("Invisible Touch", Color.cyan, 0, true);
			_invisibleElement = _mainPage.CreateBool("Invisible", Color.cyan, InvisibilityManager.IsInvisible, (Action<bool>)delegate(bool value)
			{
				InvisibilityManager.SetInvisible(value, "BoneMenu");
			});
			_toggleElement = _mainPage.CreateFunction("Toggle Invisibility", Color.white, (Action)delegate
			{
				InvisibilityManager.Toggle();
			});
			_settingsPage = _mainPage.CreatePage("Settings", Color.white, 0, true);
			_selfViewElement = _settingsPage.CreateFunction(MenuText.SelfViewElementName(InvisibilityManager.SelfView), Color.white, (Action)delegate
			{
				InvisibilityManager.SelfView = NextEnum(InvisibilityManager.SelfView);
				InvisibilityManager.OnSelfViewChanged();
				SavePreferences();
				RefreshMenuText();
				((MelonBase)this).LoggerInstance.Msg($"Self View set to {InvisibilityManager.SelfView}");
			});
			_itemHidingElement = _settingsPage.CreateFunction(MenuText.ItemHidingElementName(InvisibilityManager.ItemHiding), Color.white, (Action)delegate
			{
				InvisibilityManager.ItemHiding = NextEnum(InvisibilityManager.ItemHiding);
				InvisibilityManager.OnItemHidingChanged();
				SavePreferences();
				RefreshMenuText();
				((MelonBase)this).LoggerInstance.Msg($"Item hiding set to {InvisibilityManager.ItemHiding}");
			});
			_bindingElement = _settingsPage.CreateFunction(MenuText.BindingElementName(InvisibilityManager.Binding), Color.white, (Action)delegate
			{
				InvisibilityManager.Binding = NextEnum(InvisibilityManager.Binding);
				SavePreferences();
				RefreshMenuText();
				((MelonBase)this).LoggerInstance.Msg($"Toggle stick set to {InvisibilityManager.Binding}");
			});
			_cooldownElement = _settingsPage.CreateInt("Cooldown (seconds)", Color.white, InvisibilityManager.CooldownSeconds, 1, 0, 60, (Action<int>)delegate(int value)
			{
				InvisibilityManager.CooldownSeconds = value;
				InvisibilityManager.OnCooldownChanged();
				SavePreferences();
				((MelonBase)this).LoggerInstance.Msg($"Invisibility cooldown set to {InvisibilityManager.CooldownSeconds}s");
			});
			_hapticsEnabledElement = _settingsPage.CreateBool("Haptics", Color.white, InvisibilityManager.HapticsEnabled, (Action<bool>)delegate(bool value)
			{
				InvisibilityManager.HapticsEnabled = value;
				SavePreferences();
				((MelonBase)this).LoggerInstance.Msg("Toggle haptics: " + (value ? "ON" : "OFF"));
			});
			_hapticPatternElement = _settingsPage.CreateFunction(MenuText.HapticPatternElementName(InvisibilityManager.HapticPattern), Color.white, (Action)delegate
			{
				InvisibilityManager.HapticPattern = NextEnum(InvisibilityManager.HapticPattern);
				SavePreferences();
				RefreshMenuText();
				((MelonBase)this).LoggerInstance.Msg($"Haptic pattern set to {InvisibilityManager.HapticPattern}");
			});
			_hapticTargetElement = _settingsPage.CreateFunction(MenuText.HapticTargetElementName(InvisibilityManager.HapticTarget), Color.white, (Action)delegate
			{
				InvisibilityManager.HapticTarget = NextEnum(InvisibilityManager.HapticTarget);
				SavePreferences();
				RefreshMenuText();
				((MelonBase)this).LoggerInstance.Msg($"Haptic target set to {InvisibilityManager.HapticTarget}");
			});
			_hapticStrengthElement = _settingsPage.CreateFloat("Haptic Strength", Color.white, InvisibilityManager.HapticStrength, 0.25f, 0.25f, 2f, (Action<float>)delegate(float value)
			{
				InvisibilityManager.HapticStrength = value;
				SavePreferences();
			});
			_menuCreated = true;
			RefreshMenuText();
		}
	}

	private void RefreshMenuText()
	{
		if (_mainPage != null)
		{
			_mainPage.Name = "Invisible Touch";
		}
		if (_settingsPage != null)
		{
			_settingsPage.Name = "Settings";
		}
		if (_invisibleElement != null)
		{
			((Element)_invisibleElement).ElementName = "Invisible";
		}
		if (_toggleElement != null)
		{
			((Element)_toggleElement).ElementName = "Toggle Invisibility";
		}
		if (_selfViewElement != null)
		{
			((Element)_selfViewElement).ElementName = MenuText.SelfViewElementName(InvisibilityManager.SelfView);
		}
		if (_itemHidingElement != null)
		{
			((Element)_itemHidingElement).ElementName = MenuText.ItemHidingElementName(InvisibilityManager.ItemHiding);
			((Element)_itemHidingElement).SetTooltip("Other players must have this mod. Their clients hide carried item renderers while the item remains physical. Holsters also includes body-slot weapons and loaded magazines.");
		}
		if (_bindingElement != null)
		{
			((Element)_bindingElement).ElementName = MenuText.BindingElementName(InvisibilityManager.Binding);
		}
		if (_cooldownElement != null)
		{
			((Element)_cooldownElement).ElementName = "Cooldown (seconds)";
		}
		if (_hapticsEnabledElement != null)
		{
			((Element)_hapticsEnabledElement).ElementName = "Haptics";
			((Element)_hapticsEnabledElement).SetTooltip("Auto vibrates the toggle stick for controller toggles and both controllers for BoneMenu toggles.");
		}
		if (_hapticPatternElement != null)
		{
			((Element)_hapticPatternElement).ElementName = MenuText.HapticPatternElementName(InvisibilityManager.HapticPattern);
		}
		if (_hapticTargetElement != null)
		{
			((Element)_hapticTargetElement).ElementName = MenuText.HapticTargetElementName(InvisibilityManager.HapticTarget);
			((Element)_hapticTargetElement).SetTooltip("Auto vibrates the toggle stick for controller toggles and both controllers for BoneMenu toggles.");
		}
		if (_hapticStrengthElement != null)
		{
			((Element)_hapticStrengthElement).ElementName = "Haptic Strength";
		}
	}
}
internal static class MenuText
{
	public const string ModPageName = "Invisible Touch";

	public const string SettingsPageName = "Settings";

	public const string InvisibleLabel = "Invisible";

	public const string ToggleLabel = "Toggle Invisibility";

	public const string CooldownLabel = "Cooldown (seconds)";

	public const string HapticsEnabledLabel = "Haptics";

	public const string HapticStrengthLabel = "Haptic Strength";

	public const string ItemsTooltip = "Other players must have this mod. Their clients hide carried item renderers while the item remains physical. Holsters also includes body-slot weapons and loaded magazines.";

	public const string HapticsTooltip = "Auto vibrates the toggle stick for controller toggles and both controllers for BoneMenu toggles.";

	public static string SelfViewName(SelfViewMode value)
	{
		return value switch
		{
			SelfViewMode.FullBody => "Full Body", 
			SelfViewMode.HandsOnly => "Hands Only", 
			SelfViewMode.Hidden => "Hidden", 
			_ => value.ToString(), 
		};
	}

	public static string SelfViewElementName(SelfViewMode value)
	{
		return "Self View: " + SelfViewName(value);
	}

	public static string ItemHidingName(ItemHidingMode value)
	{
		return value switch
		{
			ItemHidingMode.Off => "Off", 
			ItemHidingMode.HeldOnly => "Held Only", 
			ItemHidingMode.HeldAndHolstered => "Held + Holsters", 
			_ => value.ToString(), 
		};
	}

	public static string ItemHidingElementName(ItemHidingMode value)
	{
		return "Items: " + ItemHidingName(value);
	}

	public static string BindingName(ControllerBinding value)
	{
		return value switch
		{
			ControllerBinding.LeftStickClick => "Left Click", 
			ControllerBinding.RightStickClick => "Right Click", 
			ControllerBinding.Disabled => "Disabled", 
			_ => value.ToString(), 
		};
	}

	public static string BindingElementName(ControllerBinding value)
	{
		return "Toggle Stick: " + BindingName(value);
	}

	public static string HapticPatternName(HapticPattern value)
	{
		return value switch
		{
			HapticPattern.OneOnTwoOff => "1 ON / 2 OFF", 
			HapticPattern.TwoOnOneOff => "2 ON / 1 OFF", 
			HapticPattern.SinglePulse => "Single Pulse", 
			HapticPattern.DoublePulse => "Double Pulse", 
			_ => value.ToString(), 
		};
	}

	public static string HapticPatternElementName(HapticPattern value)
	{
		return "Haptic Pattern: " + HapticPatternName(value);
	}

	public static string HapticTargetName(HapticTarget value)
	{
		return value switch
		{
			HapticTarget.Auto => "Auto", 
			HapticTarget.Left => "Left", 
			HapticTarget.Right => "Right", 
			HapticTarget.Both => "Both", 
			_ => value.ToString(), 
		};
	}

	public static string HapticTargetElementName(HapticTarget value)
	{
		return "Haptic Hand: " + HapticTargetName(value);
	}
}
public enum SelfViewMode
{
	FullBody,
	HandsOnly,
	Hidden
}