Decompiled source of PlayerSteveSuits v2.2.7

PlayerSteveSuits.dll

Decompiled a week 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.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ModelReplacement;
using Newtonsoft.Json.Linq;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PlayerSteveSuits")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.2.7.0")]
[assembly: AssemblyInformationalVersion("2.2.7")]
[assembly: AssemblyProduct("PlayerSteveSuits")]
[assembly: AssemblyTitle("PlayerSteveSuits")]
[assembly: AssemblyVersion("2.2.7.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PlayerSteveSuits
{
	[BepInPlugin("thewilczur.playerstevesuits", "Player Steve Suits", "2.2.7")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(StartOfRound), "Start")]
		private static class StartPatch
		{
			[HarmonyPostfix]
			[HarmonyPriority(0)]
			[HarmonyAfter(new string[] { "x753.More_Suits", "TooManySuits" })]
			private static void Postfix(StartOfRound __instance)
			{
				int num = CountPlayerSuitDefinitions(__instance);
				if (num == 0)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogError((object)"No player suit definitions were loaded. The package must contain moresuits/player1.png through player8.png.");
					}
				}
				else
				{
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogInfo((object)$"Found {num}/8 player suit definitions created by More Suits.");
					}
				}
				_lastVisibleCount = -1;
				_lastDetectedPlayerCount = -1;
				PlayerSuitObjects.Clear();
				RefreshVisibleSuits();
				Tick(force: true);
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "OnPlayerConnectedClientRpc")]
		private static class PlayerConnectedPatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				if ((Object)(object)_instance != (Object)null)
				{
					((MonoBehaviour)_instance).StartCoroutine(RefreshAfterPlayerListChange());
				}
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "OnClientDisconnectClientRpc")]
		private static class PlayerDisconnectedPatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				if ((Object)(object)_instance != (Object)null)
				{
					((MonoBehaviour)_instance).StartCoroutine(RefreshAfterPlayerListChange());
				}
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Update")]
		private static class PlayerUpdateFallbackPatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				Tick();
			}
		}

		public const string Guid = "thewilczur.playerstevesuits";

		public const string Name = "Player Steve Suits";

		public const string Version = "2.2.7";

		internal static ManualLogSource Log;

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<bool> ConnectedPlayersOnly;

		private static ConfigEntry<int> MaximumPlayerSuits;

		internal static ConfigEntry<bool> UseSynchronizedSkins;

		internal static ConfigEntry<bool> DetectActiveMinecraftAccount;

		internal static ConfigEntry<string> MinecraftProfileOverride;

		private static bool _tooManySuitsPatched;

		private static int _lastVisibleCount = -1;

		private static int _lastDetectedPlayerCount = -1;

		private static readonly List<UnlockableSuit> PlayerSuitObjects = new List<UnlockableSuit>();

		private static Plugin _instance;

		private static float _nextRefresh;

		private static int _tickFailureLogged;

		private void Awake()
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			_instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Player Suits", true, "Enables the player1-player8 suit entries and their Steve model replacements. Requires a restart.");
			ConnectedPlayersOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Suits", "Only Show Connected Players", true, "Shows only as many player suits as there are players currently in the lobby.");
			MaximumPlayerSuits = ((BaseUnityPlugin)this).Config.Bind<int>("Suits", "Maximum Player Suits", 8, new ConfigDescription("Maximum number of player suits that can be shown.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			UseSynchronizedSkins = ((BaseUnityPlugin)this).Config.Bind<bool>("Skins", "Use Synchronized Minecraft Skins", true, "Applies the corresponding player's Minecraft skin synchronized by Player Steve Suits.");
			DetectActiveMinecraftAccount = ((BaseUnityPlugin)this).Config.Bind<bool>("Skins", "Detect Active Minecraft Account", true, "Reads the active Java profile name from the official Minecraft Launcher and downloads its current skin from Mojang. Login tokens and the launcher's skin cache are not read.");
			MinecraftProfileOverride = ((BaseUnityPlugin)this).Config.Bind<string>("Skins", "Minecraft Username or UUID Override", string.Empty, "Optional local override for computers with multiple accounts or incorrect launcher data. Enter your current Minecraft Java username or UUID. Leave blank for automatic detection.");
			DetectActiveMinecraftAccount.SettingChanged += delegate
			{
				ActiveMinecraftSkinRefresh.ScheduleRefresh((MonoBehaviour)(object)this);
			};
			MinecraftProfileOverride.SettingChanged += delegate
			{
				ActiveMinecraftSkinRefresh.ScheduleRefresh((MonoBehaviour)(object)this);
			};
			EnsureMoreSuitsAssets();
			if (Enabled.Value)
			{
				for (int num = 1; num <= 8; num++)
				{
					ModelReplacementAPI.RegisterSuitModelReplacement($"player{num}", typeof(PlayerSkinSteveReplacement));
				}
			}
			Harmony val = new Harmony("thewilczur.playerstevesuits");
			val.PatchAll();
			PatchTooManySuits(val);
			ActiveMinecraftSkinRefresh.StartImmediately((MonoBehaviour)(object)this);
			((MonoBehaviour)this).StartCoroutine(MaintenanceLoop());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Player Steve Suits 2.2.7 loaded.");
		}

		private IEnumerator MaintenanceLoop()
		{
			while (true)
			{
				Tick(force: true);
				yield return (object)new WaitForSecondsRealtime(1f);
			}
		}

		private static void EnsureMoreSuitsAssets()
		{
			try
			{
				string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
				if (string.IsNullOrWhiteSpace(directoryName))
				{
					return;
				}
				string text = Path.Combine(directoryName, "moresuits");
				Directory.CreateDirectory(text);
				int num = 0;
				for (int i = 1; i <= 8; i++)
				{
					string path = $"player{i}.png";
					string text2 = Path.Combine(text, path);
					if (!File.Exists(text2))
					{
						string text3 = Path.Combine(directoryName, path);
						if (File.Exists(text3))
						{
							File.Copy(text3, text2, overwrite: false);
							num++;
						}
					}
				}
				if (num > 0)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)$"Prepared {num} player suit textures in the More Suits folder.");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)("Could not prepare the More Suits texture folder: " + ex.GetBaseException().Message));
				}
			}
		}

		private void Update()
		{
			Tick();
		}

		private static void Tick(bool force = false)
		{
			Plugin instance = _instance;
			if ((Object)(object)instance == (Object)null || (!force && Time.realtimeSinceStartup < _nextRefresh))
			{
				return;
			}
			_nextRefresh = Time.realtimeSinceStartup + 1f;
			try
			{
				RefreshVisibleSuits();
				ActiveMinecraftSkinRefresh.Update((MonoBehaviour)(object)instance);
				PlayerSkinNetwork.Update((MonoBehaviour)(object)instance);
			}
			catch (Exception ex)
			{
				if (Interlocked.Exchange(ref _tickFailureLogged, 1) == 0)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogError((object)$"Player skin maintenance failed: {ex.GetBaseException()}");
					}
				}
			}
		}

		private static IEnumerator RefreshAfterPlayerListChange()
		{
			yield return null;
			RefreshVisibleSuits();
			yield return (object)new WaitForSecondsRealtime(0.25f);
			RefreshVisibleSuits();
			MethodInfo methodInfo = AccessTools.Method(typeof(StartOfRound), "PositionSuitsOnRack", (Type[])null, (Type[])null);
			if ((Object)(object)StartOfRound.Instance != (Object)null)
			{
				methodInfo?.Invoke(StartOfRound.Instance, null);
			}
			yield return (object)new WaitForSecondsRealtime(1f);
			RefreshVisibleSuits();
		}

		private static int CountPlayerSuitDefinitions(StartOfRound round)
		{
			if (round?.unlockablesList?.unlockables == null)
			{
				return 0;
			}
			int number;
			return round.unlockablesList.unlockables.Count((UnlockableItem item) => TryPlayerNumber(Normalize(item?.unlockableName), out number));
		}

		private static void RefreshVisibleSuits()
		{
			StartOfRound instance = StartOfRound.Instance;
			if (instance?.unlockablesList?.unlockables == null)
			{
				return;
			}
			int visibleSuitCount = GetVisibleSuitCount();
			UnlockableSuit[] array = Resources.FindObjectsOfTypeAll<UnlockableSuit>();
			foreach (UnlockableSuit val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					int value;
					try
					{
						value = val.syncedSuitID.Value;
					}
					catch
					{
						continue;
					}
					if (value >= 0 && value < instance.unlockablesList.unlockables.Count && TryPlayerNumber(Normalize(instance.unlockablesList.unlockables[value]?.unlockableName), out var _) && !PlayerSuitObjects.Contains(val))
					{
						PlayerSuitObjects.Add(val);
					}
				}
			}
			PlayerSuitObjects.RemoveAll((UnlockableSuit suit) => (Object)(object)suit == (Object)null);
			array = PlayerSuitObjects.ToArray();
			foreach (UnlockableSuit val2 in array)
			{
				int value2;
				try
				{
					value2 = val2.syncedSuitID.Value;
				}
				catch
				{
					continue;
				}
				if (value2 >= 0 && value2 < instance.unlockablesList.unlockables.Count && TryPlayerNumber(Normalize(instance.unlockablesList.unlockables[value2]?.unlockableName), out var number2))
				{
					bool flag = number2 <= visibleSuitCount;
					if (!((Component)val2).gameObject.activeSelf)
					{
						((Component)val2).gameObject.SetActive(true);
					}
					Renderer[] componentsInChildren = ((Component)val2).GetComponentsInChildren<Renderer>(true);
					for (int num = 0; num < componentsInChildren.Length; num++)
					{
						componentsInChildren[num].enabled = flag;
					}
					Collider[] componentsInChildren2 = ((Component)val2).GetComponentsInChildren<Collider>(true);
					for (int num = 0; num < componentsInChildren2.Length; num++)
					{
						componentsInChildren2[num].enabled = flag;
					}
					InteractTrigger[] componentsInChildren3 = ((Component)val2).GetComponentsInChildren<InteractTrigger>(true);
					foreach (InteractTrigger obj3 in componentsInChildren3)
					{
						((Behaviour)obj3).enabled = flag;
						obj3.interactable = flag;
					}
				}
			}
			if (_lastVisibleCount != visibleSuitCount)
			{
				_lastVisibleCount = visibleSuitCount;
				NotifyTooManySuits();
			}
		}

		private static int GetVisibleSuitCount()
		{
			if (Enabled == null || !Enabled.Value)
			{
				return 0;
			}
			int num = Mathf.Clamp(MaximumPlayerSuits?.Value ?? 8, 1, 8);
			if (ConnectedPlayersOnly == null || !ConnectedPlayersOnly.Value)
			{
				return num;
			}
			StartOfRound instance = StartOfRound.Instance;
			int valueOrDefault = (instance?.ClientPlayerList?.Count).GetValueOrDefault();
			int valueOrDefault2 = (instance?.allPlayerScripts?.Count((PlayerControllerB player) => (Object)(object)player != (Object)null && player.isPlayerControlled)).GetValueOrDefault();
			int num2 = ((!((Object)(object)instance == (Object)null)) ? (instance.connectedPlayersAmount + 1) : 0);
			NetworkManager singleton = NetworkManager.Singleton;
			int valueOrDefault3 = ((singleton == null) ? ((int?)null) : singleton.ConnectedClientsIds?.Count).GetValueOrDefault();
			int val = Math.Max(Math.Max(valueOrDefault, valueOrDefault2), Math.Max(num2, valueOrDefault3));
			val = Mathf.Clamp(Math.Max(val, 1), 1, 8);
			if (_lastDetectedPlayerCount != val)
			{
				_lastDetectedPlayerCount = val;
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)($"Detected {val} lobby player(s) " + $"(client list: {valueOrDefault}, controlled: {valueOrDefault2}, " + $"joined: {num2}, network: {valueOrDefault3})."));
				}
			}
			return Math.Min(num, val);
		}

		private static bool ShouldIncludeSuit(UnlockableSuit suit)
		{
			if ((Object)(object)suit == (Object)null)
			{
				return false;
			}
			StartOfRound instance = StartOfRound.Instance;
			int value;
			try
			{
				value = suit.syncedSuitID.Value;
			}
			catch
			{
				return true;
			}
			if (instance?.unlockablesList?.unlockables == null || value < 0 || value >= instance.unlockablesList.unlockables.Count)
			{
				return true;
			}
			if (TryPlayerNumber(Normalize(instance.unlockablesList.unlockables[value]?.unlockableName), out var number))
			{
				return number <= GetVisibleSuitCount();
			}
			return true;
		}

		private static void FilterTooManySuits(ref IEnumerable<UnlockableSuit> __result)
		{
			if (__result != null)
			{
				__result = __result.Where(ShouldIncludeSuit);
			}
		}

		private static void PatchTooManySuits(Harmony harmony)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(AccessTools.TypeByName("TooManySuits.SuitManager"), "GetUnlockedSuits", (Type[])null, (Type[])null);
			if (!(methodInfo == null))
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "FilterTooManySuits", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_tooManySuitsPatched = true;
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)"TooManySuits filtering support enabled.");
				}
			}
		}

		private static void NotifyTooManySuits()
		{
			if (!_tooManySuitsPatched)
			{
				return;
			}
			try
			{
				object obj = AccessTools.PropertyGetter(AccessTools.TypeByName("TooManySuits.TooManySuits"), "SuitManager")?.Invoke(null, null);
				AccessTools.Method(obj?.GetType(), "UpdateSuits", (Type[])null, (Type[])null)?.Invoke(obj, null);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not refresh TooManySuits pages: " + ex.GetBaseException().Message));
				}
			}
		}

		internal static bool TryPlayerNumber(string name, out int number)
		{
			number = 0;
			if (name != null && name.StartsWith("player", StringComparison.OrdinalIgnoreCase) && int.TryParse(name.Substring(6), out number) && number >= 1)
			{
				return number <= 8;
			}
			return false;
		}

		internal static string Normalize(string name)
		{
			string text = (name ?? string.Empty).Trim();
			if (text.EndsWith(" suit", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - 5).Trim();
			}
			return text;
		}
	}
	internal static class ActiveMinecraftAccountPatch
	{
		private static bool _resolved;

		private static string _profileName;

		private static string _profileId;

		internal static void Prefix(ref string[] names)
		{
			if (Plugin.DetectActiveMinecraftAccount != null && Plugin.DetectActiveMinecraftAccount.Value && (names == null || !names.Any((string name) => !string.IsNullOrWhiteSpace(name))))
			{
				string activeProfileName = GetActiveProfileName();
				if (!string.IsNullOrWhiteSpace(activeProfileName))
				{
					names = new string[1] { activeProfileName };
				}
			}
		}

		internal static string GetActiveProfileName()
		{
			if (_resolved)
			{
				return _profileName;
			}
			_resolved = true;
			try
			{
				string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
				string folderPath2 = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
				string[] array = new string[3]
				{
					Path.Combine(folderPath, ".minecraft", "launcher_accounts.json"),
					Path.Combine(folderPath, ".minecraft", "launcher_accounts_microsoft_store.json"),
					Path.Combine(folderPath2, "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe", "LocalCache", "Local", "launcher_accounts.json")
				};
				string text = array.FirstOrDefault(File.Exists);
				if (text == null)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("Minecraft Launcher account file was not found. Checked: " + string.Join("; ", array)));
					}
					return null;
				}
				JObject obj = JObject.Parse(File.ReadAllText(text));
				JToken obj2 = obj["accounts"];
				JObject val = (JObject)(object)((obj2 is JObject) ? obj2 : null);
				string text2 = ((JToken)obj).Value<string>((object)"activeAccountLocalId");
				JToken obj3 = (string.IsNullOrWhiteSpace(text2) ? null : ((val != null) ? val[text2] : null));
				object profileName;
				if (obj3 == null)
				{
					profileName = null;
				}
				else
				{
					JToken obj4 = obj3[(object)"minecraftProfile"];
					profileName = ((obj4 != null) ? obj4.Value<string>((object)"name") : null);
				}
				_profileName = (string)profileName;
				object profileId;
				if (obj3 == null)
				{
					profileId = null;
				}
				else
				{
					JToken obj5 = obj3[(object)"minecraftProfile"];
					profileId = ((obj5 != null) ? obj5.Value<string>((object)"id") : null);
				}
				_profileId = (string)profileId;
				if (string.IsNullOrWhiteSpace(_profileName) && val != null)
				{
					JToken? obj6 = (from property in val.Properties()
						select property.Value).FirstOrDefault((Func<JToken, bool>)delegate(JToken account)
					{
						object value;
						if (account == null)
						{
							value = null;
						}
						else
						{
							JToken obj9 = account[(object)"minecraftProfile"];
							value = ((obj9 != null) ? obj9.Value<string>((object)"name") : null);
						}
						return !string.IsNullOrWhiteSpace((string?)value);
					});
					object profileName2;
					if (obj6 == null)
					{
						profileName2 = null;
					}
					else
					{
						JToken obj7 = obj6[(object)"minecraftProfile"];
						profileName2 = ((obj7 != null) ? obj7.Value<string>((object)"name") : null);
					}
					_profileName = (string)profileName2;
					object profileId2;
					if (obj6 == null)
					{
						profileId2 = null;
					}
					else
					{
						JToken obj8 = obj6[(object)"minecraftProfile"];
						profileId2 = ((obj8 != null) ? obj8.Value<string>((object)"id") : null);
					}
					_profileId = (string)profileId2;
				}
				if (!string.IsNullOrWhiteSpace(_profileName))
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)("Detected active Java profile '" + _profileName + "' (UUID: " + (_profileId ?? "unavailable") + ") in " + Path.GetFileName(text) + "."));
					}
				}
				else
				{
					ManualLogSource log3 = Plugin.Log;
					if (log3 != null)
					{
						log3.LogWarning((object)"No Java profile was found in Minecraft Launcher; player suits will use the default Steve texture.");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log4 = Plugin.Log;
				if (log4 != null)
				{
					log4.LogWarning((object)("Could not detect the active Minecraft Java profile: " + ex.GetBaseException().Message));
				}
			}
			return _profileName;
		}

		internal static string GetActiveProfileId()
		{
			GetActiveProfileName();
			return _profileId;
		}
	}
	internal static class ActiveMinecraftSkinRefresh
	{
		private static bool _started;

		private static float _notBefore = -1f;

		internal static void StartImmediately(MonoBehaviour runner)
		{
			_notBefore = Time.realtimeSinceStartup + 3f;
		}

		internal static void ScheduleRefresh(MonoBehaviour runner)
		{
			_started = false;
			_notBefore = Time.realtimeSinceStartup + 1f;
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)"Minecraft skin setting changed; scheduled a fresh skin lookup.");
			}
		}

		internal static void Update(MonoBehaviour runner)
		{
			if (_started || (Object)(object)runner == (Object)null)
			{
				return;
			}
			if (_notBefore < 0f)
			{
				_notBefore = Time.realtimeSinceStartup + 3f;
			}
			else
			{
				if (Time.realtimeSinceStartup < _notBefore)
				{
					return;
				}
				string text = Plugin.MinecraftProfileOverride?.Value?.Trim();
				bool flag = !string.IsNullOrWhiteSpace(text);
				if (!flag && (Plugin.DetectActiveMinecraftAccount == null || !Plugin.DetectActiveMinecraftAccount.Value))
				{
					_started = true;
					return;
				}
				string text2 = (flag ? text : ActiveMinecraftAccountPatch.GetActiveProfileName());
				string launcherProfileId = ((flag && IsUuid(text)) ? text : (flag ? null : ActiveMinecraftAccountPatch.GetActiveProfileId()));
				if (string.IsNullOrWhiteSpace(text2))
				{
					_started = true;
					return;
				}
				runner.StartCoroutine(DownloadAndApply(text2, launcherProfileId));
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)(flag ? ("Started Mojang skin lookup using the local profile override '" + text2 + "'.") : ("Started a non-destructive Mojang skin refresh for '" + text2 + "'.")));
				}
				_started = true;
			}
		}

		private static bool IsUuid(string value)
		{
			string text = (value ?? string.Empty).Replace("-", string.Empty).Trim().ToLowerInvariant();
			if (text.Length == 32)
			{
				return text.All((char character) => (character >= '0' && character <= '9') || (character >= 'a' && character <= 'f'));
			}
			return false;
		}

		private static IEnumerator DownloadAndApply(string profileName, string launcherProfileId)
		{
			string profileId = (launcherProfileId ?? string.Empty).Replace("-", string.Empty).Trim().ToLowerInvariant();
			UnityWebRequest request;
			if (profileId.Length != 32 || !profileId.All((char character) => (character >= '0' && character <= '9') || (character >= 'a' && character <= 'f')))
			{
				request = UnityWebRequest.Get("https://api.mojang.com/users/profiles/minecraft/" + Uri.EscapeDataString(profileName));
				try
				{
					request.timeout = 20;
					yield return request.SendWebRequest();
					if ((int)request.result != 1)
					{
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogWarning((object)$"Mojang profile lookup failed for '{profileName}': {request.responseCode} {request.error}. Keeping the default Steve skin.");
						}
						yield break;
					}
					profileId = ((JToken)JObject.Parse(request.downloadHandler.text)).Value<string>((object)"id");
				}
				finally
				{
					((IDisposable)request)?.Dispose();
				}
			}
			if (string.IsNullOrWhiteSpace(profileId))
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Mojang returned no UUID for '" + profileName + "'. Keeping the existing skin."));
				}
				yield break;
			}
			string textureUrl = null;
			bool slim = false;
			request = UnityWebRequest.Get("https://sessionserver.mojang.com/session/minecraft/profile/" + profileId);
			try
			{
				request.timeout = 20;
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					ManualLogSource log3 = Plugin.Log;
					if (log3 != null)
					{
						log3.LogWarning((object)$"Mojang skin metadata lookup failed for '{profileName}': {request.responseCode} {request.error}. Keeping the existing skin.");
					}
					yield break;
				}
				JToken obj = JObject.Parse(request.downloadHandler.text)["properties"];
				object obj2;
				if (obj == null)
				{
					obj2 = null;
				}
				else
				{
					JToken? obj3 = ((IEnumerable<JToken>)obj).FirstOrDefault();
					obj2 = ((obj3 != null) ? obj3.Value<string>((object)"value") : null);
				}
				string text = (string)obj2;
				if (!string.IsNullOrWhiteSpace(text))
				{
					JToken obj4 = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(text)))["textures"];
					JToken val = ((obj4 != null) ? obj4[(object)"SKIN"] : null);
					textureUrl = ((val == null) ? null : val.Value<string>((object)"url")?.Replace("http:", "https:"));
					object a;
					if (val == null)
					{
						a = null;
					}
					else
					{
						JToken obj5 = val[(object)"metadata"];
						a = ((obj5 != null) ? obj5.Value<string>((object)"model") : null);
					}
					slim = string.Equals((string?)a, "slim", StringComparison.OrdinalIgnoreCase);
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
			if (string.IsNullOrWhiteSpace(textureUrl))
			{
				ManualLogSource log4 = Plugin.Log;
				if (log4 != null)
				{
					log4.LogWarning((object)("Mojang returned no skin texture for '" + profileName + "'. Keeping the existing skin."));
				}
				yield break;
			}
			request = UnityWebRequestTexture.GetTexture(textureUrl);
			Texture2D content;
			try
			{
				request.timeout = 20;
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					ManualLogSource log5 = Plugin.Log;
					if (log5 != null)
					{
						log5.LogWarning((object)$"Skin texture download failed for '{profileName}': {request.responseCode} {request.error}. Keeping the existing skin.");
					}
					yield break;
				}
				content = DownloadHandlerTexture.GetContent(request);
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
			try
			{
				string text2 = textureUrl.Substring(textureUrl.LastIndexOf('/') + 1).Trim('/');
				content = MinecraftSkinTexture.Prepare(content);
				PlayerSkinNetwork.SetLocalSkin(text2, content, slim);
				ManualLogSource log6 = Plugin.Log;
				if (log6 != null)
				{
					log6.LogInfo((object)("Applied fresh Mojang skin for '" + profileName + "' (UUID: " + profileId + ", model: " + (slim ? "slim" : "classic") + ", texture: " + text2 + ")."));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log7 = Plugin.Log;
				if (log7 != null)
				{
					log7.LogWarning((object)("Could not apply the downloaded Minecraft skin: " + ex.GetBaseException().Message + ". Keeping the existing skin."));
				}
			}
		}
	}
	internal static class MinecraftSkinTexture
	{
		internal static Texture2D Prepare(Texture2D source)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source == (Object)null)
			{
				return null;
			}
			((Texture)source).filterMode = (FilterMode)0;
			Texture2D val = new Texture2D(((Texture)source).width * 8, ((Texture)source).height * 8, source.format, false)
			{
				filterMode = (FilterMode)0,
				wrapMode = (TextureWrapMode)1
			};
			for (int i = 0; i < ((Texture)val).height; i++)
			{
				for (int j = 0; j < ((Texture)val).width; j++)
				{
					val.SetPixel(j, i, source.GetPixel(j / 8, i / 8));
				}
			}
			val.Apply(false, false);
			Object.Destroy((Object)(object)source);
			return val;
		}
	}
	internal static class PlayerSkinNetwork
	{
		private sealed class SkinRecord
		{
			internal string TextureId;

			internal Texture2D Texture;

			internal bool Slim;

			internal bool Downloading;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__ReceiveSubmission;

			public static HandleNamedMessageDelegate <1>__ReceiveState;

			public static HandleNamedMessageDelegate <2>__ReceiveRequest;
		}

		private const string SubmitMessage = "PlayerSteveSuits.SubmitSkin.v3";

		private const string StateMessage = "PlayerSteveSuits.SkinState.v3";

		private const string RequestMessage = "PlayerSteveSuits.RequestSkinState.v3";

		private static readonly Dictionary<int, SkinRecord> Skins = new Dictionary<int, SkinRecord>();

		private static CustomMessagingManager _manager;

		private static MonoBehaviour _runner;

		private static string _localTextureId;

		private static Texture2D _localTexture;

		private static bool _localSlim;

		private static float _nextSubmit;

		private static float _nextStateSweep;

		private static bool _wasListening;

		internal static void SetLocalSkin(string textureId, Texture2D texture, bool slim)
		{
			_localTextureId = textureId;
			_localTexture = texture;
			_localSlim = slim;
			_nextSubmit = 0f;
		}

		internal static void Update(MonoBehaviour runner)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			_runner = runner;
			if (Plugin.UseSynchronizedSkins == null || !Plugin.UseSynchronizedSkins.Value)
			{
				return;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsListening || singleton.CustomMessagingManager == null)
			{
				_wasListening = false;
				return;
			}
			if (!_wasListening)
			{
				Skins.Clear();
				_nextSubmit = 0f;
				_wasListening = true;
			}
			if (_manager != singleton.CustomMessagingManager)
			{
				_manager = singleton.CustomMessagingManager;
				Skins.Clear();
				CustomMessagingManager manager = _manager;
				object obj = <>O.<0>__ReceiveSubmission;
				if (obj == null)
				{
					HandleNamedMessageDelegate val = ReceiveSubmission;
					<>O.<0>__ReceiveSubmission = val;
					obj = (object)val;
				}
				manager.RegisterNamedMessageHandler("PlayerSteveSuits.SubmitSkin.v3", (HandleNamedMessageDelegate)obj);
				CustomMessagingManager manager2 = _manager;
				object obj2 = <>O.<1>__ReceiveState;
				if (obj2 == null)
				{
					HandleNamedMessageDelegate val2 = ReceiveState;
					<>O.<1>__ReceiveState = val2;
					obj2 = (object)val2;
				}
				manager2.RegisterNamedMessageHandler("PlayerSteveSuits.SkinState.v3", (HandleNamedMessageDelegate)obj2);
				CustomMessagingManager manager3 = _manager;
				object obj3 = <>O.<2>__ReceiveRequest;
				if (obj3 == null)
				{
					HandleNamedMessageDelegate val3 = ReceiveRequest;
					<>O.<2>__ReceiveRequest = val3;
					obj3 = (object)val3;
				}
				manager3.RegisterNamedMessageHandler("PlayerSteveSuits.RequestSkinState.v3", (HandleNamedMessageDelegate)obj3);
				_nextSubmit = 0f;
				_nextStateSweep = 0f;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Player skin network synchronization is ready.");
				}
				if (!singleton.IsServer)
				{
					RequestState();
				}
			}
			if ((Object)(object)_localTexture == (Object)null || !IsTextureId(_localTextureId))
			{
				return;
			}
			int localPlayerSlot = GetLocalPlayerSlot();
			if (localPlayerSlot < 0)
			{
				return;
			}
			Skins[localPlayerSlot] = new SkinRecord
			{
				TextureId = _localTextureId,
				Texture = _localTexture,
				Slim = _localSlim
			};
			if (Time.realtimeSinceStartup >= _nextSubmit)
			{
				_nextSubmit = Time.realtimeSinceStartup + 5f;
				if (singleton.IsServer)
				{
					BroadcastState(localPlayerSlot, _localTextureId, _localSlim);
				}
				else
				{
					SendSubmission(localPlayerSlot, _localTextureId, _localSlim);
				}
			}
			if (singleton.IsServer && Time.realtimeSinceStartup >= _nextStateSweep)
			{
				_nextStateSweep = Time.realtimeSinceStartup + 5f;
				BroadcastAllKnownSkins();
			}
		}

		internal static bool TryGet(int playerSlot, out Texture2D texture, out bool slim, out string textureId)
		{
			if (Skins.TryGetValue(playerSlot, out var value) && (Object)(object)value.Texture != (Object)null)
			{
				texture = value.Texture;
				slim = value.Slim;
				textureId = value.TextureId;
				return true;
			}
			texture = null;
			slim = false;
			textureId = null;
			return false;
		}

		private static int GetLocalPlayerSlot()
		{
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts;
			if ((Object)(object)val == (Object)null || array == null)
			{
				return -1;
			}
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] == val)
				{
					return i;
				}
			}
			int num = (int)val.playerClientId;
			if (num < 0 || num >= array.Length)
			{
				return -1;
			}
			return num;
		}

		private unsafe static void SendSubmission(int playerSlot, string textureId, bool slim)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref playerSlot, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe(textureId, false);
				((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref slim, default(ForPrimitives));
				_manager.SendNamedMessage("PlayerSteveSuits.SubmitSkin.v3", 0uL, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void ReceiveSubmission(ulong senderClientId, FastBufferReader reader)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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)
			NetworkManager singleton = NetworkManager.Singleton;
			if (!((Object)(object)singleton == (Object)null) && singleton.IsServer)
			{
				int playerSlot = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref playerSlot, default(ForPrimitives));
				string text = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
				bool slim = default(bool);
				((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref slim, default(ForPrimitives));
				if (IsTextureId(text) && SenderOwnsPlayerSlot(senderClientId, playerSlot))
				{
					AcceptRemote(playerSlot, text, slim);
					BroadcastState(playerSlot, text, slim);
				}
			}
		}

		private unsafe static void RequestState()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (_manager == null)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(1, (Allocator)2, -1);
			try
			{
				_manager.SendNamedMessage("PlayerSteveSuits.RequestSkinState.v3", 0uL, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void ReceiveRequest(ulong senderClientId, FastBufferReader reader)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (!((Object)(object)singleton == (Object)null) && singleton.IsServer)
			{
				BroadcastAllKnownSkins(senderClientId);
			}
		}

		private unsafe static void BroadcastAllKnownSkins(ulong? targetClientId = null)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer || _manager == null)
			{
				return;
			}
			KeyValuePair<int, SkinRecord>[] array = Skins.ToArray();
			FastBufferWriter val = default(FastBufferWriter);
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<int, SkinRecord> keyValuePair = array[i];
				SkinRecord value = keyValuePair.Value;
				if (value == null || !IsTextureId(value.TextureId))
				{
					continue;
				}
				((FastBufferWriter)(ref val))..ctor(272, (Allocator)2, -1);
				try
				{
					int key = keyValuePair.Key;
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref key, default(ForPrimitives));
					((FastBufferWriter)(ref val)).WriteValueSafe(value.TextureId, false);
					((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref value.Slim, default(ForPrimitives));
					if (targetClientId.HasValue)
					{
						_manager.SendNamedMessage("PlayerSteveSuits.SkinState.v3", targetClientId.Value, val, (NetworkDelivery)3);
					}
					else
					{
						_manager.SendNamedMessageToAll("PlayerSteveSuits.SkinState.v3", val, (NetworkDelivery)3);
					}
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
			}
		}

		private unsafe static void BroadcastState(int playerSlot, string textureId, bool slim)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer || _manager == null)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(272, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref playerSlot, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe(textureId, false);
				((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref slim, default(ForPrimitives));
				_manager.SendNamedMessageToAll("PlayerSteveSuits.SkinState.v3", val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void ReceiveState(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)NetworkManager.Singleton == (Object)null) && senderClientId == 0L)
			{
				int num = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
				string text = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
				bool slim = default(bool);
				((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref slim, default(ForPrimitives));
				if (IsTextureId(text) && num != GetLocalPlayerSlot())
				{
					AcceptRemote(num, text, slim);
				}
			}
		}

		private static bool SenderOwnsPlayerSlot(ulong senderClientId, int playerSlot)
		{
			PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts;
			if (array != null && playerSlot >= 0 && playerSlot < array.Length && (Object)(object)array[playerSlot] != (Object)null)
			{
				return array[playerSlot].actualClientId == senderClientId;
			}
			return false;
		}

		private static void AcceptRemote(int playerSlot, string textureId, bool slim)
		{
			if (!Skins.TryGetValue(playerSlot, out var value) || !(value.TextureId == textureId) || (!((Object)(object)value.Texture != (Object)null) && !value.Downloading))
			{
				SkinRecord skinRecord = new SkinRecord
				{
					TextureId = textureId,
					Slim = slim,
					Downloading = true
				};
				Skins[playerSlot] = skinRecord;
				if ((Object)(object)_runner != (Object)null)
				{
					_runner.StartCoroutine(DownloadRemote(playerSlot, skinRecord));
				}
			}
		}

		private static IEnumerator DownloadRemote(int playerSlot, SkinRecord expected)
		{
			string text = "https://textures.minecraft.net/texture/" + expected.TextureId;
			UnityWebRequest request = UnityWebRequestTexture.GetTexture(text);
			try
			{
				yield return request.SendWebRequest();
				SkinRecord value;
				if ((int)request.result != 1)
				{
					expected.Downloading = false;
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)$"Could not download Minecraft skin for player{playerSlot + 1}: {request.responseCode} {request.error}");
					}
				}
				else if (Skins.TryGetValue(playerSlot, out value) && value == expected)
				{
					value.Texture = MinecraftSkinTexture.Prepare(DownloadHandlerTexture.GetContent(request));
					value.Downloading = false;
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)string.Format("Downloaded synchronized Minecraft skin for player{0} (model: {1}, texture: {2}).", playerSlot + 1, value.Slim ? "slim" : "classic", value.TextureId));
					}
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private static bool IsTextureId(string value)
		{
			if (value != null && value.Length == 64)
			{
				return value.All((char character) => (character >= '0' && character <= '9') || (character >= 'a' && character <= 'f'));
			}
			return false;
		}
	}
	public sealed class PlayerSkinSteveReplacement : MRSTEVE
	{
		private static readonly FieldInfo ReplacementModelField = AccessTools.Field(typeof(BodyReplacementBase), "replacementModel");

		protected override GameObject LoadAssetsAndReturnModel()
		{
			GameObject val = ((MRSTEVE)this).LoadAssetsAndReturnModel();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val);
			((Object)val2).name = ((Object)val).name + " (PlayerSteveSuits isolated prefab)";
			Component[] componentsInChildren = val2.GetComponentsInChildren<Component>(true);
			foreach (Component val3 in componentsInChildren)
			{
				if (!((Object)(object)val3 == (Object)null) && !(((object)val3).GetType().FullName != "ModelReplacement.AvatarBodyUpdater.OffsetBuilder"))
				{
					FieldInfo fieldInfo = AccessTools.Field(((object)val3).GetType(), "GenerateViewModel");
					if (fieldInfo != null)
					{
						fieldInfo.SetValue(val3, true);
					}
					PropertyInfo propertyInfo = AccessTools.Property(((object)val3).GetType(), "GenerateViewModel");
					if ((object)propertyInfo != null && propertyInfo.CanWrite)
					{
						propertyInfo.SetValue(val3, true, null);
					}
				}
			}
			Object.Destroy((Object)(object)val2);
			return val2;
		}

		protected override void AddModelScripts()
		{
			((BodyReplacementBase)this).AddModelScripts();
			object? obj = ReplacementModelField?.GetValue(this);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			if (!((Object)(object)val == (Object)null))
			{
				PlayerSkinApplicator playerSkinApplicator = val.AddComponent<PlayerSkinApplicator>();
				playerSkinApplicator.Owner = ((BodyReplacementBase)this).controller;
				playerSkinApplicator.SuitName = ((BodyReplacementBase)this).suitName;
				playerSkinApplicator.ReplacementOwner = (BodyReplacementBase)(object)this;
			}
		}
	}
	public sealed class PlayerSkinApplicator : MonoBehaviour
	{
		internal PlayerControllerB Owner;

		internal string SuitName;

		internal BodyReplacementBase ReplacementOwner;

		private static readonly FieldInfo ReplacementViewModelField = AccessTools.Field(typeof(BodyReplacementBase), "replacementViewModel");

		private Texture _lastTexture;

		private string _lastTextureId;

		private float _nextAttempt;

		private bool _loggedGeometry;

		private GameObject _lastViewModel;

		private void Update()
		{
			ActiveMinecraftSkinRefresh.Update((MonoBehaviour)(object)this);
			PlayerSkinNetwork.Update((MonoBehaviour)(object)this);
			if (Plugin.UseSynchronizedSkins == null || !Plugin.UseSynchronizedSkins.Value || Time.realtimeSinceStartup < _nextAttempt)
			{
				return;
			}
			_nextAttempt = Time.realtimeSinceStartup + 0.5f;
			string name = Plugin.Normalize(SuitName);
			if (!Plugin.TryPlayerNumber(name, out var number))
			{
				int num = (((Object)(object)Owner == (Object)null) ? (-1) : Owner.currentSuitID);
				List<UnlockableItem> list = StartOfRound.Instance?.unlockablesList?.unlockables;
				if (list == null || num < 0 || num >= list.Count || !Plugin.TryPlayerNumber(Plugin.Normalize(list[num].unlockableName), out number))
				{
					return;
				}
			}
			name = $"player{number}";
			PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts;
			if (array == null || number > array.Length || (Object)(object)array[number - 1] == (Object)null)
			{
				return;
			}
			int num2 = number - 1;
			if (!PlayerSkinNetwork.TryGet(num2, out var texture, out var slim, out var textureId) || (Object)(object)texture == (Object)null)
			{
				return;
			}
			object? obj = ReplacementViewModelField?.GetValue(ReplacementOwner);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			bool flag = (Object)(object)texture != (Object)(object)_lastTexture;
			bool flag2 = (Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)_lastViewModel;
			if (!flag && !flag2)
			{
				return;
			}
			if (flag)
			{
				ApplyTextureAndGeometry(((Component)this).gameObject, texture, slim);
			}
			if ((Object)(object)val != (Object)null)
			{
				ApplyTextureAndGeometry(val, texture, slim);
			}
			_lastTexture = (Texture)(object)texture;
			_lastViewModel = val;
			if (_lastTextureId != textureId)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)string.Format("Applied {0} skin from player slot {1} (model: {2}, texture: {3}).", name, num2, slim ? "slim" : "classic", textureId));
				}
				_lastTextureId = textureId;
			}
		}

		private void ApplyTextureAndGeometry(GameObject root, Texture2D texture, bool slim)
		{
			SkinnedMeshRenderer[] componentsInChildren = root.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			SkinnedMeshRenderer[] array = componentsInChildren;
			foreach (SkinnedMeshRenderer val in array)
			{
				if ((Object)(object)val != (Object)null)
				{
					((Renderer)val).material.mainTexture = (Texture)(object)texture;
				}
			}
			ApplySlimGeometry(componentsInChildren, slim);
		}

		private void ApplySlimGeometry(SkinnedMeshRenderer[] renderers, bool slim)
		{
			SkinnedMeshRenderer val = null;
			int num = -1;
			int num2 = int.MinValue;
			foreach (SkinnedMeshRenderer val2 in renderers)
			{
				Mesh val3 = ((val2 != null) ? val2.sharedMesh : null);
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				for (int j = 0; j < val3.blendShapeCount; j++)
				{
					string text = val3.GetBlendShapeName(j) ?? string.Empty;
					int num3 = val3.vertexCount;
					if (text.IndexOf("slim", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("alex", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						num3 += 1000000;
					}
					else if (text.IndexOf("arm", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						num3 += 500000;
					}
					if (num3 > num2)
					{
						num2 = num3;
						val = val2;
						num = j;
					}
					val2.SetBlendShapeWeight(j, 0f);
				}
			}
			bool flag = (Object)(object)val != (Object)null && num >= 0 && num2 >= 500000;
			if (slim && flag)
			{
				val.SetBlendShapeWeight(num, 100f);
			}
			if (!_loggedGeometry)
			{
				string text2 = ((!flag || (Object)(object)((val != null) ? val.sharedMesh : null) == (Object)null) ? "none" : (((Object)val.sharedMesh).name + "/" + val.sharedMesh.GetBlendShapeName(num)));
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("Minecraft geometry for " + Plugin.Normalize(SuitName) + ": skin=" + (slim ? "slim" : "classic") + ", dedicated slim blend shape=" + text2 + "."));
				}
				_loggedGeometry = true;
			}
		}
	}
}