Decompiled source of PeakClearCosmetics v1.7.2

BepInEx/plugins/PeakClearCosmetics/PeakClearCosmetics.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
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: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("PeakClearCosmetics")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PeakClearCosmetics")]
[assembly: AssemblyTitle("PeakClearCosmetics")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 PeakClearCosmeticsMod
{
	[BepInPlugin("com.tmh.peak.clearcosmetics", "PeakClearCosmetics", "1.7.0")]
	public class Plugin : BaseUnityPlugin
	{
		private ConfigEntry<bool> _verboseLogging;

		private ConfigEntry<KeyboardShortcut> _hotkey;

		private ConfigEntry<bool> _dryRun;

		private ConfigEntry<bool> _showButton;

		private ConfigEntry<float> _buttonX;

		private ConfigEntry<float> _buttonY;

		private ConfigEntry<string> _passportNameContains;

		private ConfigEntry<bool> _compatMultiCosmetic;

		private ConfigEntry<bool> _compatMoreCustomizations;

		private ConfigEntry<int> _reassertFrames;

		private const string ButtonText = "Clear Cosmetics";

		private GameObject _btnRoot;

		private Button _btn;

		private TextMeshProUGUI _label;

		private RectTransform _rt;

		private bool _clearing;

		private void Awake()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			_verboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", true, "If true, log details about scanning & detection.");
			_hotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Hotkey", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Hotkey to clear cosmetics immediately.");
			_dryRun = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "DryRun", false, "If true, do not modify anything; only log what would happen.");
			_showButton = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowButton", true, "Draw on-screen button when passport UI is open.");
			_buttonX = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "ButtonX", 20f, "Button X position from right.");
			_buttonY = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "ButtonY", 20f, "Button Y position from bottom.");
			_passportNameContains = ((BaseUnityPlugin)this).Config.Bind<string>("Detection", "PassportObjectNameContains", "Canvas_HUD", "Substring that identifies the passport UI Canvas/panel name.");
			_compatMultiCosmetic = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "TargetMultiCosmetic", true, "Also clear Multi_Cosmetic state.");
			_compatMoreCustomizations = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "TargetMoreCustomizations", true, "Also clear MoreCustomizations state.");
			_reassertFrames = ((BaseUnityPlugin)this).Config.Bind<int>("Compatibility", "ReassertClearFrames", 5, "Defensively reassert cleared values for N frames.");
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[PCC] v1.7.0 (compat + targeted-panel-refresh) awake. DLL path: " + Assembly.GetExecutingAssembly().Location));
		}

		private void Start()
		{
			CreateButton();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[PCC] UI created.");
		}

		private void Update()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = _hotkey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				OnClickClear();
			}
			bool flag = _showButton.Value && IsPassportOpen();
			if (Object.op_Implicit((Object)(object)_btnRoot) && _btnRoot.activeSelf != flag)
			{
				_btnRoot.SetActive(flag);
			}
		}

		private bool IsPassportOpen()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)Cursor.lockState > 0)
			{
				return false;
			}
			string text = _passportNameContains.Value?.Trim();
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			text = text.ToLowerInvariant();
			Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>();
			Canvas[] array2 = array;
			foreach (Canvas val in array2)
			{
				if (!Object.op_Implicit((Object)(object)val) || !((Behaviour)val).isActiveAndEnabled)
				{
					continue;
				}
				GameObject gameObject = ((Component)val).gameObject;
				string name = ((Object)gameObject).name;
				if (name.IndexOf("PCC_OverlayCanvas", StringComparison.OrdinalIgnoreCase) < 0 && name.ToLowerInvariant().Contains(text))
				{
					if (_verboseLogging.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("[PCC] Passport match: '" + name + "'"));
					}
					return true;
				}
			}
			return false;
		}

		private void CreateButton()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: 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)
			if (!Object.op_Implicit((Object)(object)_btnRoot))
			{
				GameObject val = new GameObject("PCC_OverlayCanvas");
				Object.DontDestroyOnLoad((Object)(object)val);
				Canvas val2 = val.AddComponent<Canvas>();
				val2.renderMode = (RenderMode)0;
				val2.sortingOrder = 32767;
				val.layer = 5;
				val.AddComponent<CanvasScaler>();
				val.AddComponent<GraphicRaycaster>();
				_btnRoot = new GameObject("PCC_Button");
				_btnRoot.transform.SetParent(val.transform, false);
				_rt = _btnRoot.AddComponent<RectTransform>();
				_rt.sizeDelta = new Vector2(220f, 44f);
				_rt.anchorMin = new Vector2(1f, 0f);
				_rt.anchorMax = new Vector2(1f, 0f);
				_rt.pivot = new Vector2(1f, 0f);
				_rt.anchoredPosition = new Vector2(0f - _buttonX.Value, _buttonY.Value);
				Image val3 = _btnRoot.AddComponent<Image>();
				((Graphic)val3).color = new Color(0f, 0f, 0f, 0.55f);
				_btn = _btnRoot.AddComponent<Button>();
				((UnityEvent)_btn.onClick).AddListener(new UnityAction(OnClickClear));
				GameObject val4 = new GameObject("Label");
				val4.transform.SetParent(_btnRoot.transform, false);
				RectTransform val5 = val4.AddComponent<RectTransform>();
				val5.anchorMin = Vector2.zero;
				val5.anchorMax = Vector2.one;
				val5.offsetMin = Vector2.zero;
				val5.offsetMax = Vector2.zero;
				_label = val4.AddComponent<TextMeshProUGUI>();
				((TMP_Text)_label).text = "Clear Cosmetics";
				((TMP_Text)_label).alignment = (TextAlignmentOptions)514;
				((TMP_Text)_label).fontSize = 22f;
				((Graphic)_label).raycastTarget = false;
				_btnRoot.SetActive(false);
			}
		}

		private void OnClickClear()
		{
			if (!_clearing)
			{
				((MonoBehaviour)this).StartCoroutine(ClearRoutine());
			}
		}

		private IEnumerator ClearRoutine()
		{
			_clearing = true;
			bool changed = false;
			try
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"[PCC] Clear → Photon props + PlayerPrefs (+ compat targets)");
				int photonCleared = 0;
				int prefsCleared = 0;
				Player lp = PhotonNetwork.LocalPlayer;
				if (lp != null)
				{
					List<object> keys = ((Dictionary<object, object>)(object)lp.CustomProperties)?.Keys?.Cast<object>().ToList();
					if (keys != null && keys.Count > 0)
					{
						Hashtable toNull = new Hashtable();
						foreach (object kObj in keys)
						{
							string k = kObj as string;
							if (string.IsNullOrEmpty(k))
							{
								continue;
							}
							string kl = k.ToLowerInvariant();
							int num;
							if (!kl.Contains("hat") && !kl.Contains("eye") && !kl.Contains("mouth") && !kl.Contains("accessor") && !kl.Contains("cosmetic"))
							{
								switch (kl)
								{
								case "hats":
								case "eyes":
								case "sxdmulticosmetics":
									goto IL_01f9;
								}
								if (!kl.StartsWith("mc_"))
								{
									num = (kl.Contains("morecustom") ? 1 : 0);
									goto IL_01fa;
								}
							}
							goto IL_01f9;
							IL_01fa:
							if (num != 0)
							{
								toNull[(object)k] = null;
								if (_verboseLogging.Value)
								{
									((BaseUnityPlugin)this).Logger.LogInfo((object)("[PCC] Will null Photon key: " + k));
								}
							}
							continue;
							IL_01f9:
							num = 1;
							goto IL_01fa;
						}
						if (((Dictionary<object, object>)(object)toNull).Count > 0)
						{
							photonCleared = ((Dictionary<object, object>)(object)toNull).Count;
							changed = true;
							if (!_dryRun.Value)
							{
								lp.SetCustomProperties(toNull, (Hashtable)null, (WebFlags)null);
							}
						}
					}
				}
				List<string> keysToWipe = new List<string>
				{
					"hat", "hats", "eyes", "mouth", "accessory", "accessories", "acc", "accs", "glasses", "brows",
					"eyebrows", "face", "cosmetics", "cosmetic_accessories", "cosmetic_hats", "cosmetic_mouths", "cosmetic_eyes", "SXDMultiCosmetics"
				};
				if (_compatMultiCosmetic.Value)
				{
					keysToWipe.AddRange(new string[9] { "MC_SelectedHats", "MC_SelectedEyes", "MC_SelectedMouths", "MC_SelectedAccessories", "MC_ActiveSet", "MC_LastEquipped", "MC_MultiSet", "MC_Serialized", "MC_State" });
				}
				if (_compatMoreCustomizations.Value)
				{
					keysToWipe.AddRange(new string[3] { "MoreCustomizations_Selected", "MoreCustomizations_Serialized", "MoreCustomizations_State" });
				}
				foreach (string j in keysToWipe)
				{
					if (PlayerPrefs.HasKey(j))
					{
						if (_verboseLogging.Value)
						{
							((BaseUnityPlugin)this).Logger.LogInfo((object)("[PCC] Will blank PlayerPrefs key: " + j));
						}
						if (!_dryRun.Value)
						{
							PlayerPrefs.SetString(j, "");
						}
						prefsCleared++;
						changed = true;
					}
				}
				if (changed && !_dryRun.Value)
				{
					PlayerPrefs.Save();
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"[PCC] Clear base complete. Photon nulled={photonCleared}, PlayerPrefs cleared={prefsCleared}");
				if (!_dryRun.Value)
				{
					InvokeCompatRefresh();
				}
			}
			catch (Exception ex)
			{
				Exception e = ex;
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"[PCC] ClearRoutine exception: {e}");
			}
			for (int i = 0; i < Mathf.Max(0, _reassertFrames.Value); i++)
			{
				ReassertClearedOnce();
				yield return null;
			}
			GameObject panel = GetPassportPanelRoot();
			if ((Object)(object)panel != (Object)null)
			{
				yield return ((MonoBehaviour)this).StartCoroutine(NudgePassportPanel(panel));
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[PCC] Passport panel not found — running fallback layout rebuild.");
				RefreshPassportLayoutFallback();
			}
			_clearing = false;
		}

		private void ReassertClearedOnce()
		{
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			try
			{
				string[] array = new string[25]
				{
					"hat", "hats", "eyes", "mouth", "accessory", "accessories", "glasses", "cosmetics", "cosmetic_accessories", "cosmetic_hats",
					"cosmetic_mouths", "cosmetic_eyes", "SXDMultiCosmetics", "MC_SelectedHats", "MC_SelectedEyes", "MC_SelectedMouths", "MC_SelectedAccessories", "MC_ActiveSet", "MC_LastEquipped", "MC_MultiSet",
					"MC_Serialized", "MC_State", "MoreCustomizations_Selected", "MoreCustomizations_Serialized", "MoreCustomizations_State"
				};
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (PlayerPrefs.HasKey(text))
					{
						PlayerPrefs.SetString(text, "");
					}
				}
				PlayerPrefs.Save();
				Player localPlayer = PhotonNetwork.LocalPlayer;
				if (localPlayer == null)
				{
					return;
				}
				Hashtable val = new Hashtable();
				foreach (object item in ((Dictionary<object, object>)(object)localPlayer.CustomProperties)?.Keys?.Cast<object>() ?? Array.Empty<object>())
				{
					string text2 = item as string;
					if (string.IsNullOrEmpty(text2))
					{
						continue;
					}
					string text3 = text2.ToLowerInvariant();
					if (!text3.Contains("hat") && !text3.Contains("eye") && !text3.Contains("mouth") && !text3.Contains("accessor") && !text3.Contains("cosmetic"))
					{
						switch (text3)
						{
						default:
							if (!text3.StartsWith("mc_") && !text3.Contains("morecustom"))
							{
								continue;
							}
							break;
						case "hats":
						case "eyes":
						case "sxdmulticosmetics":
							break;
						}
					}
					val[(object)text2] = null;
				}
				if (((Dictionary<object, object>)(object)val).Count > 0)
				{
					localPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"[PCC] ReassertClearedOnce failed: {arg}");
			}
		}

		private void InvokeCompatRefresh()
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				int num = 0;
				Assembly[] array = assemblies;
				foreach (Assembly assembly in array)
				{
					string text = assembly.GetName().Name ?? "";
					if (text.IndexOf("multi", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("cosmetic", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("custom", StringComparison.OrdinalIgnoreCase) < 0)
					{
						continue;
					}
					Type[] types = assembly.GetTypes();
					foreach (Type type in types)
					{
						string text2 = type.FullName ?? type.Name;
						if (text2.IndexOf("multi", StringComparison.OrdinalIgnoreCase) < 0 && text2.IndexOf("cosmetic", StringComparison.OrdinalIgnoreCase) < 0 && text2.IndexOf("custom", StringComparison.OrdinalIgnoreCase) < 0)
						{
							continue;
						}
						MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						MethodInfo[] array2 = methods;
						foreach (MethodInfo methodInfo in array2)
						{
							string name = methodInfo.Name;
							if ((name.IndexOf("Refresh", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Rebuild", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("UpdateUI", StringComparison.OrdinalIgnoreCase) < 0) || methodInfo.GetParameters().Length != 0)
							{
								continue;
							}
							try
							{
								object obj = null;
								if (methodInfo.IsStatic)
								{
									goto IL_019d;
								}
								obj = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null);
								if (obj != null)
								{
									goto IL_019d;
								}
								goto end_IL_0158;
								IL_019d:
								methodInfo.Invoke(obj, null);
								num++;
								end_IL_0158:;
							}
							catch
							{
							}
						}
					}
				}
				if (_verboseLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"[PCC] Compat refresh calls attempted: {num}");
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"[PCC] InvokeCompatRefresh failed: {arg}");
			}
		}

		private GameObject GetPassportPanelRoot()
		{
			try
			{
				Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>();
				string value = _passportNameContains.Value ?? string.Empty;
				Canvas[] array2 = array;
				foreach (Canvas val in array2)
				{
					if (!Object.op_Implicit((Object)(object)val) || !((Behaviour)val).enabled)
					{
						continue;
					}
					string name = ((Object)((Component)val).gameObject).name;
					if (name.IndexOf(value, StringComparison.OrdinalIgnoreCase) < 0)
					{
						continue;
					}
					TextMeshProUGUI[] componentsInChildren = ((Component)val).GetComponentsInChildren<TextMeshProUGUI>(true);
					TextMeshProUGUI[] array3 = componentsInChildren;
					foreach (TextMeshProUGUI val2 in array3)
					{
						if (!Object.op_Implicit((Object)(object)val2))
						{
							continue;
						}
						string text = ((TMP_Text)val2).text ?? string.Empty;
						if (text.IndexOf("Passport", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							Transform transform = ((TMP_Text)val2).transform;
							RectTransform val3 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
							RectTransform val4 = val3;
							while (Object.op_Implicit((Object)(object)val4) && Object.op_Implicit((Object)(object)((Transform)val4).parent) && (Object)(object)((Transform)val4).parent != (Object)(object)((Component)val).transform)
							{
								Transform parent = ((Transform)val4).parent;
								val4 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
							}
							GameObject val5 = (Object.op_Implicit((Object)(object)val4) ? ((Component)val4).gameObject : ((Component)val).gameObject);
							if (_verboseLogging.Value)
							{
								((BaseUnityPlugin)this).Logger.LogInfo((object)("[PCC] Passport panel root: '" + ((Object)val5).name + "' under HUD '" + name + "'"));
							}
							return val5;
						}
					}
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"[PCC] GetPassportPanelRoot failed: {arg}");
			}
			return null;
		}

		private IEnumerator NudgePassportPanel(GameObject panel)
		{
			if (Object.op_Implicit((Object)(object)panel) && panel.activeSelf)
			{
				if (Object.op_Implicit((Object)(object)EventSystem.current))
				{
					EventSystem.current.SetSelectedGameObject((GameObject)null);
				}
				panel.SetActive(false);
				yield return null;
				panel.SetActive(true);
				yield return null;
				Canvas.ForceUpdateCanvases();
				RectTransform[] componentsInChildren = panel.GetComponentsInChildren<RectTransform>(true);
				foreach (RectTransform rt in componentsInChildren)
				{
					LayoutRebuilder.ForceRebuildLayoutImmediate(rt);
				}
				Canvas.ForceUpdateCanvases();
				if (_verboseLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"[PCC] NudgePassportPanel: toggled panel & rebuilt layout.");
				}
			}
		}

		private void RefreshPassportLayoutFallback()
		{
			try
			{
				Canvas.ForceUpdateCanvases();
				Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>();
				int num = 0;
				Canvas[] array2 = array;
				foreach (Canvas val in array2)
				{
					if (Object.op_Implicit((Object)(object)val))
					{
						RectTransform[] componentsInChildren = ((Component)val).GetComponentsInChildren<RectTransform>(true);
						foreach (RectTransform val2 in componentsInChildren)
						{
							LayoutRebuilder.ForceRebuildLayoutImmediate(val2);
							num++;
						}
					}
				}
				Canvas.ForceUpdateCanvases();
				if (_verboseLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"[PCC] Fallback layout rebuild complete, rects={num}.");
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"[PCC] RefreshPassportLayoutFallback error: {arg}");
			}
		}
	}
}