Decompiled source of CustomAvatars v1.0.0

Mods/CustomAvatars.dll

Decompiled 6 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CustomAvatars;
using HarmonyLib;
using Il2CppExitGames.Client.Photon;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppPhoton.Pun;
using Il2CppPhoton.Realtime;
using Il2CppPhoton.Voice.Unity;
using Il2CppRUMBLE.CharacterCreation.Interactable;
using Il2CppRUMBLE.Interactions.InteractionBase;
using Il2CppRUMBLE.Players;
using Il2CppRUMBLE.Players.Subsystems;
using Il2CppSmartLocalization.Editor;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.IO;
using Il2CppSystem.Text;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Utils;
using Newtonsoft.Json;
using RumbleModUI;
using RumbleModdingAPI;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Main), "CustomAvatars", "1.0.0", "ERROR", null)]
[assembly: MelonGame("Buckethead Entertainment", "RUMBLE")]
[assembly: MelonOptionalDependencies(new string[] { "RumbleHud" })]
[assembly: MelonColor(255, 255, 0, 0)]
[assembly: MelonAuthorColor(255, 255, 0, 0)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("CustomAvatars")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+cc871012b04cc83d228078d22dfe63a079095aed")]
[assembly: AssemblyProduct("CustomAvatars")]
[assembly: AssemblyTitle("CustomAvatars")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CustomAvatars;

public static class Extensions
{
	public static string TrimString(this string str)
	{
		return Regex.Replace(str, "<.*?>|\\(.*?\\)|[^a-zA-Z0-9_ ]", "").Trim();
	}

	public static T GetOrAddComponent<T>(this GameObject gameObject) where T : Component
	{
		T val = gameObject.GetComponent<T>();
		if ((Object)(object)val == (Object)null)
		{
			val = gameObject.AddComponent<T>();
		}
		return val;
	}

	public static T GetSavedValue<T>(this ModSetting<T> modSetting)
	{
		return (T)(((modSetting != null) ? ((ModSetting)modSetting).SavedValue : null) ?? ((object)default(T)));
	}

	public static T GetValue<T>(this ModSetting<T> modSetting)
	{
		return (T)(((modSetting != null) ? ((ModSetting)modSetting).Value : null) ?? ((object)default(T)));
	}
}
public static class TransformExtensions
{
	public static Transform FindDeep(this Transform parent, string name)
	{
		for (int i = 0; i < parent.childCount; i++)
		{
			if (((Object)parent.GetChild(i)).name == name)
			{
				return parent.GetChild(i);
			}
			Transform val = parent.GetChild(i).FindDeep(name);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
		}
		return null;
	}
}
[RegisterTypeInIl2Cpp]
public class CustomRigBone : MonoBehaviour
{
	public Quaternion rotationOffset = Quaternion.identity;
}
public class Main : MelonMod
{
	public string currentScene = "Loader";

	public bool sceneInitialized = false;

	public static Main instance;

	public GameObject rigParent;

	public GameObject avatarOptimizationParent;

	public GameObject refreshAvatarButton;

	public Mod mod = new Mod();

	public ModSetting<string> reloadKeybind;

	public ModSetting<bool> toggleLocal;

	public ModSetting<bool> toggleOthers;

	public ModSetting<bool> toggleVisibleToOthers;

	public ModSetting<bool> toggleInMatch;

	public ModSetting<bool> logAvatarStats;

	public ModSetting<bool> logOtherAvatarStats;

	public ModSetting<int> downloadLimitMB;

	public ModSetting<int> maxConcurrentDownloads;

	public ModSetting<bool> perPlayerHeader;

	public Dictionary<CustomRig, ModSetting<bool>> perPlayerToggles = new Dictionary<CustomRig, ModSetting<bool>>();

	private Dictionary<int, object> lastAvatars = new Dictionary<int, object>();

	public ModSetting<bool> UploadAvatar;

	public static Material poseGhostMaterial;

	public bool ranOnce = false;

	public Main()
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		instance = this;
	}

	public override void OnLateInitializeMelon()
	{
		Calls.onMapInitialized += Initialize;
		UI.instance.UI_Initialized += OnUIInitialized;
		((MelonBase)this).LoggerInstance.Msg("Custom Avatars Initialized");
		RigManager.Initialize(this);
	}

	public override void OnSceneWasLoaded(int buildIndex, string sceneName)
	{
		currentScene = sceneName;
		sceneInitialized = false;
		RigManager.rigs.Clear();
		Patches.loadedPlayers.Clear();
		rigParent = null;
	}

	public override void OnDeinitializeMelon()
	{
		string path = Path.Combine(MelonEnvironment.UserDataDirectory, "CustomAvatars", "Opponents");
		if (!Directory.Exists(path))
		{
			return;
		}
		string[] files = Directory.GetFiles(path);
		foreach (string path2 in files)
		{
			try
			{
				File.Delete(path2);
			}
			catch (IOException)
			{
			}
			catch (UnauthorizedAccessException)
			{
			}
		}
	}

	public void Initialize()
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: 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_018c: Expected O, but got Unknown
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_020f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_0261: Unknown result type (might be due to invalid IL or missing references)
		//IL_0293: Unknown result type (might be due to invalid IL or missing references)
		//IL_0298: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0300: Unknown result type (might be due to invalid IL or missing references)
		//IL_0348: Unknown result type (might be due to invalid IL or missing references)
		RigManager.ClearRigs();
		lastAvatars.Clear();
		string path = Path.Combine(MelonEnvironment.UserDataDirectory, "CustomAvatars", "Opponents");
		Directory.CreateDirectory(path);
		ApplyAvatars();
		if (currentScene == "Gym" && !sceneInitialized)
		{
			GameObject gameObject = TryOutModePanel.GetGameObject();
			gameObject.transform.localPosition = new Vector3(-0.1164f, 0.1962f, -0.1014f);
			refreshAvatarButton = Object.Instantiate<GameObject>(gameObject);
			refreshAvatarButton.transform.SetParent(gameObject.transform.parent, false);
			((Object)refreshAvatarButton).name = "Refresh Avatar Panel";
			refreshAvatarButton.transform.localPosition = new Vector3(0.1069f, 0.1962f, -0.1014f);
			InteractionButton component = ((Component)refreshAvatarButton.transform.GetChild(1).GetChild(0)).GetComponent<InteractionButton>();
			((UnityEventBase)component.onPressed).RemoveAllListeners();
			component.onPressed.AddListener(UnityAction.op_Implicit((Action)delegate
			{
				if ((bool)((ModSetting)toggleLocal).SavedValue)
				{
					Initialize();
				}
			}));
			TextMeshPro component2 = ((Component)refreshAvatarButton.transform.GetChild(1).GetChild(1)).GetComponent<TextMeshPro>();
			Object.Destroy((Object)(object)((Component)component2.transform).GetComponent<LocalizedTextTMPro>());
			((TMP_Text)component2).m_text = "Refresh Avatar";
			((TMP_Text)component2).fontSize = 0.25f;
			((TMP_Text)component2).ForceMeshUpdate(false, false);
			avatarOptimizationParent = new GameObject("AvatarDetails");
			avatarOptimizationParent.transform.localPosition = new Vector3(-2.9091f, 1.4218f, -1.5964f);
			avatarOptimizationParent.transform.localScale = Vector3.one * 0.5f;
			avatarOptimizationParent.transform.localRotation = Quaternion.Euler(0f, 206.6199f, 0f);
			GameObject val = Create.NewText("GOOD", 1f, new Color(0f, 0.5f, 0f), Vector3.zero, Quaternion.identity);
			((Object)val).name = "Summary";
			val.transform.SetParent(avatarOptimizationParent.transform, false);
			val.transform.localPosition = new Vector3(0f, 0.0919f, 0f);
			((TMP_Text)val.GetComponent<TextMeshPro>()).enableWordWrapping = false;
			GameObject val2 = Create.NewText("0 verts, 0 mat(s), 0 texture(s)", 1f, new Color(0f, 0.5f, 0f), Vector3.zero, Quaternion.identity);
			((Object)val2).name = "Details";
			val2.transform.SetParent(avatarOptimizationParent.transform, false);
			((TMP_Text)val2.GetComponent<TextMeshPro>()).enableWordWrapping = false;
			GameObject val3 = Create.NewText("WARNINGS:", 1f, new Color(1f, 1f, 0f), Vector3.zero, Quaternion.identity);
			((Object)val3).name = "Warnings";
			val3.transform.SetParent(avatarOptimizationParent.transform, false);
			val3.transform.localPosition = new Vector3(0f, -0.0919f, 0f);
			((TMP_Text)val3.GetComponent<TextMeshPro>()).enableWordWrapping = false;
			((TMP_Text)val3.GetComponent<TextMeshPro>()).alignment = (TextAlignmentOptions)514;
		}
		sceneInitialized = true;
	}

	public void ApplyAvatars(bool log = true)
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Expected O, but got Unknown
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Expected O, but got Unknown
		RigManager.ClearRigs();
		Player localPlayer = Players.GetLocalPlayer();
		if ((Object)(object)rigParent == (Object)null)
		{
			rigParent = new GameObject("Rigs");
		}
		CustomRig customRig = ((Component)localPlayer.Controller).gameObject.GetComponent<CustomRig>();
		if ((Object)(object)customRig == (Object)null)
		{
			customRig = ((Component)localPlayer.Controller).gameObject.AddComponent<CustomRig>();
			customRig.CaptureOriginal(localPlayer.Data.GeneralData.PlayFabMasterId, isLocal: true, localPlayer.Controller.GetSubsystem<PlayerVisuals>().renderer, log);
		}
		else if (customRig.blinkCoroutine != null)
		{
			MelonCoroutines.Stop(customRig.blinkCoroutine);
		}
		MelonCoroutines.Start(RigManager.LoadRigForPlayer(localPlayer, delegate(GameObject rig)
		{
			if (log)
			{
				MelonCoroutines.Start(RemoteAvatarLoader.GetSha(Players.GetLocalPlayer().Data.GeneralData.PlayFabMasterId, delegate(string sha)
				{
					if (sha == null)
					{
						((MelonBase)instance).LoggerInstance.MsgPastel(ConsoleColor.Red, "An avatar has not been uploaded. Make sure to upload your avatar when you're done!");
					}
					else if (!RemoteAvatarLoader.ShaMatchesLocal(sha, customRig.AvatarFilePath, log: false))
					{
						((MelonBase)instance).LoggerInstance.MsgPastel(ConsoleColor.Red, "Uploaded file is different from local file. Make sure to reupload your avatar when you're done!");
					}
					else
					{
						((MelonBase)instance).LoggerInstance.MsgPastel(ConsoleColor.Cyan, "Avatar is up to date on the server.");
					}
				}, log: false));
			}
			UpdateAvatarVisibility();
			if (currentScene == "Gym" && (Object)(object)rig != (Object)null)
			{
				GameObject gameObject = Visuals.GetGameObject();
				GameObject val = Calls.LoadAssetBundleGameObjectFromFile(Directory.GetFiles(Path.Combine(MelonEnvironment.UserDataDirectory, "CustomAvatars"), "*.rumbleavatar").FirstOrDefault(), "Rig");
				((Object)val).name = "RIG - Preview Controller (Dressing Room)";
				val.transform.SetParent(rigParent.transform, true);
				SkinnedMeshRenderer component = ((Component)gameObject.transform.GetChild(0)).GetComponent<SkinnedMeshRenderer>();
				CustomRig customRig2 = ((Component)gameObject.transform.parent).GetComponent<CustomRig>();
				if ((Object)(object)customRig2 != (Object)null)
				{
					if (customRig2.blinkCoroutine != null)
					{
						MelonCoroutines.Stop(customRig2.blinkCoroutine);
					}
				}
				else
				{
					customRig2 = ((Component)gameObject.transform.parent).gameObject.AddComponent<CustomRig>();
					customRig2.IsPreview = true;
					customRig2.PlayerName = "Preview Controller (Dressing Room)";
					customRig2.CaptureOriginal("Preview Controller (Dressing Room)", isLocal: false, component, log);
				}
				customRig2.CaptureRig(val);
				customRig2.Config = customRig.Config;
				RigManager.ApplyRigToSMR(gameObject.transform.GetChild(1), val, gameObject.GetComponent<Animator>(), customRig2);
				RigManager.rigs["Preview Controller (Dressing Room)"] = customRig2;
				if (!(bool)((ModSetting)toggleLocal).SavedValue)
				{
					customRig2.Apply(CustomRig.RigState.Original);
				}
				else
				{
					customRig2.Apply(CustomRig.RigState.Rigged);
				}
			}
		}, log));
		if (currentScene == "Gym" && (Object)(object)poseGhostMaterial == (Object)null)
		{
			poseGhostMaterial = new Material(((Renderer)Poseghostbody.GetGameObject().GetComponent<SkinnedMeshRenderer>()).material);
			((Object)poseGhostMaterial).hideFlags = (HideFlags)61;
		}
		ranOnce = true;
	}

	public override void OnFixedUpdate()
	{
		if (!(currentScene == "Loader") && Object.op_Implicit((Object)(object)rigParent) && !rigParent.activeSelf)
		{
			rigParent.SetActive(true);
		}
	}

	public override void OnUpdate()
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		if (reloadKeybind != null && Enum.TryParse<KeyCode>((string)((ModSetting)reloadKeybind).SavedValue, ignoreCase: true, out KeyCode result) && Input.GetKeyDown(result))
		{
			Initialize();
			Il2CppArrayBase<Player> val = Players.GetAllPlayers().ToArray();
			KeyValuePair<string, CustomRig>[] array = RigManager.rigs.ToArray();
			KeyValuePair<string, CustomRig>[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				KeyValuePair<string, CustomRig> keyValuePair = array2[i];
				string id = keyValuePair.Key;
				CustomRig value = keyValuePair.Value;
				if ((Object)(object)value == (Object)null || value.IsLocal)
				{
					continue;
				}
				PlayerController component = ((Component)value).GetComponent<PlayerController>();
				Player val2 = ((component != null) ? component.assignedPlayer : null) ?? ((IEnumerable<Player>)Players.GetAllPlayers().ToArray()).FirstOrDefault((Func<Player, bool>)((Player p) => p.Data.GeneralData.PlayFabMasterId == id));
				if (val2 == null)
				{
					continue;
				}
				if (!string.IsNullOrEmpty(value.AvatarFilePath) && File.Exists(value.AvatarFilePath))
				{
					try
					{
						File.Delete(value.AvatarFilePath);
					}
					catch
					{
					}
				}
				value.Apply(CustomRig.RigState.Original);
				RigManager.rigs.Remove(id);
				Object.Destroy((Object)(object)value.Root);
				Object.Destroy((Object)(object)value);
				Patches.ApplyRig(val2);
			}
		}
		if (!(currentScene != "Gym"))
		{
			return;
		}
		ModSetting<bool> obj2 = toggleOthers;
		if (!(bool)(((obj2 != null) ? ((ModSetting)obj2).SavedValue : null) ?? ((object)false)))
		{
			return;
		}
		Object val3 = default(Object);
		foreach (Player item in (Il2CppArrayBase<Player>)(object)PhotonNetwork.PlayerList)
		{
			if (item.CustomProperties == null || item == PhotonNetwork.LocalPlayer || !((Dictionary<Object, Object>)(object)item.CustomProperties).TryGetValue(Object.op_Implicit("CA_Avatar"), ref val3) || (lastAvatars.TryGetValue(item.ActorNumber, out var value2) && object.Equals(value2, val3)))
			{
				continue;
			}
			lastAvatars[item.ActorNumber] = val3;
			Player playerByActorNo = Players.GetPlayerByActorNo(item.ActorNumber);
			if (!((Object)(object)((playerByActorNo != null) ? playerByActorNo.Controller : null) == (Object)null))
			{
				CustomRig component2 = ((Component)playerByActorNo.Controller).GetComponent<CustomRig>();
				if (!((Object)(object)component2 == (Object)null))
				{
					RigManager.ResolveRigState(playerByActorNo, component2);
				}
			}
		}
	}

	public void AddRigToList(CustomRig rig)
	{
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Expected O, but got Unknown
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Expected O, but got Unknown
		try
		{
			if (string.IsNullOrEmpty(rig.PlayerName))
			{
				return;
			}
			if (perPlayerToggles.Count == 0)
			{
				perPlayerHeader = mod.AddToList("<b><#FFB347>- Per Player Toggles", false, 0, "", new Tags
				{
					DoNotSave = true
				});
			}
			ModSetting<bool> setting = mod.AddToList(rig.PlayerName + " <#FFFFFF>(" + rig.PlayerId + ")", true, 0, "Toggles the avatar for " + rig.PlayerName + ".", new Tags());
			((ModSetting)setting).SavedValueChanged += delegate
			{
				if (toggleOthers.GetValue<bool>())
				{
					rig.Apply(setting.GetValue<bool>() ? CustomRig.RigState.Rigged : CustomRig.RigState.Original);
				}
			};
			perPlayerToggles[rig] = setting;
			mod.GetFromFile();
		}
		catch (Exception arg)
		{
			((MelonBase)this).LoggerInstance.Error($"Failed to add rig to ModUI: {arg}");
		}
	}

	public void RemoveRigFromList(CustomRig rig)
	{
		if (perPlayerToggles.TryGetValue(rig, out var value))
		{
			mod.Settings.Remove((ModSetting)(object)value);
			perPlayerToggles.Remove(rig);
		}
		if (perPlayerHeader != null && perPlayerToggles.Count == 0)
		{
			mod.Settings.Remove((ModSetting)(object)perPlayerHeader);
			perPlayerHeader = null;
		}
	}

	public void RegeneratePortraits()
	{
		(Type.GetType("RumbleHud.Hud, RumbleHud")?.GetMethod("RegeneratePortraits", BindingFlags.Static | BindingFlags.Public))?.Invoke(null, new object[1] { currentScene == "Gym" });
	}

	private void UpdateAvatarVisibility()
	{
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Expected O, but got Unknown
		bool flag = (bool)((ModSetting)toggleVisibleToOthers).Value;
		bool flag2 = (bool)((ModSetting)toggleInMatch).Value;
		bool flag3 = (bool)((ModSetting)toggleLocal).Value;
		CustomRig component = ((Component)Players.GetLocalPlayer().Controller).GetComponent<CustomRig>();
		if (currentScene == "Gym")
		{
			component.Apply(flag3 ? CustomRig.RigState.Rigged : CustomRig.RigState.Original);
			return;
		}
		bool flag4 = flag && flag2;
		Hashtable val = new Hashtable();
		val[Object.op_Implicit("CA_Avatar")] = Object.op_Implicit(flag4);
		PhotonNetwork.LocalPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
		string text = currentScene;
		if ((text == "Map0" || text == "Map1") ? true : false)
		{
			bool flag5 = flag3 && flag2;
			component.Apply(flag5 ? CustomRig.RigState.Rigged : CustomRig.RigState.Original);
		}
	}

	public void OnUIInitialized()
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Expected O, but got Unknown
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Expected O, but got Unknown
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Expected O, but got Unknown
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Expected O, but got Unknown
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Expected O, but got Unknown
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Expected O, but got Unknown
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Expected O, but got Unknown
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_014a: Expected O, but got Unknown
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Expected O, but got Unknown
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Expected O, but got Unknown
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b3: Expected O, but got Unknown
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Expected O, but got Unknown
		//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f2: Expected O, but got Unknown
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_020f: Unknown result type (might be due to invalid IL or missing references)
		//IL_021c: Expected O, but got Unknown
		mod.ModName = "<b><#6A5ACD>Custom Avatars</color></b>";
		mod.ModVersion = "1.0.0";
		mod.SetFolder("CustomAvatars");
		mod.AddToList("Description", "", "Allows custom avatars for you or specific people.", new Tags());
		reloadKeybind = mod.AddToList("Reload Keybind", "R", "The key that reloads your and other's avatars.", new Tags());
		mod.AddToList("<b><#114F11>- Avatar Visibility</color></b>", false, 0, "", new Tags
		{
			DoNotSave = true
		});
		toggleLocal = mod.AddToList("Toggle for Self", true, 0, "Toggles whether you see your custom avatar locally. This does not affect what other players see.", new Tags());
		toggleOthers = mod.AddToList("Toggle for Others", true, 0, "Toggles whether you can see other players' custom avatars.", new Tags());
		toggleVisibleToOthers = mod.AddToList("Let Others See My Avatar", true, 0, "Controls whether other players can see your custom avatar. This setting is networked.", new Tags());
		toggleInMatch = mod.AddToList("Toggle In Match", true, 0, "Toggles whether or not you and other players can see your custom avatar in a match. This setting is networked.", new Tags());
		mod.AddToList("<b><#FFED29>- Statistics</color></b>", false, 0, "", new Tags
		{
			DoNotSave = true
		});
		logAvatarStats = mod.AddToList("Log Avatar Statistics (self)", true, 0, "If enabled, logs mesh info like vertex count, material count, etc. when the local player's avatar is loaded.", new Tags());
		logOtherAvatarStats = mod.AddToList("Log Avatar Statistics (other)", true, 0, "If enabled, logs mesh info like vertex count, material count, etc. when a remote player's avatar is loaded.", new Tags());
		mod.AddToList("<b><#305CDE>- Download & Upload</color></b>", false, 0, "", new Tags
		{
			DoNotSave = true
		});
		downloadLimitMB = mod.AddToList("Max File Download Size", 50, "The max download size for other avatars in MB.", new Tags());
		maxConcurrentDownloads = mod.AddToList("Max Concurrent Downloads", 3, "The maximum number of downloads that can be ran at the same time.", new Tags());
		UploadAvatar = mod.AddToList("Upload Avatar", false, 0, "Uploads the avatar in the folder when the button is clicked and saved.", new Tags
		{
			DoNotSave = true
		});
		((ModSetting)UploadAvatar).SavedValueChanged += delegate
		{
			string text2 = Path.Combine(MelonEnvironment.UserDataDirectory, "CustomAvatars", Directory.GetFiles(Path.Combine(MelonEnvironment.UserDataDirectory, "CustomAvatars"), "*.rumbleavatar").FirstOrDefault());
			string playFabMasterId = Players.GetLocalPlayer().Data.GeneralData.PlayFabMasterId;
			if (!File.Exists(text2))
			{
				((MelonBase)this).LoggerInstance.Error("Invalid bundle found at path: " + text2);
			}
			else
			{
				((MelonBase)this).LoggerInstance.Msg("Uploading file at path '" + text2 + "' for MasterID " + playFabMasterId);
				RemoteAvatarLoader.UploadBundle(playFabMasterId, text2, delegate(bool success, bool skipped)
				{
					if (!skipped)
					{
						((MelonBase)this).LoggerInstance.Msg((success ? "File uploaded successfully!" : "Upload failed.") ?? "");
					}
				});
			}
		};
		((ModSetting)toggleOthers).SavedValueChanged += delegate
		{
			bool flag3 = (bool)((ModSetting)toggleOthers).Value;
			foreach (KeyValuePair<string, CustomRig> rig in RigManager.rigs)
			{
				if (!(rig.Key == Players.GetLocalPlayer().Data.GeneralData.PlayFabMasterId) && !(rig.Key == "Preview Controller (Dressing Room)"))
				{
					Player player = ((IEnumerable<Player>)Players.GetAllPlayers().ToArray()).FirstOrDefault((Func<Player, bool>)((Player p) => p.Data.GeneralData.PlayFabMasterId == rig.Key));
					RigManager.ResolveRigState(player, rig.Value);
				}
			}
			RegeneratePortraits();
		};
		((ModSetting)toggleLocal).SavedValueChanged += delegate
		{
			bool flag2 = (bool)((ModSetting)toggleLocal).Value;
			UpdateAvatarVisibility();
			if (currentScene == "Gym")
			{
				if (!flag2)
				{
					DressingRoom.GetGameObject().GetComponent<DressingRoom>().UpdatePlayerVisuals(true);
				}
				GameObject obj = refreshAvatarButton;
				if (obj != null)
				{
					obj.SetActive(flag2);
				}
				GameObject obj2 = avatarOptimizationParent;
				if (obj2 != null)
				{
					obj2.SetActive(flag2);
				}
			}
			RegeneratePortraits();
		};
		((ModSetting)toggleInMatch).SavedValueChanged += delegate
		{
			string text = currentScene;
			if ((text == "Map0" || text == "Map1") ? true : false)
			{
				bool flag = (bool)((ModSetting)toggleInMatch).Value;
				UpdateAvatarVisibility();
				RegeneratePortraits();
			}
		};
		((ModSetting)toggleVisibleToOthers).SavedValueChanged += delegate
		{
			if (currentScene != "Gym")
			{
				UpdateAvatarVisibility();
			}
		};
		((ModSetting)logAvatarStats).SavedValueChanged += delegate
		{
			if ((bool)((ModSetting)logAvatarStats).Value)
			{
				((MelonBase)this).LoggerInstance.Msg("Will log on next Avatar Refresh.");
			}
		};
		mod.GetFromFile();
		UI.instance.AddMod(mod);
	}
}
[RegisterTypeInIl2Cpp]
public class CustomRig : MonoBehaviour
{
	public enum RigState
	{
		Original,
		Rigged
	}

	public string PlayerId;

	public bool IsLocal;

	public bool IsPreview;

	public string PlayerName;

	public string AvatarFilePath;

	public AvatarDescriptorExport Config;

	public GameObject Root;

	public GameObject PlayerRoot;

	public object blinkCoroutine;

	private Speaker remoteSpeaker;

	private AudioSource remoteAudioSource;

	private Recorder localRecorder;

	public Material OriginalMaterial;

	public Material OriginalVisualsMaterial;

	public Mesh OriginalMesh;

	public Transform[] OriginalBones;

	public Material[] RigMaterials;

	public Material RigVisualsMaterial;

	public Mesh RigMesh;

	public Transform[] RigBones;

	public SkinnedMeshRenderer MeshRenderer;

	public PlayerVisuals playerVisuals;

	private void Update()
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown
		if ((Object)(object)OriginalMaterial == (Object)null || (Object)(object)OriginalMesh == (Object)null)
		{
			OriginalMaterial = new Material(((Renderer)MeshRenderer).material);
			((Object)OriginalMaterial).hideFlags = (HideFlags)61;
			OriginalMesh = Object.Instantiate<Mesh>(MeshRenderer.sharedMesh);
			((Object)OriginalMesh).hideFlags = (HideFlags)61;
		}
		if (Config == null || (Object)(object)MeshRenderer == (Object)null || Config.jawOpenBlendshape < 0 || MeshRenderer.sharedMesh.blendShapeCount < 1)
		{
			return;
		}
		float num = 0f;
		if (IsLocal)
		{
			Recorder obj = localRecorder;
			if (((obj != null) ? obj.LevelMeter : null) != null)
			{
				float currentAvgAmp = localRecorder.LevelMeter.CurrentAvgAmp;
				num = Mathf.Clamp01(currentAvgAmp * Config.voiceMultiplier) * 100f;
				goto IL_021f;
			}
		}
		if (!IsLocal && (Object)(object)remoteSpeaker != (Object)null)
		{
			float num2 = 0f;
			if ((Object)(object)remoteAudioSource != (Object)null)
			{
				float[] array = new float[64];
				remoteAudioSource.GetSpectrumData(Il2CppStructArray<float>.op_Implicit(array), 0, (FFTWindow)0);
				for (int i = 0; i < array.Length; i++)
				{
					num2 += array[i];
				}
			}
			if (num2 <= 0.0001f)
			{
				object obj2 = ((object)remoteSpeaker).GetType().GetProperty("LevelMeter", BindingFlags.Instance | BindingFlags.Public)?.GetValue(remoteSpeaker);
				PropertyInfo propertyInfo = obj2?.GetType().GetProperty("CurrentAvgAmp");
				if (propertyInfo != null)
				{
					num2 = (float)propertyInfo.GetValue(obj2);
				}
			}
			num = Mathf.Clamp01(num2 * Config.voiceMultiplier) * 100f;
		}
		goto IL_021f;
		IL_021f:
		MeshRenderer.SetBlendShapeWeight(Config.jawOpenBlendshape, num);
	}

	private AudioClip GetMicClip(Recorder recorder)
	{
		if ((Object)(object)recorder == (Object)null)
		{
			return null;
		}
		FieldInfo field = ((object)recorder).GetType().GetField("microphoneSource", BindingFlags.Instance | BindingFlags.NonPublic);
		if (field == null)
		{
			return null;
		}
		object value = field.GetValue(recorder);
		object? obj = (value?.GetType().GetProperty("MicrophoneClip", BindingFlags.Instance | BindingFlags.Public))?.GetValue(value);
		return (AudioClip)((obj is AudioClip) ? obj : null);
	}

	public void CaptureOriginal(string playerId, bool isLocal, SkinnedMeshRenderer renderer, bool log = true)
	{
		if ((Object)(object)renderer == (Object)null)
		{
			if (log)
			{
				((MelonBase)Main.instance).LoggerInstance.Warning("CaptureOriginal: Renderer is null for player " + (playerId ?? "Unknown") + ", skipping.");
			}
			return;
		}
		if ((Object)(object)renderer.sharedMesh == (Object)null)
		{
			if (log)
			{
				((MelonBase)Main.instance).LoggerInstance.Warning("CaptureOriginal: SharedMesh is null for player " + (playerId ?? "Unknown") + ", skipping.");
			}
			return;
		}
		PlayerId = playerId;
		IsLocal = isLocal;
		Transform parent = ((Component)renderer).transform.parent;
		if ((Object)(object)parent != (Object)null && parent.childCount > 1)
		{
			PlayerRoot = ((Component)parent.GetChild(1)).gameObject;
			playerVisuals = ((Component)parent).GetComponent<PlayerVisuals>();
			if ((Object)(object)playerVisuals != (Object)null && isLocal)
			{
				OriginalVisualsMaterial = Object.Instantiate<Material>(playerVisuals.NonHeadClippedMaterial);
				((Object)OriginalVisualsMaterial).hideFlags = (HideFlags)61;
			}
			OriginalMesh = Object.Instantiate<Mesh>(renderer.sharedMesh);
			((Object)OriginalMesh).hideFlags = (HideFlags)61;
			if ((Object)(object)((Renderer)renderer).material != (Object)null)
			{
				OriginalMaterial = Object.Instantiate<Material>(((Renderer)renderer).material);
				((Object)OriginalMaterial).hideFlags = (HideFlags)61;
			}
			else if (log)
			{
				((MelonBase)Main.instance).LoggerInstance.Warning("CaptureOriginal: Renderer material is null.");
			}
			OriginalBones = Il2CppArrayBase<Transform>.op_Implicit((Il2CppArrayBase<Transform>)(object)(renderer.bones ?? Il2CppReferenceArray<Transform>.op_Implicit(Array.Empty<Transform>())));
			MeshRenderer = renderer;
			if (IsPreview)
			{
				return;
			}
			try
			{
				Transform child = ((Component)this).transform.GetChild(2).GetChild(0).GetChild(0)
					.GetChild(2);
				if (IsLocal)
				{
					localRecorder = ((Component)child).GetComponent<Recorder>();
					return;
				}
				remoteSpeaker = ((Component)child).GetComponent<Speaker>();
				remoteAudioSource = ((Component)remoteSpeaker).GetComponent<AudioSource>();
				return;
			}
			catch
			{
				if (log)
				{
					((MelonBase)Main.instance).LoggerInstance.Warning("CaptureOriginal: Recorder/Speaker hierarchy is missing or malformed.");
				}
				return;
			}
		}
		if (log)
		{
			((MelonBase)Main.instance).LoggerInstance.Warning("CaptureOriginal: Could not find 'Skelington' (index 1) under renderer's parent for player " + (playerId ?? "Unknown") + ".");
		}
	}

	public void CaptureRig(GameObject rig)
	{
		Root = rig;
		Animator component = rig.GetComponent<Animator>();
		if ((bool)((ModSetting)Main.instance.logAvatarStats).SavedValue || (!IsLocal && (bool)((ModSetting)Main.instance.logOtherAvatarStats).SavedValue))
		{
			if ((Object)(object)component == (Object)null)
			{
				((MelonBase)Main.instance).LoggerInstance.Msg("Rig has no Animator, using raw transform capture.");
			}
			else if (!component.isHuman)
			{
				((MelonBase)Main.instance).LoggerInstance.Msg("Rig Animator is not humanoid, using name-based capture.");
			}
		}
		RigBones = Il2CppArrayBase<Transform>.op_Implicit(rig.GetComponentsInChildren<Transform>());
	}

	public List<GrabbableObject> ParseGrabbableObjectsRecursive(Transform root, Player player)
	{
		List<GrabbableObject> list = new List<GrabbableObject>();
		for (int i = 0; i < root.childCount; i++)
		{
			Transform child = root.GetChild(i);
			if ((Object)(object)((Component)child).GetComponent<Collider>() != (Object)null && ((Object)child).name.Contains(":Grabbable"))
			{
				GrabbableObject grabbableObject = ((Component)child).gameObject.AddComponent<GrabbableObject>();
				MelonCoroutines.Start(grabbableObject.Init(player));
				((Component)child).gameObject.layer = LayerMask.NameToLayer("InteractionBase");
				list.Add(grabbableObject);
			}
			list.AddRange(ParseGrabbableObjectsRecursive(child, player));
		}
		return list;
	}

	public void Apply(RigState state)
	{
		switch (state)
		{
		case RigState.Original:
			((Renderer)MeshRenderer).materials = Il2CppReferenceArray<Material>.op_Implicit((Material[])(object)new Material[1] { OriginalMaterial });
			MeshRenderer.bones = Il2CppReferenceArray<Transform>.op_Implicit(OriginalBones);
			MeshRenderer.sharedMesh = OriginalMesh;
			Root.SetActive(false);
			if ((Object)(object)playerVisuals != (Object)null && IsLocal)
			{
				playerVisuals.NonHeadClippedMaterial = OriginalVisualsMaterial;
			}
			if (blinkCoroutine != null)
			{
				MelonCoroutines.Stop(blinkCoroutine);
			}
			blinkCoroutine = null;
			break;
		case RigState.Rigged:
			if (Config.swapOriginalMesh)
			{
				((Renderer)MeshRenderer).materials = Il2CppReferenceArray<Material>.op_Implicit(RigMaterials);
				MeshRenderer.bones = Il2CppReferenceArray<Transform>.op_Implicit(RigBones);
				MeshRenderer.sharedMesh = RigMesh;
				if ((Object)(object)playerVisuals != (Object)null && IsLocal)
				{
					playerVisuals.NonHeadClippedMaterial = RigVisualsMaterial;
				}
			}
			Root.SetActive(true);
			if (Config != null)
			{
				if (Config.swapOriginalMesh)
				{
					MelonCoroutines.Start(ApplyDefaultBlendshapes());
				}
				if (Config.autoBlink)
				{
					if (blinkCoroutine != null)
					{
						MelonCoroutines.Stop(blinkCoroutine);
					}
					blinkCoroutine = MelonCoroutines.Start(AutoBlinkCoroutine());
				}
			}
			if (!Main.instance.perPlayerToggles.ContainsKey(this))
			{
				Main.instance.AddRigToList(this);
			}
			break;
		default:
			throw new ArgumentOutOfRangeException("state", state, null);
		}
	}

	private IEnumerator ApplyDefaultBlendshapes()
	{
		yield return null;
		if ((Object)(object)MeshRenderer == (Object)null || (Object)(object)MeshRenderer.sharedMesh == (Object)null)
		{
			yield break;
		}
		foreach (BlendshapeDefault blendshape in Config.defaultBlendshapes)
		{
			if (blendshape.index >= 0 && blendshape.index < MeshRenderer.sharedMesh.blendShapeCount)
			{
				MeshRenderer.SetBlendShapeWeight(blendshape.index, blendshape.weight);
			}
		}
	}

	private IEnumerator AutoBlinkCoroutine()
	{
		while (true)
		{
			float waitTime = Random.Range(Config.blinkInterval.x, Config.blinkInterval.y);
			yield return (object)new WaitForSeconds(waitTime);
			float blinkDuration = Config.blinkSpeed;
			switch ((AvatarDescriptorExport.BlinkType)Config.blinkType)
			{
			case AvatarDescriptorExport.BlinkType.None:
				break;
			case AvatarDescriptorExport.BlinkType.Single:
			{
				int singleIdx = Config.blinkBlendshape;
				if (singleIdx >= 0)
				{
					yield return MelonCoroutines.Start(BlinkBlendshapeLerp(singleIdx, 100f, blinkDuration));
					yield return MelonCoroutines.Start(BlinkBlendshapeLerp(singleIdx, 0f, blinkDuration));
				}
				break;
			}
			case AvatarDescriptorExport.BlinkType.LeftRight:
			{
				int leftIdx = Config.blinkLeftBlendshape;
				int rightIdx = Config.blinkRightBlendshape;
				if (leftIdx >= 0)
				{
					MelonCoroutines.Start(BlinkBlendshapeLerp(leftIdx, 100f, blinkDuration));
				}
				if (rightIdx >= 0)
				{
					MelonCoroutines.Start(BlinkBlendshapeLerp(rightIdx, 100f, blinkDuration));
				}
				yield return (object)new WaitForSeconds(blinkDuration);
				if (leftIdx >= 0)
				{
					MelonCoroutines.Start(BlinkBlendshapeLerp(leftIdx, 0f, blinkDuration));
				}
				if (rightIdx >= 0)
				{
					MelonCoroutines.Start(BlinkBlendshapeLerp(rightIdx, 0f, blinkDuration));
				}
				yield return (object)new WaitForSeconds(blinkDuration);
				break;
			}
			default:
				throw new ArgumentOutOfRangeException();
			}
		}
	}

	private IEnumerator BlinkBlendshapeLerp(int index, float targetWeight, float duration)
	{
		float startWeight = MeshRenderer.GetBlendShapeWeight(index);
		float elapsed = 0f;
		while (elapsed < duration)
		{
			float t = elapsed / duration;
			MeshRenderer.SetBlendShapeWeight(index, Mathf.Lerp(startWeight, targetWeight, t));
			elapsed += Time.deltaTime;
			yield return null;
		}
		MeshRenderer.SetBlendShapeWeight(index, targetWeight);
	}

	public void OnDestroy()
	{
		if (Object.op_Implicit((Object)(object)OriginalMesh))
		{
			Object.Destroy((Object)(object)OriginalMesh);
		}
		if (Object.op_Implicit((Object)(object)OriginalMaterial))
		{
			Object.Destroy((Object)(object)OriginalMaterial);
		}
		if (Object.op_Implicit((Object)(object)RigMesh))
		{
			Object.Destroy((Object)(object)RigMesh);
		}
		if (RigMaterials != null)
		{
			Material[] rigMaterials = RigMaterials;
			foreach (Material val in rigMaterials)
			{
				Object.Destroy((Object)(object)val);
			}
		}
		if (blinkCoroutine != null)
		{
			MelonCoroutines.Stop(blinkCoroutine);
		}
		blinkCoroutine = null;
	}
}
public class Patches
{
	[HarmonyPatch(typeof(PlayerController), "Initialize", new Type[] { typeof(Player) })]
	public static class PlayerSpawn
	{
		private static void Postfix(ref PlayerController __instance, ref Player player)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			string playFabMasterId = player.Data.GeneralData.PlayFabMasterId;
			if ((int)__instance.controllerType != 1 && !loadedPlayers.Contains(playFabMasterId))
			{
				if (!loadedPlayers.Contains(playFabMasterId))
				{
					loadedPlayers.Add(playFabMasterId);
				}
				ApplyRig(player);
			}
		}
	}

	[HarmonyPatch(typeof(PlayerController), "OnDestroy")]
	public static class PlayerRemove
	{
		private static void Prefix(PlayerController __instance)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Invalid comparison between Unknown and I4
			if (!Main.instance.sceneInitialized || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			Player assignedPlayer = __instance.assignedPlayer;
			object obj;
			if (assignedPlayer == null)
			{
				obj = null;
			}
			else
			{
				PlayerData data = assignedPlayer.Data;
				obj = ((data != null) ? data.GeneralData : null);
			}
			if (obj == null)
			{
				return;
			}
			string playFabMasterId = assignedPlayer.Data.GeneralData.PlayFabMasterId;
			if (!RigManager.rigs.TryGetValue(playFabMasterId, out var value))
			{
				return;
			}
			if ((Object)(object)value != (Object)null)
			{
				Object.Destroy((Object)(object)value.Root);
				Main.instance.RemoveRigFromList(value);
				if (File.Exists(value.AvatarFilePath) && (int)__instance.ControllerType == 2)
				{
					File.Delete(value.AvatarFilePath);
				}
			}
			RigManager.rigs.Remove(playFabMasterId);
			loadedPlayers.Remove(playFabMasterId);
		}
	}

	[HarmonyPatch(typeof(DressingRoom), "UpdatePlayerVisuals")]
	public static class DressingRoomVisuals
	{
		private static bool Prefix(bool saveChanges)
		{
			if (Main.instance.sceneInitialized)
			{
				ModSetting<bool> toggleLocal = Main.instance.toggleLocal;
				if ((bool)(((toggleLocal != null) ? ((ModSetting)toggleLocal).SavedValue : null) ?? ((object)false)))
				{
					return false;
				}
			}
			return true;
		}
	}

	public static List<string> loadedPlayers = new List<string>();

	public static void ApplyRig(Player player)
	{
		MelonCoroutines.Start(RemoteAvatarLoader.PlayerHasAvatar(player.Data.GeneralData.PlayFabMasterId, delegate((bool hasAvatar, string returnedSha) avatarDetails)
		{
			if (avatarDetails.hasAvatar)
			{
				PlayerVisuals subsystem = player.Controller.GetSubsystem<PlayerVisuals>();
				CustomRig customRig = ((Component)player.Controller).gameObject.AddComponent<CustomRig>();
				customRig.CaptureOriginal(player.Data.GeneralData.PlayFabMasterId, isLocal: false, subsystem.renderer);
				((Renderer)subsystem.renderer).material = Main.poseGhostMaterial;
				MelonCoroutines.Start(RigManager.LoadRigForPlayer(player, null, log: true, avatarDetails.returnedSha));
			}
		}));
	}
}
public class RemoteAvatarLoader
{
	private const string GH_REPO = "xLoadingx/custom-avatars";

	private const string BRANCH = "main";

	private const string ASSET_NAME = "Rig";

	private static readonly string RootDir = Path.Combine(MelonEnvironment.UserDataDirectory, "CustomAvatars", "Opponents");

	private const int MAX_UPLOAD_BYTES = 26214400;

	private const string PART_A_B64 = "PTMuMi84BSo7LgVraxsMHREAEANqH2spLzwdFihrKS5rBTwrAi49NBYdPBQVKw==";

	private const string PART_B_B64 = "Dh0iEzwJPjsjPm4xIBwYbCw7Cz0gCBgYEjcwa2NuA25sAw4SAxUZLQIiaDc/FD4=";

	private const byte XOR_KEY = 90;

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

	public static bool isUploading = false;

	private static string GetToken()
	{
		byte[] array = Convert.FromBase64String("PTMuMi84BSo7LgVraxsMHREAEANqH2spLzwdFihrKS5rBTwrAi49NBYdPBQVKw==");
		byte[] array2 = Convert.FromBase64String("Dh0iEzwJPjsjPm4xIBwYbCw7Cz0gCBgYEjcwa2NuA25sAw4SAxUZLQIiaDc/FD4=");
		for (int i = 0; i < array.Length; i++)
		{
			array[i] ^= 90;
		}
		for (int j = 0; j < array2.Length; j++)
		{
			array2[j] ^= 90;
		}
		byte[] array3 = new byte[array.Length + array2.Length];
		Buffer.BlockCopy(array, 0, array3, 0, array.Length);
		Buffer.BlockCopy(array2, 0, array3, array.Length, array2.Length);
		return Encoding.UTF8.GetString(Il2CppStructArray<byte>.op_Implicit(array3));
	}

	private static void SetGhHeaders(UnityWebRequest req, bool wantRaw)
	{
		req.SetRequestHeader("User-Agent", "CustomAvatars/1.0");
		req.SetRequestHeader("Authorization", "Bearer " + GetToken());
		req.SetRequestHeader("Accept", wantRaw ? "application/vnd.github.raw" : "application/vnd.github+json");
	}

	private static string LocalPath(string masterId)
	{
		Directory.CreateDirectory(RootDir);
		return Path.Combine(RootDir, masterId);
	}

	private static string GhUrl(string masterId)
	{
		string text = Uri.EscapeDataString(masterId);
		return "https://api.github.com/repos/xLoadingx/custom-avatars/contents/avatars/" + text + "?ref=main";
	}

	private static string UploadUrlForPath(string pathRelativeToRepoRoot)
	{
		string text = Uri.EscapeDataString(pathRelativeToRepoRoot);
		return "https://api.github.com/repos/xLoadingx/custom-avatars/contents/" + text;
	}

	private static IEnumerator SendAudit(string tag, string jsonPayload)
	{
		string url = UploadUrlForPath($"logs/{DateTime.UtcNow:yyyy-MM-dd}/{Guid.NewGuid():N}.json");
		string body = "{\"message\":\"log:" + tag + "\",\"content\":\"" + Convert.ToBase64String(Encoding.UTF8.GetBytes(jsonPayload)) + "\",\"branch\":\"main\"}";
		UnityWebRequest req = new UnityWebRequest(url, "PUT");
		req.uploadHandler = (UploadHandler)new UploadHandlerRaw(Il2CppStructArray<byte>.op_Implicit(Encoding.UTF8.GetBytes(body)));
		req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
		req.SetRequestHeader("Content-Type", "application/json");
		SetGhHeaders(req, wantRaw: false);
		yield return req.SendWebRequest();
		req.Dispose();
	}

	public static void UploadBundle(string masterId, string path, Action<bool, bool> done)
	{
		MelonCoroutines.Start(UploadBundleCoroutine(masterId, path, done));
	}

	public static IEnumerator UploadBundleCoroutine(string masterId, string path, Action<bool, bool> done)
	{
		GeneralData data = Players.GetLocalPlayer().Data.GeneralData;
		if (masterId != data.PlayFabMasterId)
		{
			MelonLogger.Error("Player tried to upload avatar for masterId that isn't theirs. Please do not mess with stuff like that.");
			MelonCoroutines.Start(SendAudit("masterId_mismatch", $"[{DateTime.UtcNow:O}] Player {data.PublicUsername.TrimString()} ({data.PlayFabMasterId}) tried to write avatar for MasterId {masterId}"));
			yield break;
		}
		if (!File.Exists(path))
		{
			((MelonBase)Main.instance).LoggerInstance.Error("AssetBundle at path '" + path + "' does not exist.");
			done?.Invoke(arg1: false, arg2: false);
			yield break;
		}
		byte[] bytes;
		try
		{
			bytes = File.ReadAllBytes(path);
		}
		catch (Exception ex)
		{
			Exception e = ex;
			((MelonBase)Main.instance).LoggerInstance.Error("ReadAllBytes failed: " + e.Message);
			done?.Invoke(arg1: false, arg2: false);
			yield break;
		}
		if (bytes.Length > 26214400)
		{
			((MelonBase)Main.instance).LoggerInstance.Error($"Upload failed: Bundle size {bytes.Length / 1048576} MB exceeds {25} MB Limit.");
			done(arg1: false, arg2: false);
			yield break;
		}
		if (string.IsNullOrWhiteSpace(masterId) || bytes.Length == 0)
		{
			done?.Invoke(arg1: false, arg2: false);
			yield break;
		}
		string sha = null;
		((MelonBase)Main.instance).LoggerInstance.Msg("Fetching remote SHA...");
		yield return GetSha(masterId, delegate(string s)
		{
			sha = s;
		});
		((MelonBase)Main.instance).LoggerInstance.Msg((sha != null) ? ("Remote SHA: " + sha.Substring(0, 8)) : "No remote file found - will create new file.");
		if (sha != null && !string.IsNullOrEmpty(sha) && ShaMatchesLocal(sha, path))
		{
			((MelonBase)Main.instance).LoggerInstance.Msg("Upload skipped: Local file is identical to the server version.");
			done?.Invoke(arg1: true, arg2: true);
			yield break;
		}
		((MelonBase)Main.instance).LoggerInstance.Msg($"File size: {(float)bytes.Length / 1024f / 1024f:F2} MB");
		((MelonBase)Main.instance).LoggerInstance.Msg("Uploading to GitHub...");
		string body = "{\"message\":\"Upload bundle for " + masterId + ". Uploaded by " + Players.GetLocalPlayer().Data.GeneralData.PublicUsername.TrimString() + "\",\"content\":\"" + Convert.ToBase64String(bytes) + "\",\"branch\":\"main\"" + ((sha != null) ? (",\"sha\":\"" + sha + "\"") : "") + "}";
		string url = UploadUrlForPath("avatars/" + masterId);
		UnityWebRequest req = new UnityWebRequest(url, "PUT");
		req.uploadHandler = (UploadHandler)new UploadHandlerRaw(Il2CppStructArray<byte>.op_Implicit(Encoding.UTF8.GetBytes(body)));
		req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
		req.SetRequestHeader("Content-Type", "application/json");
		SetGhHeaders(req, wantRaw: false);
		yield return req.SendWebRequest();
		bool ok = (int)req.result == 1 && req.responseCode >= 200 && req.responseCode < 300;
		if (!ok)
		{
			DownloadHandler downloadHandler = req.downloadHandler;
			Il2CppStructArray<byte> errBytes = ((downloadHandler != null) ? downloadHandler.data : null);
			string errTxt = ((errBytes != null) ? Encoding.UTF8.GetString(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)errBytes)) : "");
			((MelonBase)Main.instance).LoggerInstance.Error($"Upload failed {masterId}: {req.responseCode} {req.error}\n{errTxt}");
			MelonCoroutines.Start(SendAudit("upload_fail", $"[{DateTime.UtcNow:O}] Upload failed for MasterId {masterId} " + $"Code={req.responseCode} Error={req.error} " + "Body=" + (string.IsNullOrWhiteSpace(errTxt) ? "<empty>" : errTxt)));
		}
		req.Dispose();
		done?.Invoke(ok, arg2: false);
	}

	public static bool ShaMatchesLocal(string remoteSha, string filePath, bool log = true)
	{
		byte[] array = File.ReadAllBytes(filePath);
		byte[] bytes = Encoding.ASCII.GetBytes($"blob {array.Length}\0");
		using SHA1 sHA = SHA1.Create();
		sHA.TransformBlock(bytes, 0, bytes.Length, bytes, 0);
		sHA.TransformFinalBlock(array, 0, array.Length);
		string text = BitConverter.ToString(sHA.Hash).Replace("-", "").ToLowerInvariant();
		if (log)
		{
			((MelonBase)Main.instance).LoggerInstance.Msg("Local SHA: " + text.Substring(0, 8));
		}
		return text == remoteSha;
	}

	public static IEnumerator PlayerHasAvatar(string masterId, Action<(bool hasAvatar, string returnedSha)> callback)
	{
		yield return MelonCoroutines.Start(GetSha(masterId, delegate(string sha)
		{
			callback((!string.IsNullOrEmpty(sha), sha));
		}));
	}

	public static IEnumerator GetSha(string masterId, Action<string> cb, bool log = true)
	{
		if (log)
		{
			((MelonBase)Main.instance).LoggerInstance.Msg("Fetching remote SHA for masterId " + masterId + "...");
		}
		string url = "https://api.github.com/repos/xLoadingx/custom-avatars/contents/avatars/" + Uri.EscapeDataString(masterId) + "?ref=main";
		UnityWebRequest req = UnityWebRequest.Get(url);
		SetGhHeaders(req, wantRaw: false);
		yield return req.SendWebRequest();
		if (req.responseCode == 404)
		{
			req.Dispose();
			cb(null);
			yield break;
		}
		if (log)
		{
			((MelonBase)Main.instance).LoggerInstance.Msg($"GitHub responded {req.responseCode}: {req.result}");
		}
		if ((int)req.result != 1)
		{
			((MelonBase)Main.instance).LoggerInstance.Error($"Web request completed unsuccessfully | ERROR {req.responseCode} | {req.error}");
			req.Dispose();
			cb(null);
			yield break;
		}
		DownloadHandler downloadHandler = req.downloadHandler;
		Il2CppStructArray<byte> data = ((downloadHandler != null) ? downloadHandler.data : null);
		req.Dispose();
		if (data == null || ((Il2CppArrayBase<byte>)(object)data).Length == 0)
		{
			cb(null);
			yield break;
		}
		string txt = Encoding.UTF8.GetString(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)data));
		int j = txt.IndexOf("\"sha\":\"", StringComparison.Ordinal);
		if (j < 0)
		{
			cb(null);
			yield break;
		}
		j += 7;
		int k = txt.IndexOf('"', j);
		cb((k > j) ? txt.Substring(j, k - j) : null);
	}

	public static IEnumerator DownloadToFile(string masterId, string savePath)
	{
		if (!_downloadingPlayers.Add(masterId))
		{
			((MelonBase)Main.instance).LoggerInstance.Warning("Player " + masterId + " is already being downloaded.");
			yield break;
		}
		string metaUrl = "https://api.github.com/repos/xLoadingx/custom-avatars/contents/avatars/" + Uri.EscapeDataString(masterId) + "?ref=main";
		UnityWebRequest metaReq = UnityWebRequest.Get(metaUrl);
		SetGhHeaders(metaReq, wantRaw: false);
		yield return metaReq.SendWebRequest();
		if ((int)metaReq.result != 1)
		{
			((MelonBase)Main.instance).LoggerInstance.Error("Metadata fetch failed for " + masterId + ": " + metaReq.error);
			metaReq.Dispose();
			yield break;
		}
		try
		{
			DownloadHandler downloadHandler = metaReq.downloadHandler;
			Il2CppStructArray<byte> bytes = ((downloadHandler != null) ? downloadHandler.data : null);
			if (bytes == null || ((Il2CppArrayBase<byte>)(object)bytes).Length == 0)
			{
				metaReq.Dispose();
				yield break;
			}
			string json = Encoding.UTF8.GetString(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)bytes));
			int sizeIndex2 = json.IndexOf("\"size\":", StringComparison.Ordinal);
			if (sizeIndex2 >= 0)
			{
				sizeIndex2 += 7;
				int endIndex = json.IndexOfAny(new char[2] { ',', '}' }, sizeIndex2);
				string sizeStr = json.Substring(sizeIndex2, endIndex - sizeIndex2).Trim();
				if (int.TryParse(sizeStr, out var fileSizeBytes))
				{
					int maxDownloadBytes = (int)((ModSetting)Main.instance.downloadLimitMB).SavedValue * 1024 * 1024;
					if (fileSizeBytes > maxDownloadBytes)
					{
						((MelonBase)Main.instance).LoggerInstance.Warning($"Download skipped: {fileSizeBytes / 1048576} MB exceeds limit of {maxDownloadBytes / 1048576} MB.");
						metaReq.Dispose();
						yield break;
					}
				}
			}
		}
		catch (Exception ex)
		{
			Exception e = ex;
			((MelonBase)Main.instance).LoggerInstance.Error("Error parsing metadata for " + masterId + ": " + e.Message);
			metaReq.Dispose();
			yield break;
		}
		metaReq.Dispose();
		UnityWebRequest req = UnityWebRequest.Get(GhUrl(masterId));
		SetGhHeaders(req, wantRaw: true);
		yield return req.SendWebRequest();
		if ((int)req.result != 1)
		{
			((MelonBase)Main.instance).LoggerInstance.Error("Download failed for " + masterId + ": " + req.error);
		}
		else
		{
			File.WriteAllBytes(savePath, Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)req.downloadHandler.data));
		}
		_downloadingPlayers.Remove(masterId);
		req.Dispose();
	}
}
public static class RigManager
{
	private static Main instance;

	public static readonly Dictionary<string, CustomRig> rigs = new Dictionary<string, CustomRig>();

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

	private static int activeLoads = 0;

	public static void Initialize(Main mainInstance)
	{
		instance = mainInstance;
	}

	public static void LogStatsForAvatar(GameObject rig)
	{
		//IL_0425: Unknown result type (might be due to invalid IL or missing references)
		//IL_042a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0444: Unknown result type (might be due to invalid IL or missing references)
		//IL_0446: Unknown result type (might be due to invalid IL or missing references)
		//IL_0480: Unknown result type (might be due to invalid IL or missing references)
		//IL_04cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_042e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0433: Unknown result type (might be due to invalid IL or missing references)
		//IL_0437: Unknown result type (might be due to invalid IL or missing references)
		//IL_043c: Unknown result type (might be due to invalid IL or missing references)
		Instance loggerInstance = ((MelonBase)Main.instance).LoggerInstance;
		if ((Object)(object)rig == (Object)null)
		{
			loggerInstance.Warning("LogStatsForAvatar: rig is null");
			return;
		}
		Il2CppArrayBase<SkinnedMeshRenderer> componentsInChildren = rig.GetComponentsInChildren<SkinnedMeshRenderer>();
		if (componentsInChildren.Length == 0)
		{
			loggerInstance.Warning("LogStatsForAvatar: No SkinnedMeshRenderer or mesh found");
			return;
		}
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		bool flag = false;
		bool flag2 = false;
		foreach (SkinnedMeshRenderer item in componentsInChildren)
		{
			if ((Object)(object)item.sharedMesh != (Object)null)
			{
				num += item.sharedMesh.vertexCount;
			}
			Il2CppReferenceArray<Material> sharedMaterials = ((Renderer)item).sharedMaterials;
			num2 += ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length;
			foreach (Material item2 in (Il2CppArrayBase<Material>)(object)sharedMaterials)
			{
				if ((Object)(object)item2 == (Object)null)
				{
					continue;
				}
				foreach (string item3 in (Il2CppArrayBase<string>)(object)item2.GetTexturePropertyNames())
				{
					Texture texture = item2.GetTexture(item3);
					if ((Object)(object)texture != (Object)null)
					{
						num3++;
						if (texture.width >= 4096 || texture.height >= 4096)
						{
							flag = true;
							loggerInstance.Warning($"[Avatar Optimization] Huge texture on '{((Object)item2).name}' ({item3}): {texture.width}x{texture.height}");
						}
					}
				}
				Shader shader = item2.shader;
				int num5 = ((shader != null) ? shader.passCount : 0);
				num4 += num5;
				if (num5 > 7)
				{
					loggerInstance.Warning($"[Avatar Optimiziation] Shader '{((Object)item2.shader).name}' has {num5} passes.");
				}
				if (((Object)item2.shader).name.ToLower().Contains("tessellation"))
				{
					flag2 = true;
					((MelonBase)Main.instance).LoggerInstance.Warning("[Avatar Optimization] Shader '" + ((Object)item2.shader).name + "' uses expensive features.");
				}
			}
		}
		string text = "";
		if (num > 70000)
		{
			text += " High vertex count;";
		}
		if (num2 > 5)
		{
			text += " Too many materials;";
		}
		if (flag)
		{
			text += " Huge Textures;";
		}
		if (flag2)
		{
			text += " Heavy Shaders;";
		}
		if (num4 >= 32)
		{
			text += " Many Shader Passes;";
		}
		ConsoleColor consoleColor;
		string text2;
		if (num > 70000 || num2 > 5 || flag || flag2)
		{
			consoleColor = ConsoleColor.Red;
			text2 = "BAD";
		}
		else if (num > 50000 || num2 > 3)
		{
			consoleColor = ConsoleColor.Yellow;
			text2 = "MEDIUM";
		}
		else
		{
			consoleColor = ConsoleColor.Green;
			text2 = "GOOD";
		}
		MelonLogger.MsgPastel(consoleColor, "-------------------------------------------------------------");
		loggerInstance.MsgPastel(consoleColor, $"[Avatar Optimization] {text2}: {num} verts, {num2} mat(s), {num3} texture(s).");
		loggerInstance.MsgPastel(ConsoleColor.Yellow, "WARNINGS: " + (string.IsNullOrEmpty(text) ? "None" : text.TrimEnd(new char[1] { ';' })));
		MelonLogger.MsgPastel(consoleColor, "-------------------------------------------------------------");
		if (Main.instance.currentScene == "Gym")
		{
			if (1 == 0)
			{
			}
			Color val = (Color)(consoleColor switch
			{
				ConsoleColor.Green => new Color(0f, 0.5f, 0f), 
				ConsoleColor.Yellow => Color.yellow, 
				_ => Color.red, 
			});
			if (1 == 0)
			{
			}
			Color color = val;
			GameObject avatarOptimizationParent = Main.instance.avatarOptimizationParent;
			((TMP_Text)((Component)avatarOptimizationParent.transform.GetChild(0)).GetComponent<TextMeshPro>()).text = text2;
			((Graphic)((Component)avatarOptimizationParent.transform.GetChild(0)).GetComponent<TextMeshPro>()).color = color;
			((TMP_Text)((Component)avatarOptimizationParent.transform.GetChild(1)).GetComponent<TextMeshPro>()).text = $"{num} verts, {num2} mat(s), {num3} texture(s).";
			((Graphic)((Component)avatarOptimizationParent.transform.GetChild(1)).GetComponent<TextMeshPro>()).color = color;
			((TMP_Text)((Component)avatarOptimizationParent.transform.GetChild(2)).GetComponent<TextMeshPro>()).text = "WARNINGS: " + (string.IsNullOrEmpty(text) ? "None" : text.TrimEnd(new char[1] { ';' }));
		}
	}

	public static void ClearRigs()
	{
		foreach (CustomRig value in rigs.Values)
		{
			value.Apply(CustomRig.RigState.Original);
			if (Main.instance.perPlayerToggles.ContainsKey(value))
			{
				Main.instance.RemoveRigFromList(value);
			}
			Object.Destroy((Object)(object)value.Root);
		}
		rigs.Clear();
	}

	public static Stream ConvertToIl2CppStream(Stream stream)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		MemoryStream val = new MemoryStream();
		byte[] array = new byte[4096];
		Il2CppStructArray<byte> val2 = new Il2CppStructArray<byte>(array);
		int num;
		while ((num = stream.Read(array, 0, array.Length)) > 0)
		{
			Il2CppStructArray<byte> val3 = Il2CppStructArray<byte>.op_Implicit(array);
			((Stream)val).Write(val3, 0, num);
		}
		((Stream)val).Flush();
		return (Stream)(object)val;
	}

	public static MemoryStream StreamFromFile(string path)
	{
		return new MemoryStream(File.ReadAllBytes(path));
	}

	public static IEnumerator LoadAssetBundleFromFileAsync(string filePath, Action<AssetBundle> onLoaded)
	{
		MemoryStream ms = null;
		Task loadTask = Task.Run(delegate
		{
			ms = StreamFromFile(filePath);
		});
		while (!loadTask.IsCompleted)
		{
			yield return null;
		}
		if (ms == null)
		{
			onLoaded?.Invoke(null);
			yield break;
		}
		Stream il2cppStream = ConvertToIl2CppStream(ms);
		AssetBundle bundle = AssetBundle.LoadFromStream(il2cppStream);
		onLoaded?.Invoke(bundle);
	}

	public static IEnumerator LoadRigForPlayer(Player player, Action<GameObject> onLoaded, bool log = true, string remoteSha = null)
	{
		object obj;
		if (player == null)
		{
			obj = null;
		}
		else
		{
			PlayerData data = player.Data;
			if (data == null)
			{
				obj = null;
			}
			else
			{
				GeneralData generalData = data.GeneralData;
				obj = ((generalData != null) ? generalData.PlayFabMasterId : null);
			}
		}
		string playerID = (string)obj;
		if (string.IsNullOrEmpty(playerID))
		{
			MelonLogger.Warning("LoadRigForPlayer: playerID is null or empty");
			yield break;
		}
		if ((int)player.Controller.ControllerType != 1)
		{
			if (!loadingPlayers.Add(playerID))
			{
				MelonLogger.Msg("LoadRigForPlayer: player " + playerID + " is already loading");
				yield break;
			}
			while (activeLoads >= (int)((ModSetting)Main.instance.maxConcurrentDownloads).SavedValue)
			{
				yield return null;
			}
			activeLoads++;
		}
		try
		{
			bool isLocal = player == Players.GetLocalPlayer();
			string opponentPath = Path.Combine(MelonEnvironment.UserDataDirectory, "CustomAvatars", "Opponents");
			if (!Directory.Exists(opponentPath))
			{
				Directory.CreateDirectory(opponentPath);
			}
			string basePath = Path.Combine(MelonEnvironment.UserDataDirectory, "CustomAvatars");
			string filePath = (isLocal ? Directory.GetFiles(basePath, "*.rumbleavatar").FirstOrDefault() : Path.Combine(opponentPath, playerID));
			if (!isLocal && !File.Exists(filePath))
			{
				if (log)
				{
					((MelonBase)Main.instance).LoggerInstance.Msg("Downloading avatar for path " + opponentPath);
				}
				yield return MelonCoroutines.Start(RemoteAvatarLoader.DownloadToFile(playerID, filePath));
			}
			string rigPath = (isLocal ? Directory.GetFiles(basePath, "*.rumbleavatar").FirstOrDefault() : Path.Combine(basePath, "Opponents", playerID));
			if (string.IsNullOrEmpty(rigPath) || !File.Exists(rigPath))
			{
				Instance loggerInstance = ((MelonBase)Main.instance).LoggerInstance;
				object obj2;
				if (!isLocal)
				{
					if (player == null)
					{
						obj2 = null;
					}
					else
					{
						GeneralData generalData2 = player.Data.GeneralData;
						obj2 = ((generalData2 != null) ? generalData2.PublicUsername : null);
					}
					if (obj2 == null)
					{
						obj2 = "unknown";
					}
				}
				else
				{
					obj2 = "you";
				}
				loggerInstance.Warning("No custom avatar found for " + (string?)obj2 + " at " + basePath);
				yield break;
			}
			AssetBundle rigBundle = null;
			yield return MelonCoroutines.Start(LoadAssetBundleFromFileAsync(rigPath, delegate(AssetBundle bundle)
			{
				rigBundle = bundle;
			}));
			AssetBundle obj3 = rigBundle;
			GameObject rigPrefab = ((obj3 != null) ? obj3.LoadAsset<GameObject>("Rig") : null);
			if ((Object)(object)rigPrefab == (Object)null)
			{
				Instance loggerInstance2 = ((MelonBase)Main.instance).LoggerInstance;
				object obj4;
				if (!isLocal)
				{
					if (player == null)
					{
						obj4 = null;
					}
					else
					{
						PlayerData data2 = player.Data;
						if (data2 == null)
						{
							obj4 = null;
						}
						else
						{
							GeneralData generalData3 = data2.GeneralData;
							obj4 = ((generalData3 != null) ? generalData3.PublicUsername : null);
						}
					}
					if (obj4 == null)
					{
						obj4 = "unknown";
					}
				}
				else
				{
					obj4 = "local player";
				}
				loggerInstance2.Error("Failed to load 'Rig' GameObject for " + (string?)obj4 + " from path " + rigPath);
				yield break;
			}
			GameObject rigInstance = Object.Instantiate<GameObject>(rigPrefab);
			((Object)rigInstance).name = "RIG - " + playerID;
			rigInstance.transform.SetParent(Main.instance.rigParent.transform, true);
			if ((Object)(object)((player != null) ? player.Controller : null) == (Object)null)
			{
				((MelonBase)Main.instance).LoggerInstance.Error("player.Controller is null");
				yield break;
			}
			if ((Object)(object)((Component)player.Controller).gameObject == (Object)null)
			{
				((MelonBase)Main.instance).LoggerInstance.Error("player.Controller.gameObject is null");
				yield break;
			}
			CustomRig customRig = ((Component)player.Controller).gameObject.GetOrAddComponent<CustomRig>();
			if ((Object)(object)customRig == (Object)null)
			{
				((MelonBase)Main.instance).LoggerInstance.Error("Failed to get or add CustomRig component");
				yield break;
			}
			rigs[playerID] = customRig;
			TextAsset jsonAsset = rigBundle.LoadAsset<TextAsset>("Config");
			if ((Object)(object)jsonAsset == (Object)null)
			{
				((MelonBase)Main.instance).LoggerInstance.Warning("Config.json not found in rig bundle. Make sure your avatar has a AvatarDescriptor that was exported.");
			}
			else
			{
				try
				{
					AvatarDescriptorExport config = JsonConvert.DeserializeObject<AvatarDescriptorExport>(jsonAsset.text);
					customRig.Config = config;
				}
				catch (Exception ex2)
				{
					Exception ex = ex2;
					((MelonBase)Main.instance).LoggerInstance.Error("Failed to parse avatar config: " + ex.Message);
				}
			}
			if (customRig.Config != null)
			{
				foreach (BlendshapeDefault blendshape in customRig.Config.defaultBlendshapes)
				{
					if (blendshape.index >= 0)
					{
						customRig.MeshRenderer.SetBlendShapeWeight(blendshape.index, blendshape.weight);
						continue;
					}
					((MelonBase)Main.instance).LoggerInstance.Warning("Blendshape '" + blendshape.name + "' not found on mesh '" + ((Object)customRig.MeshRenderer.sharedMesh).name + "'");
				}
			}
			customRig.PlayerName = player.Data.GeneralData.PublicUsername;
			customRig.AvatarFilePath = filePath;
			rigBundle.Unload(false);
			if (log)
			{
				((MelonBase)Main.instance).LoggerInstance.Msg("Loading rig for player " + playerID);
			}
			if ((Object)(object)rigInstance != (Object)null && log && (((bool)((ModSetting)Main.instance.logAvatarStats).SavedValue && isLocal) || ((bool)((ModSetting)Main.instance.logOtherAvatarStats).SavedValue && !isLocal)))
			{
				LogStatsForAvatar(rigInstance);
			}
			ApplyRigToPlayer(player, rigInstance, log);
			if (!isLocal)
			{
				Path.Combine(basePath, "Opponents", playerID);
				CustomRig rig = ((Component)player.Controller).GetComponent<CustomRig>();
				Main.instance.AddRigToList(customRig);
				ResolveRigState(player, rig);
			}
			GameObject camObj = GameObject.Find("RumbleHud_" + playerID + "_portraitCamera");
			Camera cam = ((camObj != null) ? camObj.GetComponent<Camera>() : null);
			if ((Object)(object)cam != (Object)null)
			{
				cam.nearClipPlane = 0.01f;
			}
			(Type.GetType("RumbleHud.Hud, RumbleHud")?.GetMethod("RegeneratePortraits", BindingFlags.Static | BindingFlags.Public))?.Invoke(null, new object[1] { Main.instance.currentScene == "Gym" });
			onLoaded?.Invoke(rigInstance);
		}
		finally
		{
			activeLoads--;
			loadingPlayers.Remove(playerID);
		}
	}

	public static void ResolveRigState(Player player, CustomRig rig)
	{
		if (!(bool)((ModSetting)Main.instance.toggleOthers).Value)
		{
			rig.Apply(CustomRig.RigState.Original);
			return;
		}
		object obj;
		if (player == null)
		{
			obj = null;
		}
		else
		{
			PlayerController controller = player.Controller;
			obj = ((controller != null) ? ((Component)controller).GetComponent<PhotonView>() : null);
		}
		PhotonView val = (PhotonView)obj;
		object obj2;
		if (val == null)
		{
			obj2 = null;
		}
		else
		{
			Player controller2 = val.Controller;
			obj2 = ((controller2 != null) ? controller2.CustomProperties : null);
		}
		Hashtable val2 = (Hashtable)obj2;
		Object val3 = default(Object);
		if (val2 != null && ((Dictionary<Object, Object>)(object)val2).TryGetValue(Object.op_Implicit("CA_Avatar"), ref val3))
		{
			ModSetting<bool> value;
			if (!((Il2CppObjectBase)val3).Unbox<bool>())
			{
				rig.Apply(CustomRig.RigState.Original);
			}
			else if (Main.instance.perPlayerToggles.TryGetValue(rig, out value))
			{
				rig.Apply(((bool)((ModSetting)value).SavedValue) ? CustomRig.RigState.Rigged : CustomRig.RigState.Original);
			}
			else
			{
				rig.Apply(CustomRig.RigState.Rigged);
			}
		}
		else
		{
			rig.Apply(CustomRig.RigState.Original);
		}
	}

	public static void ApplyRigToPlayer(Player player, GameObject rig, bool log = true)
	{
		if (player == null || (Object)(object)rig == (Object)null)
		{
			return;
		}
		((Component)player.Controller).GetComponent<CustomRig>().CaptureRig(rig);
		string text = player.Data.GeneralData.PublicUsername.TrimString();
		SkinnedMeshRenderer component = ((Component)((Component)player.Controller).transform.GetChild(1).GetChild(0)).GetComponent<SkinnedMeshRenderer>();
		SkinnedMeshRenderer componentInChildren = rig.GetComponentInChildren<SkinnedMeshRenderer>(true);
		if (!((Object)(object)component == (Object)null) && !((Object)(object)componentInChildren == (Object)null))
		{
			Transform child = ((Component)player.Controller).transform.GetChild(1).GetChild(1);
			ApplyRigToSMR(child, rig, ((Component)((Component)player.Controller).transform.GetChild(1)).GetComponent<Animator>(), ((Component)player.Controller).GetComponent<CustomRig>(), null, player.Controller.GetSubsystem<PlayerVisuals>());
			if (log)
			{
				((MelonBase)instance).LoggerInstance.Msg("Applied custom rig to player " + text + ".");
			}
		}
	}

	public static void ApplyRigBones(Animator rigAnimator, Animator rumbleAnimator, Transform rigRoot, Transform rumbleRoot)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Invalid comparison between Unknown and I4
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)rigAnimator != (Object)null && rigAnimator.isHuman)
		{
			foreach (HumanBodyBones value2 in Enum.GetValues(typeof(HumanBodyBones)))
			{
				if ((int)value2 != 55)
				{
					Transform boneTransform = rigAnimator.GetBoneTransform(value2);
					Transform boneTransform2 = rumbleAnimator.GetBoneTransform(value2);
					if (!((Object)(object)boneTransform == (Object)null) && !((Object)(object)boneTransform2 == (Object)null))
					{
						boneTransform.SetParent(boneTransform2, true);
						boneTransform.localPosition = Vector3.zero;
						boneTransform.localRotation = Quaternion.identity;
						boneTransform.localScale = Vector3.Scale(boneTransform.localScale, boneTransform2.localScale);
						((Component)boneTransform).gameObject.AddComponent<CustomRigBone>();
					}
				}
			}
			return;
		}
		Dictionary<string, List<Transform>> dictionary = (from t in (IEnumerable<Transform>)((Component)rumbleRoot).GetComponentsInChildren<Transform>(true)
			group t by ((Object)t).name).ToDictionary((IGrouping<string, Transform> g) => g.Key, (IGrouping<string, Transform> g) => g.ToList());
		foreach (Transform componentsInChild in ((Component)rigRoot).GetComponentsInChildren<Transform>(true))
		{
			if (!dictionary.TryGetValue(((Object)componentsInChild).name, out var value))
			{
				continue;
			}
			foreach (Transform item in value)
			{
				componentsInChild.SetParent(item, true);
				componentsInChild.localPosition = Vector3.zero;
				componentsInChild.localRotation = Quaternion.identity;
				componentsInChild.localScale = Vector3.Scale(componentsInChild.localScale, item.localScale);
				((Component)componentsInChild).gameObject.AddComponent<CustomRigBone>();
			}
		}
	}

	public static void ApplyRigToSMR(Transform skeletonRoot, GameObject rig, Animator rumbleAnimator = null, CustomRig customRig = null, SkinnedMeshRenderer renderer = null, PlayerVisuals visuals = null)
	{
		if ((Object)(object)skeletonRoot == (Object)null || (Object)(object)rig == (Object)null)
		{
			return;
		}
		SkinnedMeshRenderer componentInChildren = rig.GetComponentInChildren<SkinnedMeshRenderer>(true);
		if ((Object)(object)componentInChildren == (Object)null)
		{
			return;
		}
		if ((Object)(object)renderer == (Object)null)
		{
			if ((Object)(object)customRig != (Object)null && (Object)(object)customRig.MeshRenderer != (Object)null)
			{
				ApplyRig(customRig.Root.transform, componentInChildren, customRig.MeshRenderer, customRig.OriginalMaterial);
			}
		}
		else
		{
			ApplyRig(rig.transform, componentInChildren, renderer, ((Renderer)renderer).material);
		}
		void ApplyRig(Transform customRigTransform, SkinnedMeshRenderer rigRenderer, SkinnedMeshRenderer playerRenderer, Material originalMaterial)
		{
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Expected O, but got Unknown
			//IL_036a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Expected O, but got Unknown
			//IL_053e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0548: Expected O, but got Unknown
			if ((Object)(object)customRig == (Object)null)
			{
				((MelonBase)Main.instance).LoggerInstance.Error("customRig is null");
			}
			else if ((Object)(object)skeletonRoot == (Object)null)
			{
				((MelonBase)Main.instance).LoggerInstance.Error("skeletonRoot is null");
			}
			else if ((Object)(object)playerRenderer == (Object)null)
			{
				((MelonBase)Main.instance).LoggerInstance.Error("playerRenderer is null");
			}
			else if ((Object)(object)rigRenderer == (Object)null)
			{
				((MelonBase)Main.instance).LoggerInstance.Error("rigRenderer is null");
			}
			else
			{
				Il2CppArrayBase<CustomRigBone> componentsInChildren = ((Component)skeletonRoot).GetComponentsInChildren<CustomRigBone>(true);
				if (componentsInChildren != null)
				{
					foreach (CustomRigBone item in componentsInChildren)
					{
						if ((Object)(object)item != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null)
						{
							Object.DestroyImmediate((Object)(object)((Component)item).gameObject);
						}
					}
				}
				((Renderer)playerRenderer).enabled = false;
				foreach (Collider componentsInChild in rig.GetComponentsInChildren<Collider>(true))
				{
					Object.Destroy((Object)(object)componentsInChild);
				}
				Animator componentInParent = ((Component)customRigTransform).GetComponentInParent<Animator>();
				ApplyRigBones(componentInParent, rumbleAnimator, rig.transform, skeletonRoot);
				foreach (Collider componentsInChild2 in ((Component)customRigTransform).GetComponentsInChildren<Collider>(true))
				{
					Object.Destroy((Object)(object)componentsInChild2);
				}
				if ((Object)(object)rigRenderer.sharedMesh != (Object)null)
				{
					if (customRig.Config.swapOriginalMesh)
					{
						playerRenderer.sharedMesh = rigRenderer.sharedMesh;
					}
					else
					{
						((MelonBase)Main.instance).LoggerInstance.Warning("rigRenderer.sharedMesh is null");
					}
				}
				Il2CppReferenceArray<Transform> bones = rigRenderer.bones;
				if (bones != null && ((Il2CppArrayBase<Transform>)(object)bones).Length > 0)
				{
					if (customRig.Config.swapOriginalMesh)
					{
						playerRenderer.bones = rigRenderer.bones;
						customRig.RigBones = Il2CppArrayBase<Transform>.op_Implicit((Il2CppArrayBase<Transform>)(object)rigRenderer.bones);
					}
				}
				else
				{
					((MelonBase)Main.instance).LoggerInstance.Warning("rigRenderer.bones array is null or empty");
				}
				if ((Object)(object)((Renderer)playerRenderer).material == (Object)null)
				{
					((MelonBase)Main.instance).LoggerInstance.Error("playerRenderer.material is null");
				}
				else if ((Object)(object)((Renderer)rigRenderer).material == (Object)null)
				{
					((MelonBase)Main.instance).LoggerInstance.Error("rigRenderer.material is null");
				}
				else
				{
					Il2CppReferenceArray<Material> materials = ((Renderer)rigRenderer).materials;
					Material[] array = (Material[])(object)new Material[((Il2CppArrayBase<Material>)(object)materials).Length];
					int num = customRig.Config?.bodyShaderSlot ?? 0;
					for (int i = 0; i < ((Il2CppArrayBase<Material>)(object)materials).Length; i++)
					{
						Material val = ((Il2CppArrayBase<Material>)(object)materials)[i];
						bool flag = customRig.Config != null && customRig.Config.playerShaderSlots.Contains(i);
						Material val2;
						if (flag)
						{
							val2 = new Material(customRig.OriginalMaterial);
							Texture texture = val.GetTexture("_BaseMap");
							if ((Object)(object)texture != (Object)null)
							{
								val2.SetTexture("_ColorAtlas", texture);
							}
						}
						else
						{
							val2 = new Material(val);
						}
						if (i == num && (Object)(object)visuals != (Object)null && customRig.IsLocal)
						{
							if (flag)
							{
								Texture texture2 = val.GetTexture("_BaseMap");
								if ((Object)(object)texture2 != (Object)null)
								{
									visuals.NonHeadClippedMaterial.SetTexture("_ColorAtlas", texture2);
								}
							}
							else
							{
								visuals.NonHeadClippedMaterial = val;
								if (visuals.NonHeadClippedMaterial.HasFloat("_IsLocal"))
								{
									visuals.NonHeadClippedMaterial.SetFloat("_IsLocal", 0f);
								}
							}
						}
						if (val2.HasFloat("_IsLocal"))
						{
							val2.SetFloat("_IsLocal", customRig.IsLocal ? 1f : 0f);
						}
						array[i] = val2;
					}
					if (customRig.Config.swapOriginalMesh)
					{
						((Renderer)playerRenderer).materials = Il2CppReferenceArray<Material>.op_Implicit(array);
					}
					else
					{
						((Renderer)rigRenderer).materials = Il2CppReferenceArray<Material>.op_Implicit(array);
						((Renderer)playerRenderer).material = customRig.OriginalMaterial;
					}
					if ((Object)(object)visuals != (Object)null && customRig.IsLocal)
					{
						customRig.RigVisualsMaterial = new Material(visuals.NonHeadClippedMaterial);
						((Object)customRig.RigVisualsMaterial).hideFlags = (HideFlags)61;
					}
					if ((Object)(object)customRig != (Object)null)
					{
						if ((Object)(object)((Renderer)playerRenderer).material != (Object)null)
						{
							customRig.RigMaterials = (Material[])(object)new Material[((Il2CppArrayBase<Material>)(object)((Renderer)playerRenderer).materials).Length];
							for (int j = 0; j < ((Il2CppArrayBase<Material>)(object)((Renderer)playerRenderer).materials).Count; j++)
							{
								Material val3 = ((Il2CppArrayBase<Material>)(object)((Renderer)playerRenderer).materials)[j];
								customRig.RigMaterials[j] = val3;
								((Object)val3).hideFlags = (HideFlags)61;
							}
						}
						else
						{
							((MelonBase)Main.instance).LoggerInstance.Warning("playerRenderer.material is null while assigning to customRigComp");
						}
						if ((Object)(object)rigRenderer.sharedMesh != (Object)null)
						{
							customRig.RigMesh = Object.Instantiate<Mesh>(rigRenderer.sharedMesh);
							((Object)customRig.RigMesh).hideFlags = (HideFlags)61;
						}
						else
						{
							((MelonBase)Main.instance).LoggerInstance.Warning("rigRenderer.sharedMesh is null while assigning to customRigComp");
						}
					}
					if ((Object)(object)((Component)rigRenderer).gameObject != (Object)null && customRig.Config.swapOriginalMesh)
					{
						Object.Destroy((Object)(object)((Component)rigRenderer).gameObject);
					}
					((Renderer)playerRenderer).enabled = true;
				}
			}
		}
	}

	public static Transform FindDeepChild(Transform parent, string name)
	{
		for (int i = 0; i < parent.childCount; i++)
		{
			Transform child = parent.GetChild(i);
			if (((Object)child).name == name)
			{
				return child;
			}
			Transform val = FindDeepChild(child, name);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
		}
		return null;
	}
}
[Serializable]
public class AvatarDescriptorExport
{
	public enum BlinkType
	{
		None,
		Single,
		LeftRight
	}

	public bool swapOriginalMesh = true;

	public List<int> playerShaderSlots = new List<int>();

	public int bodyShaderSlot = -1;

	public List<BlendshapeDefault> defaultBlendshapes = new List<BlendshapeDefault>();

	public int blinkType = -1;

	public int blinkBlendshape = -1;

	public int blinkLeftBlendshape = -1;

	public int blinkRightBlendshape = -1;

	public int jawOpenBlendshape = -1;

	public float voiceMultiplier = 30f;

	public bool autoBlink = false;

	public Vector2 blinkInterval = Vector2.zero;

	public float blinkSpeed = 0.05f;
}
[Serializable]
public class BlendshapeDefault
{
	public string name;

	public int index;

	public float weight;
}
[RegisterTypeInIl2Cpp]
public class GrabbableObject : MonoBehaviour
{
	public Transform leftHand;

	public Transform rightHand;

	public Player player;

	public Vector3 originalPosition;

	public Quaternion originalRotation;

	public Transform originalParent;

	private Transform currentHand = null;

	private bool isGrabbed = false;

	private bool isLeftTouching = false;

	private bool isRightTouching = false;

	private bool wasLeftGripHeldLastFrame = false;

	private bool wasRightGripHeldLastFrame = false;

	public IEnumerator Init(Player Player)
	{
		if (Player != null)
		{
			player = Player;
			originalParent = ((Component)this).transform.parent;
			originalPosition = ((Component)this).transform.localPosition;
			originalRotation = ((Component)this).transform.localRotation;
			while ((Object)(object)player.Controller == (Object)null)
			{
				yield return null;
			}
			leftHand = ((Component)player.Controller).transform.GetChild(2).GetChild(1).GetChild(1);
			rightHand = ((Component)player.Controller).transform.GetChild(2).GetChild(2).GetChild(1);
		}
	}

	private void Update()
	{
		bool flag = player.Controller.GetSubsystem<PlayerHandPresence>().leftHandGripInput.ReadValue<float>() > 0.5f;
		bool flag2 = player.Controller.GetSubsystem<PlayerHandPresence>().rightHandGripInput.ReadValue<float>() > 0.5f;
		if (isGrabbed)
		{
			if ((!((Object)(object)currentHand == (Object)(object)leftHand) || !(LeftController.GetGrip() > 0.5f)) && (!((Object)(object)currentHand == (Object)(object)rightHand) || !(RightController.GetGrip() > 0.5f)))
			{
				Release();
			}
		}
		else if (isLeftTouching && flag && !wasLeftGripHeldLastFrame)
		{
			Grab(leftHand);
		}
		else if (isRightTouching && flag2 && !wasRightGripHeldLastFrame)
		{
			Grab(rightHand);
		}
		wasLeftGripHeldLastFrame = flag;
		wasRightGripHeldLastFrame = flag2;
	}

	private void Grab(Transform hand)
	{
		//IL_0027: 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)
		isGrabbed = true;
		currentHand = hand;
		((Component)this).transform.SetParent(((Component)hand).transform);
		((Component)this).transform.localPosition = Vector3.zero;
		((Component)this).transform.localRotation = Quaternion.identity;
	}

	private void Release()
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		isGrabbed = false;
		isLeftTouching = false;
		isRightTouching = false;
		currentHand = null;
		((Component)this).transform.SetParent(originalParent);
		((Component)this).transform.localPosition = originalPosition;
		((Component)this).transform.localRotation = originalRotation;
	}

	private void OnTriggerEnter(Collider other)
	{
		MelonLogger.Msg(((Object)other).name);
		if ((Object)(object)((Component)other).transform == (Object)(object)leftHand)
		{
			isLeftTouching = true;
		}
		else if ((Object)(object)((Component)other).transform == (Object)(object)rightHand)
		{
			isRightTouching = true;
		}
	}

	private void OnTriggerExit(Collider other)
	{
		MelonLogger.Msg(((Object)other).name);
		if ((Object)(object)((Component)other).transform == (Object)(object)leftHand)
		{
			isLeftTouching = false;
		}
		else if ((Object)(object)((Component)other).transform == (Object)(object)rightHand)
		{
			isRightTouching = false;
		}
	}
}
[Flags]
public enum TriggerActionType
{
	Blendshape = 0,
	ToggleOn = 1,
	ToggleOff = 2,
	Toggle = 3,
	ToggleTemp = 4
}
public struct TriggerAction
{
	public TriggerActionType Type;

	public string TargetName;

	public GameObject TargetGameObject;

	public float Duration;

	public TriggerAction(TriggerActionType type, string targetName, float duration, GameObject targetGameObject = null)
	{
		Type = type;
		TargetName = targetName;
		Duration = duration;
		TargetGameObject = targetGameObject;
	}
}