Decompiled source of SpectatorHeadOffsetRemover v1.0.3

HeadOffsetRemover.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BoneLib;
using BoneLib.BoneMenu;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.VRMK;
using Il2CppSystem;
using MelonLoader;
using Microsoft.CodeAnalysis;
using OffsetRemover;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "Head Offset Remover", "1.0.0", "Hiiiiii :3", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyVersion("0.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 OffsetRemover
{
	internal sealed class BonePair
	{
		public Transform src;

		public Transform dst;

		public bool isHeadSubtree;

		public Vector3 headRestLocal;
	}
	internal sealed class RendererPair
	{
		public Renderer src;

		public Renderer dst;

		public bool forceDisable;
	}
	public class Core : MelonMod
	{
		private enum CameraKind
		{
			Headset,
			Spectator,
			Other
		}

		public static GameObject headlessAvatarClone;

		public static Animator headlessAvatarCloneAnimator;

		public static bool IsEnabled = true;

		public static bool ignoreSpectator = true;

		private static BonePair[] bonePairsArr = new BonePair[0];

		private static int bonePairCount = 0;

		private static RendererPair[] rendererPairsArr = new RendererPair[0];

		private static int rendererPairCount = 0;

		private static readonly Dictionary<int, CameraKind> cameraKindCache = new Dictionary<int, CameraKind>();

		private static Transform headBoneOnClone;

		private static Transform headBoneOnSource;

		private static Vector3 headLocalRestPosition;

		private static Quaternion headLocalRestRotation;

		private static bool hasHeadCorrection;

		private static Dictionary<string, Vector3> headSubtreeLocalPositions = new Dictionary<string, Vector3>(StringComparer.OrdinalIgnoreCase);

		private static List<Renderer> selfRenderers = new List<Renderer>(64);

		private static List<Renderer> cloneAvatarRenderers = new List<Renderer>(64);

		private static int cloneBuiltForAvatarId = 0;

		private static volatile bool buildInProgress = false;

		private static float lastBuildAttemptTime = -999f;

		private const float RetryCooldownSeconds = 3f;

		private const float MinAcceptableRendererFraction = 0.5f;

		private static float lastErrorLogTime = -999f;

		private const float ErrorLogCooldownSeconds = 5f;

		private static readonly StringBuilder pathSb = new StringBuilder(256);

		private static readonly string[] nameStack = new string[64];

		private static int lastSyncedFrame = -1;

		private static readonly HashSet<string> ExcludedBoneNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<Transform> boneCollectScratch = new HashSet<Transform>();

		private static CameraKind ClassifyCamera(Camera cam)
		{
			int instanceID = ((Object)cam).GetInstanceID();
			if (cameraKindCache.TryGetValue(instanceID, out var value))
			{
				return value;
			}
			value = ((!(((Object)cam).name == "Headset")) ? ((((Object)cam).name == "Spectator Camera") ? CameraKind.Spectator : CameraKind.Other) : CameraKind.Headset);
			cameraKindCache[instanceID] = value;
			return value;
		}

		public override void OnInitializeMelon()
		{
			//IL_0067: 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_00ae: 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)
			MelonLogger.Msg("[OffsetRemover] Community Patch loaded.");
			RenderPipelineManager.beginCameraRendering += Action<ScriptableRenderContext, Camera>.op_Implicit((Action<ScriptableRenderContext, Camera>)OnBeginCameraRendering);
			Hooking.OnSwitchAvatarPostfix += delegate
			{
				if (IsEnabled)
				{
					MelonCoroutines.Start(RebuildClone(Player.RigManager));
				}
			};
			Page obj = Page.Root.CreatePage("OffsetRemover", Color.green, 0, true);
			obj.CreateBool("Enable OffsetRemover", Color.white, IsEnabled, (Action<bool>)delegate(bool v)
			{
				IsEnabled = v;
				if (!v)
				{
					DisableAndCleanUp();
					ApplyVisibilityToAllCameras();
				}
				else
				{
					ForceRebuild();
					ApplyVisibilityToAllCameras();
				}
			});
			obj.CreateBool("Don't Fix Spectator Camera", Color.yellow, ignoreSpectator, (Action<bool>)delegate(bool v)
			{
				ignoreSpectator = v;
				ApplyVisibilityToAllCameras();
			});
			obj.CreateFunction("Rebuild Clone", Color.red, (Action)ForceRebuild);
			MelonCoroutines.Start(SelfHealLoop());
		}

		private static void DisableAndCleanUp()
		{
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager != (Object)null && Object.op_Implicit((Object)(object)rigManager.avatar))
			{
				Il2CppArrayBase<Renderer> componentsInChildren = ((Component)rigManager.avatar).gameObject.GetComponentsInChildren<Renderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					if (!((Object)(object)componentsInChildren[i] == (Object)null))
					{
						try
						{
							componentsInChildren[i].forceRenderingOff = false;
						}
						catch
						{
						}
					}
				}
			}
			if ((Object)(object)headlessAvatarClone != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)headlessAvatarClone);
			}
			ResetCloneState();
		}

		private static void ApplyVisibilityToAllCameras()
		{
			try
			{
				foreach (Camera item in (Il2CppArrayBase<Camera>)(object)Camera.allCameras)
				{
					if ((Object)(object)item != (Object)null)
					{
						CheckRenderLoop(ClassifyCamera(item));
					}
				}
			}
			catch
			{
			}
		}

		private static void OnBeginCameraRendering(ScriptableRenderContext ctx, Camera cam)
		{
			if (!IsEnabled)
			{
				return;
			}
			try
			{
				CameraKind num = ClassifyCamera(cam);
				if (num != CameraKind.Headset)
				{
					SyncCloneIfNeeded();
				}
				CheckRenderLoop(num);
			}
			catch (Exception value)
			{
				if (Time.time - lastErrorLogTime > 5f)
				{
					lastErrorLogTime = Time.time;
					MelonLogger.Error($"[OffsetRemover] Render hook error: {value}");
				}
			}
		}

		private static IEnumerator SelfHealLoop()
		{
			while (true)
			{
				yield return (object)new WaitForSeconds(2f);
				if (!IsEnabled)
				{
					continue;
				}
				RigManager rigManager = Player.RigManager;
				if (!((Object)(object)rigManager == (Object)null) && Object.op_Implicit((Object)(object)rigManager.avatar))
				{
					int instanceID = ((Object)rigManager.avatar).GetInstanceID();
					if ((!((Object)(object)headlessAvatarClone != (Object)null) || cloneBuiltForAvatarId != instanceID) && !buildInProgress && Time.time - lastBuildAttemptTime > 3f)
					{
						MelonCoroutines.Start(RebuildClone(rigManager));
					}
				}
			}
		}

		private static void SyncCloneIfNeeded()
		{
			//IL_0080: 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_0062: 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_0143: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)headlessAvatarCloneAnimator == (Object)null || Time.frameCount == lastSyncedFrame)
			{
				return;
			}
			lastSyncedFrame = Time.frameCount;
			try
			{
				BonePair[] array = bonePairsArr;
				for (int i = 0; i < bonePairCount; i++)
				{
					BonePair bonePair = array[i];
					Transform src = bonePair.src;
					Transform dst = bonePair.dst;
					if (!((Object)(object)src == (Object)null) && !((Object)(object)dst == (Object)null))
					{
						if (bonePair.isHeadSubtree)
						{
							dst.localPosition = bonePair.headRestLocal;
							dst.localRotation = src.localRotation;
						}
						else
						{
							dst.SetPositionAndRotation(src.position, src.rotation);
						}
					}
				}
				RendererPair[] array2 = rendererPairsArr;
				for (int j = 0; j < rendererPairCount; j++)
				{
					RendererPair rendererPair = array2[j];
					Renderer src2 = rendererPair.src;
					Renderer dst2 = rendererPair.dst;
					if (!((Object)(object)src2 == (Object)null) && !((Object)(object)dst2 == (Object)null))
					{
						if (rendererPair.forceDisable)
						{
							dst2.forceRenderingOff = true;
							continue;
						}
						dst2.forceRenderingOff = false;
						dst2.enabled = src2.enabled && ((Component)src2).gameObject.activeInHierarchy;
					}
				}
				if (hasHeadCorrection && (Object)(object)headBoneOnSource == (Object)null && (Object)(object)headBoneOnClone != (Object)null)
				{
					headBoneOnClone.localRotation = headLocalRestRotation;
				}
			}
			catch (Exception ex)
			{
				if (Time.time - lastErrorLogTime > 5f)
				{
					lastErrorLogTime = Time.time;
					MelonLogger.Warning("[OffsetRemover] Clone sync failed, rebuilding: " + ex.Message);
				}
				if ((Object)(object)headlessAvatarClone != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)headlessAvatarClone);
				}
				ResetCloneState();
			}
		}

		public static void ForceRebuild()
		{
			if (!IsEnabled)
			{
				MelonLogger.Msg("[OffsetRemover] Rebuild ignored -- disabled.");
				return;
			}
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null)
			{
				MelonLogger.Warning("[OffsetRemover] Rebuild: no RigManager.");
			}
			else
			{
				MelonCoroutines.Start(RebuildClone(rigManager));
			}
		}

		private static IEnumerator RebuildClone(RigManager manager)
		{
			if (buildInProgress)
			{
				yield break;
			}
			buildInProgress = true;
			lastBuildAttemptTime = Time.time;
			cameraKindCache.Clear();
			if ((Object)(object)headlessAvatarClone != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)headlessAvatarClone);
			}
			ResetCloneState();
			float waitStart = Time.time;
			while ((Object)(object)manager == (Object)null || !Object.op_Implicit((Object)(object)manager.avatar) || (Object)(object)manager.avatar.animator == (Object)null || ((Component)manager.avatar).gameObject.GetComponentsInChildren<SkinnedMeshRenderer>(true).Length == 0)
			{
				if (Time.time - waitStart > 5f)
				{
					buildInProgress = false;
					yield break;
				}
				yield return null;
			}
			selfRenderers.Clear();
			Il2CppArrayBase<Renderer> componentsInChildren = ((Component)manager.avatar).gameObject.GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				selfRenderers.Add(componentsInChildren[i]);
			}
			GameObject val = null;
			bool flag = true;
			try
			{
				GameObject val2 = new GameObject("DISABLED CONTAINER");
				val2.SetActive(false);
				val = Object.Instantiate<GameObject>(((Component)manager.avatar).gameObject, val2.transform);
				CleanClone(val);
				val.transform.parent = null;
				Object.Destroy((Object)(object)val2);
			}
			catch (Exception ex)
			{
				flag = false;
				_ = "instantiate/clean: " + ex.Message;
			}
			if (!flag)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
				ResetCloneState();
				buildInProgress = false;
				yield break;
			}
			cloneAvatarRenderers.Clear();
			Il2CppArrayBase<Renderer> componentsInChildren2 = val.GetComponentsInChildren<Renderer>(true);
			for (int j = 0; j < componentsInChildren2.Length; j++)
			{
				cloneAvatarRenderers.Add(componentsInChildren2[j]);
			}
			int length = componentsInChildren.Length;
			Avatar componentInChildren = val.GetComponentInChildren<Avatar>();
			Animator clnAnim = (((Object)(object)componentInChildren != (Object)null) ? componentInChildren.animator : null);
			try
			{
				BuildPairings(manager.avatar.animator, clnAnim, ((Component)manager.avatar).gameObject, val);
			}
			catch (Exception ex2)
			{
				flag = false;
				_ = "pairing: " + ex2.Message;
			}
			if (length > 0 && (float)cloneAvatarRenderers.Count < (float)length * 0.5f)
			{
				flag = false;
			}
			if (!flag)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
				ResetCloneState();
				buildInProgress = false;
				yield break;
			}
			((Object)val).name = "HEADFIX DISPLAY CLONE";
			headlessAvatarClone = val;
			headlessAvatarCloneAnimator = clnAnim;
			cloneBuiltForAvatarId = ((Object)manager.avatar).GetInstanceID();
			buildInProgress = false;
			ApplyVisibilityToAllCameras();
			MelonLogger.Msg($"[OffsetRemover] Clone built -- bonePairs={bonePairCount}, renderers={cloneAvatarRenderers.Count}, headCorrection={(hasHeadCorrection ? "yes" : "no")}");
		}

		private static void BuildPairings(Animator srcAnim, Animator clnAnim, GameObject srcRoot, GameObject clnRoot)
		{
			//IL_0012: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: 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)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0488: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			headBoneOnClone = null;
			headBoneOnSource = null;
			hasHeadCorrection = false;
			headLocalRestPosition = Vector3.zero;
			headLocalRestRotation = Quaternion.identity;
			headSubtreeLocalPositions.Clear();
			Transform[] array = CollectSkinnedBones(srcRoot);
			Transform[] array2 = CollectSkinnedBones(clnRoot);
			Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
			Dictionary<string, Transform> dictionary2 = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase);
			foreach (Transform val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string relativePathFast = GetRelativePathFast(val, srcRoot.transform);
					if (!dictionary.ContainsKey(relativePathFast))
					{
						dictionary[relativePathFast] = val;
					}
				}
			}
			foreach (Transform val2 in array2)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					string relativePathFast2 = GetRelativePathFast(val2, clnRoot.transform);
					if (!dictionary2.ContainsKey(relativePathFast2))
					{
						dictionary2[relativePathFast2] = val2;
					}
				}
			}
			Transform val3 = (((Object)(object)srcAnim != (Object)null && srcAnim.isHuman) ? srcAnim.GetBoneTransform((HumanBodyBones)10) : null);
			Transform val4 = (((Object)(object)clnAnim != (Object)null && clnAnim.isHuman) ? clnAnim.GetBoneTransform((HumanBodyBones)10) : null);
			if ((Object)(object)val4 == (Object)null)
			{
				foreach (KeyValuePair<string, Transform> item in dictionary2)
				{
					if (item.Key.IndexOf("head", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						val4 = item.Value;
						break;
					}
				}
			}
			if ((Object)(object)val3 == (Object)null)
			{
				foreach (KeyValuePair<string, Transform> item2 in dictionary)
				{
					if (item2.Key.IndexOf("head", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						val3 = item2.Value;
						break;
					}
				}
			}
			if ((Object)(object)val4 != (Object)null)
			{
				if (clnAnim != null)
				{
					clnAnim.Rebind();
				}
				if (clnAnim != null)
				{
					clnAnim.Update(0f);
				}
				headBoneOnClone = val4;
				headBoneOnSource = val3;
				headLocalRestPosition = val4.localPosition;
				headLocalRestRotation = val4.localRotation;
				hasHeadCorrection = true;
				Il2CppArrayBase<Transform> componentsInChildren = ((Component)val4).GetComponentsInChildren<Transform>(true);
				for (int k = 0; k < componentsInChildren.Length; k++)
				{
					Transform val5 = componentsInChildren[k];
					if (!((Object)(object)val5 == (Object)null) && !headSubtreeLocalPositions.ContainsKey(((Object)val5).name))
					{
						headSubtreeLocalPositions[((Object)val5).name] = val5.localPosition;
					}
				}
			}
			EnsureBonePairCapacity(array.Length);
			EnsureRendererPairCapacity(Math.Max(16, clnRendersCountEstimate(clnRoot)));
			bonePairCount = 0;
			rendererPairCount = 0;
			HashSet<Transform> hashSet = new HashSet<Transform>();
			foreach (KeyValuePair<string, Transform> item3 in dictionary)
			{
				if (dictionary2.TryGetValue(item3.Key, out var value))
				{
					BonePair bonePair = new BonePair
					{
						src = item3.Value,
						dst = value,
						isHeadSubtree = IsInHeadSubtreeCached(value)
					};
					if (bonePair.isHeadSubtree && headSubtreeLocalPositions.TryGetValue(((Object)value).name, out var value2))
					{
						bonePair.headRestLocal = value2;
					}
					bonePairsArr[bonePairCount++] = bonePair;
					hashSet.Add(value);
				}
			}
			Dictionary<string, List<Transform>> dictionary3 = BuildNameMap(array);
			Dictionary<string, List<Transform>> dictionary4 = BuildNameMap(array2);
			foreach (KeyValuePair<string, List<Transform>> item4 in dictionary3)
			{
				List<Transform> value3 = item4.Value;
				if (!dictionary4.TryGetValue(item4.Key, out var value4) || value4.Count == 0)
				{
					continue;
				}
				for (int l = 0; l < value3.Count; l++)
				{
					Transform val6 = value3[l];
					bool flag = false;
					for (int m = 0; m < bonePairCount; m++)
					{
						if ((Object)(object)bonePairsArr[m].src == (Object)(object)val6)
						{
							flag = true;
							break;
						}
					}
					if (flag)
					{
						continue;
					}
					Transform val7 = null;
					for (int n = 0; n < value4.Count; n++)
					{
						if (!hashSet.Contains(value4[n]))
						{
							val7 = value4[n];
							break;
						}
					}
					if ((Object)(object)val7 == (Object)null)
					{
						val7 = value4[0];
					}
					if (!((Object)(object)val7 == (Object)null))
					{
						BonePair bonePair2 = new BonePair
						{
							src = val6,
							dst = val7,
							isHeadSubtree = IsInHeadSubtreeCached(val7)
						};
						if (bonePair2.isHeadSubtree && headSubtreeLocalPositions.TryGetValue(((Object)val7).name, out var value5))
						{
							bonePair2.headRestLocal = value5;
						}
						bonePairsArr[bonePairCount++] = bonePair2;
						hashSet.Add(val7);
					}
				}
			}
			Il2CppArrayBase<Renderer> componentsInChildren2 = srcRoot.GetComponentsInChildren<Renderer>(true);
			Il2CppArrayBase<Renderer> componentsInChildren3 = clnRoot.GetComponentsInChildren<Renderer>(true);
			Dictionary<string, List<Renderer>> dictionary5 = new Dictionary<string, List<Renderer>>(StringComparer.OrdinalIgnoreCase);
			for (int num = 0; num < componentsInChildren3.Length; num++)
			{
				Renderer val8 = componentsInChildren3[num];
				if (!((Object)(object)val8 == (Object)null))
				{
					if (!dictionary5.TryGetValue(((Object)val8).name, out var value6))
					{
						value6 = new List<Renderer>(2);
						dictionary5[((Object)val8).name] = value6;
					}
					value6.Add(val8);
				}
			}
			HashSet<Renderer> hashSet2 = new HashSet<Renderer>();
			for (int num2 = 0; num2 < componentsInChildren2.Length; num2++)
			{
				Renderer val9 = componentsInChildren2[num2];
				if ((Object)(object)val9 == (Object)null || string.IsNullOrEmpty(((Object)val9).name) || !dictionary5.TryGetValue(((Object)val9).name, out var value7) || value7.Count == 0)
				{
					continue;
				}
				Renderer val10 = null;
				for (int num3 = 0; num3 < value7.Count; num3++)
				{
					if (!hashSet2.Contains(value7[num3]))
					{
						val10 = value7[num3];
						break;
					}
				}
				if ((Object)(object)val10 == (Object)null)
				{
					val10 = value7[0];
				}
				if (!((Object)(object)val10 == (Object)null))
				{
					try
					{
						val10.sharedMaterials = val9.sharedMaterials;
					}
					catch
					{
					}
					RendererPair rendererPair = new RendererPair
					{
						src = val9,
						dst = val10,
						forceDisable = ((Object)(object)val9.sharedMaterial != (Object)null && (Object)(object)val9.sharedMaterial.shader != (Object)null && ((Object)val9.sharedMaterial.shader).name == "SLZ/Icon Billboard")
					};
					rendererPairsArr[rendererPairCount++] = rendererPair;
					hashSet2.Add(val10);
				}
			}
		}

		private static Transform[] CollectSkinnedBones(GameObject root)
		{
			boneCollectScratch.Clear();
			Il2CppArrayBase<SkinnedMeshRenderer> componentsInChildren = root.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Il2CppReferenceArray<Transform> bones = componentsInChildren[i].bones;
				if (bones == null)
				{
					continue;
				}
				for (int j = 0; j < ((Il2CppArrayBase<Transform>)(object)bones).Length; j++)
				{
					if ((Object)(object)((Il2CppArrayBase<Transform>)(object)bones)[j] != (Object)null)
					{
						boneCollectScratch.Add(((Il2CppArrayBase<Transform>)(object)bones)[j]);
					}
				}
			}
			Transform[] array = (Transform[])(object)new Transform[boneCollectScratch.Count];
			boneCollectScratch.CopyTo(array);
			return array;
		}

		private static int clnRendersCountEstimate(GameObject clnRoot)
		{
			return clnRoot.GetComponentsInChildren<Renderer>(true)?.Length ?? 0;
		}

		private static bool IsInHeadSubtreeCached(Transform t)
		{
			if (!hasHeadCorrection || (Object)(object)t == (Object)null || (Object)(object)headBoneOnClone == (Object)null)
			{
				return false;
			}
			Transform val = t;
			while ((Object)(object)val != (Object)null)
			{
				if ((Object)(object)val == (Object)(object)headBoneOnClone)
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static Dictionary<string, List<Transform>> BuildNameMap(Transform[] arr)
		{
			Dictionary<string, List<Transform>> dictionary = new Dictionary<string, List<Transform>>(StringComparer.OrdinalIgnoreCase);
			foreach (Transform val in arr)
			{
				if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(((Object)val).name))
				{
					if (!dictionary.TryGetValue(((Object)val).name, out var value))
					{
						value = new List<Transform>(2);
						dictionary[((Object)val).name] = value;
					}
					value.Add(val);
				}
			}
			return dictionary;
		}

		private static void EnsureBonePairCapacity(int needed)
		{
			if (bonePairsArr.Length < needed)
			{
				BonePair[] array = new BonePair[Math.Max(needed, Math.Max(8, bonePairsArr.Length * 2))];
				for (int i = 0; i < bonePairsArr.Length; i++)
				{
					array[i] = bonePairsArr[i];
				}
				bonePairsArr = array;
			}
		}

		private static void EnsureRendererPairCapacity(int needed)
		{
			if (rendererPairsArr.Length < needed)
			{
				RendererPair[] array = new RendererPair[Math.Max(needed, Math.Max(8, rendererPairsArr.Length * 2))];
				for (int i = 0; i < rendererPairsArr.Length; i++)
				{
					array[i] = rendererPairsArr[i];
				}
				rendererPairsArr = array;
			}
		}

		private static void ResetCloneState()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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)
			headlessAvatarClone = null;
			headlessAvatarCloneAnimator = null;
			headBoneOnClone = null;
			headBoneOnSource = null;
			hasHeadCorrection = false;
			headLocalRestPosition = Vector3.zero;
			headLocalRestRotation = Quaternion.identity;
			headSubtreeLocalPositions.Clear();
			bonePairCount = 0;
			rendererPairCount = 0;
			selfRenderers.Clear();
			cloneAvatarRenderers.Clear();
		}

		private static void CleanClone(GameObject clone)
		{
			try
			{
				SafeDestroyAll<ConfigurableJoint>(Il2CppArrayBase<ConfigurableJoint>.op_Implicit(clone.GetComponentsInChildren<ConfigurableJoint>(true)));
			}
			catch
			{
			}
			try
			{
				SafeDestroyAll<Collider>(Il2CppArrayBase<Collider>.op_Implicit(clone.GetComponentsInChildren<Collider>(true)));
			}
			catch
			{
			}
			try
			{
				SafeDestroyAll<Rigidbody>(Il2CppArrayBase<Rigidbody>.op_Implicit(clone.GetComponentsInChildren<Rigidbody>(true)));
			}
			catch
			{
			}
			try
			{
				SafeDestroyAll<InteractableHost>(Il2CppArrayBase<InteractableHost>.op_Implicit(clone.GetComponentsInChildren<InteractableHost>(true)));
			}
			catch
			{
			}
			Il2CppArrayBase<MonoBehaviour> componentsInChildren = clone.GetComponentsInChildren<MonoBehaviour>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				MonoBehaviour val = componentsInChildren[i];
				if ((Object)(object)val == (Object)null || (Object)(object)((Il2CppObjectBase)val).TryCast<Avatar>() != (Object)null)
				{
					continue;
				}
				PlayerAvatarArt val2 = ((Il2CppObjectBase)val).TryCast<PlayerAvatarArt>();
				if ((Object)(object)val2 != (Object)null)
				{
					((Behaviour)val2).enabled = false;
					continue;
				}
				try
				{
					Object.DestroyImmediate((Object)(object)val);
				}
				catch
				{
				}
			}
		}

		private static void SafeDestroyAll<T>(T[] comps) where T : Object
		{
			foreach (T val in comps)
			{
				if (!((Object)(object)val == (Object)null))
				{
					try
					{
						Object.DestroyImmediate((Object)(object)val);
					}
					catch
					{
					}
				}
			}
		}

		private static string GetRelativePathFast(Transform t, Transform root)
		{
			if ((Object)(object)t == (Object)(object)root)
			{
				return "";
			}
			int num = 0;
			Transform val = t;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root && num < nameStack.Length)
			{
				nameStack[num++] = ((Object)val).name;
				val = val.parent;
			}
			pathSb.Clear();
			for (int num2 = num - 1; num2 >= 0; num2--)
			{
				pathSb.Append(nameStack[num2]);
				if (num2 > 0)
				{
					pathSb.Append('/');
				}
			}
			return pathSb.ToString();
		}

		private static void CheckRenderLoop(CameraKind kind)
		{
			if (!Player.HandsExist || !Object.op_Implicit((Object)(object)Player.Avatar))
			{
				return;
			}
			bool flag = kind switch
			{
				CameraKind.Headset => true, 
				CameraKind.Spectator => ignoreSpectator, 
				_ => false, 
			};
			bool flag2 = (Object)(object)headlessAvatarClone != (Object)null && cloneAvatarRenderers.Count > 0;
			if (flag || !flag2)
			{
				ToggleAvatarVisibility(selfRenderers, visible: true);
				if ((Object)(object)headlessAvatarClone != (Object)null)
				{
					ToggleAvatarVisibility(cloneAvatarRenderers, visible: false);
				}
			}
			else
			{
				ToggleAvatarVisibility(selfRenderers, visible: false);
				ToggleAvatarVisibility(cloneAvatarRenderers, visible: true);
			}
		}

		private static void ToggleAvatarVisibility(List<Renderer> renderers, bool visible)
		{
			for (int i = 0; i < renderers.Count; i++)
			{
				Renderer val = renderers[i];
				if (!((Object)(object)val == (Object)null))
				{
					try
					{
						val.forceRenderingOff = !visible;
					}
					catch
					{
					}
				}
			}
		}
	}
}