Decompiled source of HornetCloakColor v1.7.3

HornetCloakColor.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HornetCloakColor.Client;
using HornetCloakColor.Shared;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[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("HornetCloakColor")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.7.3.0")]
[assembly: AssemblyInformationalVersion("1.7.3+721a346d2be5cba92c78662e0edfe9b430b63075")]
[assembly: AssemblyProduct("HornetCloakColor")]
[assembly: AssemblyTitle("HornetCloakColor")]
[assembly: InternalsVisibleTo("HornetCloakColor.SSMP")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.7.3.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace HornetCloakColor
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("hornet.cloak.color", "HornetCloakColor", "1.7.3")]
	public class HornetCloakColorPlugin : BaseUnityPlugin
	{
		public const string ModVersion = "1.7.3";

		public const string Id = "hornet.cloak.color";

		internal static HornetCloakColorPlugin? Instance { get; private set; }

		internal static ManualLogSource? LogSource { get; private set; }

		internal CloakColorConfig ColorConfig { get; private set; }

		public static string Name => "HornetCloakColor";

		public static string Version => "1.7.3";

		private void Awake()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			Instance = this;
			LogSource = ((BaseUnityPlugin)this).Logger;
			CloakPaletteConfig.Load();
			CloakSceneScanner.EnsureCreated();
			ColorConfig = new CloakColorConfig(((BaseUnityPlugin)this).Config);
			CloakColorApplier.SetLocalSceneColor(ColorConfig.CurrentColor);
			MapMaskHarmonyPatcher.Apply();
			if (SSMPBridge.TryRegister())
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"SSMP detected — multiplayer cloak sync enabled.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"SSMP not detected — running solo (your cloak only).");
			}
			ColorConfig.ColorChanged += OnConfigColorChanged;
			HeroController.OnHeroInstanceSet += new HeroSetDelegate(OnHeroInstanceSet);
			((BaseUnityPlugin)this).Logger.LogInfo((object)(Name + " v1.7.3 loaded."));
		}

		private void OnHeroInstanceSet(HeroController hero)
		{
			CloakColor currentColor = ColorConfig.CurrentColor;
			CloakColorApplier.Apply(((Component)hero).gameObject, currentColor);
			CloakColorApplier.SetLocalSceneColor(currentColor);
			SSMPBridge.NotifyLocalColorChanged(currentColor);
		}

		private void OnConfigColorChanged(CloakColor color)
		{
			if (CloakPaletteConfig.DebugLogging)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Cloak color changed to {color}");
			}
			if ((Object)(object)HeroController.SilentInstance != (Object)null)
			{
				CloakColorApplier.Apply(((Component)HeroController.SilentInstance).gameObject, color);
			}
			CloakColorApplier.SetLocalSceneColor(color);
			SSMPBridge.NotifyLocalColorChanged(color);
			LocalMapMaskTint.Refresh(GameManager.instance?.gameMap, color);
			MapMaskTint.BroadcastLocalColor(color);
		}
	}
	internal static class SSMPBridge
	{
		public const string SSMPGuid = "ssmp";

		private static bool _registered;

		private static Action<CloakColor>? _notifyColorChanged;

		private static Func<ushort, CloakColor>? _getRemoteMapColor;

		public static bool IsAvailable => Chainloader.PluginInfos.ContainsKey("ssmp");

		public static bool IsRegistered => _registered;

		public static bool TryRegister()
		{
			if (_registered)
			{
				return true;
			}
			if (!IsAvailable)
			{
				return false;
			}
			try
			{
				if (!TryBindSatellite())
				{
					return false;
				}
				_registered = true;
				return true;
			}
			catch (Exception ex)
			{
				Log.Warn("SSMP detected but addon registration failed: " + ex.Message + ". Multiplayer cloak sync disabled; local recolor still works.");
				return false;
			}
		}

		public static void NotifyLocalColorChanged(CloakColor color)
		{
			if (_registered)
			{
				_notifyColorChanged?.Invoke(color);
			}
		}

		public static CloakColor GetRemoteMapColorOrDefault(ushort playerId)
		{
			return _getRemoteMapColor?.Invoke(playerId) ?? CloakColor.Default;
		}

		private static bool TryBindSatellite()
		{
			string directoryName = Path.GetDirectoryName(typeof(HornetCloakColorPlugin).Assembly.Location);
			if (string.IsNullOrEmpty(directoryName))
			{
				Log.Warn("Could not resolve plugin directory; cannot load HornetCloakColor.SSMP.dll.");
				return false;
			}
			string text = Path.Combine(directoryName, "HornetCloakColor.SSMP.dll");
			if (!File.Exists(text))
			{
				Log.Warn("SSMP is loaded but HornetCloakColor.SSMP.dll was not found next to HornetCloakColor.dll. Reinstall the mod so both DLLs are in the same folder.");
				return false;
			}
			Type type = Assembly.LoadFrom(text).GetType("HornetCloakColor.SSMPIntegration.SatelliteEntry");
			if (type == null)
			{
				Log.Warn("HornetCloakColor.SSMP.dll is missing the integration entry type.");
				return false;
			}
			type.GetMethod("Register", BindingFlags.Static | BindingFlags.Public)?.Invoke(null, null);
			MethodInfo method = type.GetMethod("NotifyLocalColorChanged", BindingFlags.Static | BindingFlags.Public);
			if (method != null)
			{
				_notifyColorChanged = (Action<CloakColor>)Delegate.CreateDelegate(typeof(Action<CloakColor>), method);
			}
			MethodInfo method2 = type.GetMethod("GetRemoteMapColorOrDefault", BindingFlags.Static | BindingFlags.Public);
			if (method2 != null)
			{
				_getRemoteMapColor = (Func<ushort, CloakColor>)Delegate.CreateDelegate(typeof(Func<ushort, CloakColor>), method2);
			}
			return true;
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "hornet.cloak.color";

		public const string PLUGIN_NAME = "HornetCloakColor";

		public const string PLUGIN_VERSION = "1.7.3";
	}
}
namespace HornetCloakColor.Shared
{
	public readonly struct CloakColor : IEquatable<CloakColor>
	{
		public byte R { get; }

		public byte G { get; }

		public byte B { get; }

		public static CloakColor Default => new CloakColor(byte.MaxValue, byte.MaxValue, byte.MaxValue);

		public CloakColor(byte r, byte g, byte b)
		{
			R = r;
			G = g;
			B = b;
		}

		public Color ToUnityColor()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)(int)R / 255f, (float)(int)G / 255f, (float)(int)B / 255f, 1f);
		}

		public void ToHSV(out float h, out float s, out float v)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Color.RGBToHSV(ToUnityColor(), ref h, ref s, ref v);
		}

		public static CloakColor FromUnityColor(Color color)
		{
			//IL_0000: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			return new CloakColor((byte)Mathf.Clamp(Mathf.RoundToInt(color.r * 255f), 0, 255), (byte)Mathf.Clamp(Mathf.RoundToInt(color.g * 255f), 0, 255), (byte)Mathf.Clamp(Mathf.RoundToInt(color.b * 255f), 0, 255));
		}

		public static bool TryParse(string value, out CloakColor color)
		{
			color = Default;
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			string text = value.Trim();
			if (text.Contains(","))
			{
				string[] array = text.Split(',');
				if (array.Length != 3)
				{
					return false;
				}
				if (!byte.TryParse(array[0].Trim(), out var result))
				{
					return false;
				}
				if (!byte.TryParse(array[1].Trim(), out var result2))
				{
					return false;
				}
				if (!byte.TryParse(array[2].Trim(), out var result3))
				{
					return false;
				}
				color = new CloakColor(result, result2, result3);
				return true;
			}
			string text2 = (text.StartsWith("#") ? text.Substring(1) : text);
			if (text2.Length != 6)
			{
				return false;
			}
			try
			{
				byte r = Convert.ToByte(text2.Substring(0, 2), 16);
				byte g = Convert.ToByte(text2.Substring(2, 2), 16);
				byte b = Convert.ToByte(text2.Substring(4, 2), 16);
				color = new CloakColor(r, g, b);
				return true;
			}
			catch
			{
				return false;
			}
		}

		public override string ToString()
		{
			return $"#{R:X2}{G:X2}{B:X2}";
		}

		public bool Equals(CloakColor other)
		{
			if (R == other.R && G == other.G)
			{
				return B == other.B;
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj is CloakColor other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (R << 16) | (G << 8) | B;
		}

		public static bool operator ==(CloakColor a, CloakColor b)
		{
			return a.Equals(b);
		}

		public static bool operator !=(CloakColor a, CloakColor b)
		{
			return !a.Equals(b);
		}
	}
	internal static class Log
	{
		private static ManualLogSource? _fallback;

		private static ManualLogSource Source => HornetCloakColorPlugin.LogSource ?? _fallback ?? (_fallback = Logger.CreateLogSource("HornetCloakColor"));

		public static void Info(string msg)
		{
			Source.LogInfo((object)msg);
		}

		public static void Warn(string msg)
		{
			Source.LogWarning((object)msg);
		}

		public static void Error(string msg)
		{
			Source.LogError((object)msg);
		}

		public static void Debug(string msg)
		{
			Source.LogDebug((object)msg);
		}
	}
}
namespace HornetCloakColor.Client
{
	internal static class CloakColorApplier
	{
		public static void Apply(GameObject? playerObject, CloakColor color)
		{
			if (!((Object)(object)playerObject == (Object)null))
			{
				bool useCloakShader = (Object)(object)CloakShaderManager.Shader != (Object)null;
				CloakRecolor.AttachOrUpdate(playerObject, color, useCloakShader);
			}
		}

		public static void SetLocalSceneColor(CloakColor color)
		{
			CloakSceneScanner.SetColor(color);
		}
	}
	internal class CloakColorConfig
	{
		public enum Preset
		{
			Custom,
			Default,
			Crimson,
			Scarlet,
			Amber,
			Gold,
			Emerald,
			Teal,
			Azure,
			Royal,
			Violet,
			Magenta,
			Obsidian,
			Ivory
		}

		private static readonly CloakColor DefaultCustom = new CloakColor(200, 60, 60);

		public ConfigEntry<Preset> PresetChoice { get; }

		public ConfigEntry<string> CustomHex { get; }

		public CloakColor CurrentColor => Resolve(PresetChoice.Value, CustomHex.Value);

		public event Action<CloakColor>? ColorChanged;

		public CloakColorConfig(ConfigFile config)
		{
			PresetChoice = config.Bind<Preset>("Appearance", "Cloak Color Preset", Preset.Default, "Choose 'Custom' for your own hex.");
			CustomHex = config.Bind<string>("Appearance", "Custom Cloak Color", DefaultCustom.ToString(), "Custom cloak color used when preset is set to 'Custom'. Accepts #RRGGBB, RRGGBB, or 'r,g,b' (0-255 each).");
			PresetChoice.SettingChanged += delegate
			{
				this.ColorChanged?.Invoke(CurrentColor);
			};
			CustomHex.SettingChanged += delegate
			{
				this.ColorChanged?.Invoke(CurrentColor);
			};
		}

		private static CloakColor Resolve(Preset preset, string customHex)
		{
			CloakColor color;
			return preset switch
			{
				Preset.Default => CloakColor.Default, 
				Preset.Crimson => new CloakColor(156, 36, 48), 
				Preset.Scarlet => new CloakColor(220, 56, 72), 
				Preset.Amber => new CloakColor(230, 140, 40), 
				Preset.Gold => new CloakColor(232, 190, 64), 
				Preset.Emerald => new CloakColor(56, 170, 90), 
				Preset.Teal => new CloakColor(60, 180, 180), 
				Preset.Azure => new CloakColor(64, 148, 230), 
				Preset.Royal => new CloakColor(72, 92, 210), 
				Preset.Violet => new CloakColor(140, 80, 210), 
				Preset.Magenta => new CloakColor(220, 80, 180), 
				Preset.Obsidian => new CloakColor(40, 40, 55), 
				Preset.Ivory => new CloakColor(240, 230, 205), 
				Preset.Custom => CloakColor.TryParse(customHex, out color) ? color : CloakColor.Default, 
				_ => CloakColor.Default, 
			};
		}
	}
	internal static class CloakMaterialApplier
	{
		public static void Apply(MeshRenderer renderer, tk2dSprite? sprite, CloakColor color, bool useCloakShader, Dictionary<MeshRenderer, Shader> originalShaderByRenderer)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				return;
			}
			Material material = ((Renderer)renderer).material;
			if (!((Object)(object)material == (Object)null))
			{
				if (sprite == null)
				{
					sprite = ((Component)renderer).GetComponent<tk2dSprite>();
				}
				if (useCloakShader && (Object)(object)CloakShaderManager.Shader != (Object)null)
				{
					EnsureCloakShader(renderer, material, originalShaderByRenderer);
					ApplyShaderProperties(material, sprite, color);
				}
				else
				{
					RestoreOriginalShader(renderer, material, originalShaderByRenderer);
					ApplyVertexTint(material, sprite, color);
				}
			}
		}

		private static void EnsureCloakShader(MeshRenderer renderer, Material mat, Dictionary<MeshRenderer, Shader> map)
		{
			Shader shader = CloakShaderManager.Shader;
			if (!((Object)(object)mat.shader == (Object)(object)shader))
			{
				if (!map.ContainsKey(renderer))
				{
					map[renderer] = mat.shader;
				}
				Texture mainTexture = mat.mainTexture;
				mat.shader = shader;
				if ((Object)(object)mainTexture != (Object)null)
				{
					mat.mainTexture = mainTexture;
				}
			}
		}

		private static void RestoreOriginalShader(MeshRenderer renderer, Material mat, Dictionary<MeshRenderer, Shader> map)
		{
			if (map.TryGetValue(renderer, out Shader value) && !((Object)(object)value == (Object)null) && !((Object)(object)mat.shader == (Object)(object)value))
			{
				Texture mainTexture = mat.mainTexture;
				mat.shader = value;
				if ((Object)(object)mainTexture != (Object)null)
				{
					mat.mainTexture = mainTexture;
				}
			}
		}

		private static void ApplyShaderProperties(Material mat, tk2dSprite? sprite, CloakColor color)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)sprite != (Object)null && ((tk2dBaseSprite)sprite).color != Color.white)
			{
				((tk2dBaseSprite)sprite).color = Color.white;
			}
			mat.SetVectorArray(CloakShaderManager.SrcColorsId, CloakPaletteConfig.SrcColors);
			mat.SetVectorArray(CloakShaderManager.AvoidColorsId, CloakPaletteConfig.AvoidColors);
			mat.SetFloat(CloakShaderManager.MatchRadiusId, CloakPaletteConfig.MatchRadius);
			mat.SetFloat(CloakShaderManager.AvoidMatchRadiusId, CloakPaletteConfig.AvoidMatchRadius);
			if (color.Equals(CloakColor.Default))
			{
				mat.SetFloat(CloakShaderManager.StrengthId, 0f);
				return;
			}
			color.ToHSV(out var h, out var s, out var v);
			mat.SetFloat(CloakShaderManager.TargetHueId, h);
			mat.SetFloat(CloakShaderManager.TargetSatId, (s <= 0.001f) ? 0f : 1f);
			mat.SetFloat(CloakShaderManager.TargetValId, Mathf.Lerp(0.6f, 1.4f, v));
			mat.SetFloat(CloakShaderManager.StrengthId, 1f);
		}

		private static void ApplyVertexTint(Material mat, tk2dSprite? sprite, CloakColor color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			Color color2 = color.ToUnityColor();
			if ((Object)(object)sprite != (Object)null)
			{
				((tk2dBaseSprite)sprite).color = color2;
			}
			mat.color = color2;
		}

		public static void PruneDestroyed(Dictionary<MeshRenderer, Shader> map)
		{
			if (map.Count == 0)
			{
				return;
			}
			List<MeshRenderer> list = null;
			foreach (MeshRenderer key in map.Keys)
			{
				if (!Object.op_Implicit((Object)(object)key))
				{
					(list ?? (list = new List<MeshRenderer>())).Add(key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (MeshRenderer item in list)
			{
				map.Remove(item);
			}
		}
	}
	internal static class CloakPaletteConfig
	{
		public static Vector4[] SrcColors { get; private set; } = (Vector4[])(object)new Vector4[16];


		public static int SrcCount { get; private set; }

		public static Vector4[] AvoidColors { get; private set; } = (Vector4[])(object)new Vector4[16];


		public static int AvoidCount { get; private set; }

		public static float MatchRadius { get; private set; }

		public static float AvoidMatchRadius { get; private set; }

		public static bool DebugLogging { get; private set; }

		public static bool MapIconDebugLogging { get; private set; }

		public static bool LogMapIconDiagnostics
		{
			get
			{
				if (!DebugLogging)
				{
					return MapIconDebugLogging;
				}
				return true;
			}
		}

		public static bool PerfDiagnostics { get; private set; }

		public static string[] SceneScanTextureContains { get; private set; } = Array.Empty<string>();


		public static string[] SceneScanPathContains { get; private set; } = Array.Empty<string>();


		public static int SceneScanIntervalFrames { get; private set; }

		public static string[] SceneScanRegistryDenyPathContains { get; private set; } = Array.Empty<string>();


		public static int HeroMeshRescanIntervalFrames { get; private set; }

		public static bool DumpDiscoveredTextures { get; private set; }

		public static void Load()
		{
			ApplyDefaults();
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (string.IsNullOrEmpty(directoryName))
			{
				return;
			}
			string text = Path.Combine(directoryName, "cloak_palette.json");
			if (!File.Exists(text))
			{
				return;
			}
			try
			{
				if (TryApplyPaletteJson(File.ReadAllText(text)))
				{
					Log.Info($"Loaded cloak palette from {text} ({SrcCount} cloak / {AvoidCount} avoid reference color(s)).");
					if (MapIconDebugLogging)
					{
						Log.Info("[MapIcon] mapIconDebugLogging is true — tracing map/compass sync; grep log for \"[MapIcon]\".");
					}
					if (PerfDiagnostics)
					{
						Log.Info("[HCC/Perf] perfDiagnostics is true — grep BepInEx log for \"[HCC/Perf]\" (≈2s rolling window).");
					}
				}
				else
				{
					Log.Warn("cloak_palette.json was not valid; using built-in defaults from the mod DLL.");
				}
			}
			catch (Exception ex)
			{
				Log.Warn("Could not read cloak_palette.json: " + ex.Message + ". Using defaults.");
			}
		}

		private static void ApplyDefaults()
		{
			SetSources(ParseHexColors("#79404b", "#d4b8b8", "#7a414c", "#b2808c", "#351c20", "#501f3b", "#562d35", "#3b162b", "#ec7f92", "#955a70", "#efbdd1", "#ae5c6c", "#994d5c", "#a7807b", "#be485e", "#592439"));
			SetAvoidSources(ParseHexColors("#ffffff", "#000000", "#c7cbb5", "#16162c", "#808080", "#5b4133", "#231914", "#644662", "#282c34", "#8a6187"));
			MatchRadius = 0.135f;
			AvoidMatchRadius = 0.12f;
			DebugLogging = false;
			MapIconDebugLogging = false;
			SceneScanTextureContains = new string[1] { "hornet" };
			SceneScanPathContains = new string[1] { "hornet" };
			SceneScanIntervalFrames = 3;
			SceneScanRegistryDenyPathContains = new string[9] { "SpriteCache", "EnemyHitEffects", "Slash Impact", "Hero Dash Puff", "Land Effect", "/HudCamera/", "Thunk", "Warrior Rage", "Barbed Wire" };
			HeroMeshRescanIntervalFrames = 30;
			DumpDiscoveredTextures = false;
			PerfDiagnostics = false;
		}

		private static bool TryApplyPaletteJson(string json)
		{
			if (string.IsNullOrWhiteSpace(json))
			{
				return false;
			}
			string text = json.TrimStart();
			if (!text.StartsWith("{", StringComparison.Ordinal))
			{
				return false;
			}
			List<CloakColor> list = ExtractHexArray(text, "cloakColors");
			if (list.Count > 0)
			{
				SetSources(list);
			}
			if (TryExtractFloat(text, "matchRadius", out var value) && value > 0f && value <= 1f)
			{
				MatchRadius = value;
			}
			SetAvoidSources(ExtractHexArray(text, "avoidColors"));
			if (TryExtractFloat(text, "avoidMatchRadius", out var value2) && value2 > 0f && value2 <= 1f)
			{
				AvoidMatchRadius = value2;
			}
			else
			{
				AvoidMatchRadius = MatchRadius;
			}
			if (TryExtractBool(text, "debugLogging", out var value3))
			{
				DebugLogging = value3;
			}
			if (TryExtractBool(text, "mapIconDebugLogging", out var value4))
			{
				MapIconDebugLogging = value4;
			}
			List<string> list2 = ExtractStringArray(text, "sceneScanTextureContains");
			if (list2.Count > 0)
			{
				SceneScanTextureContains = list2.ToArray();
			}
			List<string> list3 = ExtractStringArray(text, "sceneScanPathContains");
			if (list3.Count > 0)
			{
				SceneScanPathContains = list3.ToArray();
			}
			if (TryExtractInt(text, "sceneScanIntervalFrames", out var value5) && value5 > 0 && value5 <= 240)
			{
				SceneScanIntervalFrames = value5;
			}
			List<string> list4 = ExtractStringArray(text, "sceneScanRegistryDenyPathContains");
			if (list4.Count > 0)
			{
				SceneScanRegistryDenyPathContains = list4.ToArray();
			}
			if (TryExtractInt(text, "heroMeshRescanIntervalFrames", out var value6) && value6 > 0 && value6 <= 600)
			{
				HeroMeshRescanIntervalFrames = value6;
			}
			if (TryExtractBool(text, "dumpDiscoveredTextures", out var value7))
			{
				DumpDiscoveredTextures = value7;
			}
			if (TryExtractBool(text, "perfDiagnostics", out var value8))
			{
				PerfDiagnostics = value8;
			}
			return true;
		}

		private static List<string> ExtractStringArray(string json, string key)
		{
			List<string> list = new List<string>();
			string pattern = "\"" + Regex.Escape(key) + "\"\\s*:\\s*\\[(?<arr>[^\\]]*)\\]";
			Match match = Regex.Match(json, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
			if (!match.Success)
			{
				return list;
			}
			foreach (Match item in Regex.Matches(match.Groups["arr"].Value, "\"(?<s>[^\"]*)\"", RegexOptions.CultureInvariant))
			{
				string value = item.Groups["s"].Value;
				if (!string.IsNullOrWhiteSpace(value))
				{
					list.Add(value);
				}
			}
			return list;
		}

		private static bool TryExtractInt(string json, string key, out int value)
		{
			value = 0;
			string pattern = "\"" + Regex.Escape(key) + "\"\\s*:\\s*(?<n>-?[0-9]+)";
			Match match = Regex.Match(json, pattern, RegexOptions.CultureInvariant);
			if (!match.Success)
			{
				return false;
			}
			return int.TryParse(match.Groups["n"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
		}

		private static CloakColor[] ParseHexColors(params string[] hexes)
		{
			List<CloakColor> list = new List<CloakColor>(hexes.Length);
			for (int i = 0; i < hexes.Length; i++)
			{
				if (CloakColor.TryParse(hexes[i], out var color))
				{
					list.Add(color);
				}
			}
			return list.ToArray();
		}

		private static void SetSources(IList<CloakColor> colors)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < SrcColors.Length; i++)
			{
				SrcColors[i] = new Vector4(10f, 10f, 10f, 1f);
			}
			int num = Math.Min(colors.Count, SrcColors.Length);
			for (int j = 0; j < num; j++)
			{
				Color val = colors[j].ToUnityColor();
				SrcColors[j] = new Vector4(val.r, val.g, val.b, 1f);
			}
			SrcCount = num;
			if (colors.Count > SrcColors.Length)
			{
				Log.Warn($"cloak_palette.json: only the first {SrcColors.Length} cloakColors are used " + $"(found {colors.Count}). Increase MaxCloakColors in the shader to lift this.");
			}
		}

		private static void SetAvoidSources(IList<CloakColor> colors)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < AvoidColors.Length; i++)
			{
				AvoidColors[i] = new Vector4(10f, 10f, 10f, 1f);
			}
			int num = Math.Min(colors.Count, AvoidColors.Length);
			for (int j = 0; j < num; j++)
			{
				Color val = colors[j].ToUnityColor();
				AvoidColors[j] = new Vector4(val.r, val.g, val.b, 1f);
			}
			AvoidCount = num;
			if (colors.Count > AvoidColors.Length)
			{
				Log.Warn($"cloak_palette.json: only the first {AvoidColors.Length} avoidColors are used " + $"(found {colors.Count}).");
			}
		}

		private static List<CloakColor> ExtractHexArray(string json, string key)
		{
			List<CloakColor> list = new List<CloakColor>();
			string pattern = "\"" + Regex.Escape(key) + "\"\\s*:\\s*\\[(?<arr>[^\\]]*)\\]";
			Match match = Regex.Match(json, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
			if (!match.Success)
			{
				return list;
			}
			foreach (Match item in Regex.Matches(match.Groups["arr"].Value, "\"(?<h>#?[0-9a-fA-F]{6})\"", RegexOptions.CultureInvariant))
			{
				string text = item.Groups["h"].Value;
				if (!text.StartsWith("#", StringComparison.Ordinal))
				{
					text = "#" + text;
				}
				if (CloakColor.TryParse(text, out var color))
				{
					list.Add(color);
				}
			}
			return list;
		}

		private static bool TryExtractBool(string json, string key, out bool value)
		{
			value = false;
			string pattern = "\"" + Regex.Escape(key) + "\"\\s*:\\s*(?<v>true|false)";
			Match match = Regex.Match(json, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
			if (!match.Success)
			{
				return false;
			}
			value = match.Groups["v"].Value.Equals("true", StringComparison.OrdinalIgnoreCase);
			return true;
		}

		private static bool TryExtractFloat(string json, string key, out float value)
		{
			value = 0f;
			string pattern = "\"" + Regex.Escape(key) + "\"\\s*:\\s*(?<n>[0-9]+(?:\\.[0-9]+)?(?:[eE][-+]?[0-9]+)?)";
			Match match = Regex.Match(json, pattern, RegexOptions.CultureInvariant);
			if (!match.Success)
			{
				return false;
			}
			return float.TryParse(match.Groups["n"].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
		}
	}
	[DefaultExecutionOrder(10000)]
	[DisallowMultipleComponent]
	internal class CloakRecolor : MonoBehaviour
	{
		private readonly Dictionary<MeshRenderer, Shader> _originalShaderByRenderer = new Dictionary<MeshRenderer, Shader>();

		private readonly List<MeshRenderer> _meshCache = new List<MeshRenderer>();

		private int _meshRescanCountdown;

		private bool _meshCacheInvalid = true;

		public CloakColor Color { get; private set; } = CloakColor.Default;


		public bool UseCloakShader { get; private set; } = true;


		private void OnEnable()
		{
			_meshCacheInvalid = true;
		}

		private void LateUpdate()
		{
			CloakMaterialApplier.PruneDestroyed(_originalShaderByRenderer);
			MaybeRefreshMeshCache();
			if (PerfDiagnostics.Enabled)
			{
				Stopwatch stopwatch = Stopwatch.StartNew();
				ApplyToCachedMeshRenderersCore();
				stopwatch.Stop();
				PerfDiagnostics.RecordRecolorLateUpdate(((Object)((Component)this).gameObject).name, _meshCache.Count, stopwatch.Elapsed.TotalMilliseconds);
			}
			else
			{
				ApplyToCachedMeshRenderersCore();
			}
		}

		public void Configure(CloakColor color, bool useCloakShader)
		{
			Color = color;
			UseCloakShader = useCloakShader;
			_meshCacheInvalid = true;
			RebuildMeshCache();
			ApplyToCachedMeshRenderersCore();
		}

		public void SetColor(CloakColor color)
		{
			Color = color;
			ApplyToCachedMeshRenderersCore();
		}

		private void MaybeRefreshMeshCache()
		{
			if (_meshCacheInvalid)
			{
				RebuildMeshCache();
			}
			else if (--_meshRescanCountdown <= 0)
			{
				RebuildMeshCache();
			}
		}

		private void RebuildMeshCache()
		{
			_meshCacheInvalid = false;
			_meshRescanCountdown = Mathf.Max(1, CloakPaletteConfig.HeroMeshRescanIntervalFrames);
			_meshCache.Clear();
			MeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<MeshRenderer>(true);
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return;
			}
			MeshRenderer[] array = componentsInChildren;
			foreach (MeshRenderer val in array)
			{
				if (!((Object)(object)val == (Object)null) && !IsUnderSsmpUsernameObject(((Component)val).transform))
				{
					_meshCache.Add(val);
				}
			}
		}

		private void ApplyToCachedMeshRenderersCore()
		{
			foreach (MeshRenderer item in _meshCache)
			{
				if ((Object)(object)item == (Object)null)
				{
					_meshCacheInvalid = true;
				}
				else
				{
					if (IsUnderSsmpUsernameObject(((Component)item).transform))
					{
						continue;
					}
					Material sharedMaterial = ((Renderer)item).sharedMaterial;
					if ((Object)(object)sharedMaterial != (Object)null)
					{
						Texture mainTexture = sharedMaterial.mainTexture;
						if (HornetTextureRegistry.Register(mainTexture))
						{
							TextureDumper.TryDump(mainTexture, "hero");
						}
					}
					CloakMaterialApplier.Apply(item, null, Color, UseCloakShader, _originalShaderByRenderer);
				}
			}
		}

		private static bool IsUnderSsmpUsernameObject(Transform t)
		{
			Transform val = t;
			while ((Object)(object)val != (Object)null)
			{
				if (((Object)val).name == "Username")
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		public static CloakRecolor? AttachOrUpdate(GameObject? playerObject, CloakColor color, bool useCloakShader)
		{
			if ((Object)(object)playerObject == (Object)null)
			{
				return null;
			}
			CloakRecolor cloakRecolor = playerObject.GetComponent<CloakRecolor>();
			if ((Object)(object)cloakRecolor == (Object)null)
			{
				cloakRecolor = playerObject.AddComponent<CloakRecolor>();
			}
			cloakRecolor.Configure(color, useCloakShader);
			return cloakRecolor;
		}
	}
	[DefaultExecutionOrder(10000)]
	[DisallowMultipleComponent]
	internal class CloakSceneScanner : MonoBehaviour
	{
		private CloakColor _color = CloakColor.Default;

		private readonly Dictionary<MeshRenderer, Shader> _originalShaderByRenderer = new Dictionary<MeshRenderer, Shader>();

		private readonly HashSet<int> _loggedTextureIds = new HashSet<int>();

		private readonly HashSet<int> _loggedRendererIds = new HashSet<int>();

		private int _frameCounter;

		public static CloakSceneScanner? Instance { get; private set; }

		public static void EnsureCreated()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!((Object)(object)Instance != (Object)null))
			{
				GameObject val = new GameObject("HornetCloakColorSceneScanner");
				Object.DontDestroyOnLoad((Object)val);
				Instance = val.AddComponent<CloakSceneScanner>();
			}
		}

		public static void SetColor(CloakColor color)
		{
			EnsureCreated();
			Instance._color = color;
		}

		private void LateUpdate()
		{
			int num = Math.Max(1, CloakPaletteConfig.SceneScanIntervalFrames);
			if (_frameCounter++ % num != 0)
			{
				return;
			}
			CloakMaterialApplier.PruneDestroyed(_originalShaderByRenderer);
			string[] sceneScanTextureContains = CloakPaletteConfig.SceneScanTextureContains;
			string[] sceneScanPathContains = CloakPaletteConfig.SceneScanPathContains;
			tk2dSprite[] array;
			double findObjectsMs;
			if (PerfDiagnostics.Enabled)
			{
				Stopwatch stopwatch = Stopwatch.StartNew();
				array = Object.FindObjectsByType<tk2dSprite>((FindObjectsSortMode)0);
				stopwatch.Stop();
				findObjectsMs = stopwatch.Elapsed.TotalMilliseconds;
			}
			else
			{
				array = Object.FindObjectsByType<tk2dSprite>((FindObjectsSortMode)0);
				findObjectsMs = 0.0;
			}
			if (array == null || array.Length == 0)
			{
				if (PerfDiagnostics.Enabled)
				{
					PerfDiagnostics.RecordSceneScan(0, 0, findObjectsMs, 0.0);
				}
			}
			else if (PerfDiagnostics.Enabled)
			{
				Stopwatch stopwatch2 = Stopwatch.StartNew();
				int appliedCount = RunScanLoop(array, sceneScanTextureContains, sceneScanPathContains);
				stopwatch2.Stop();
				double totalMilliseconds = stopwatch2.Elapsed.TotalMilliseconds;
				PerfDiagnostics.RecordSceneScan(array.Length, appliedCount, findObjectsMs, totalMilliseconds);
			}
			else
			{
				RunScanLoop(array, sceneScanTextureContains, sceneScanPathContains);
			}
		}

		private int RunScanLoop(tk2dSprite[] sprites, string[]? nameFilters, string[]? pathFilters)
		{
			int num = 0;
			foreach (tk2dSprite val in sprites)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>();
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				Material sharedMaterial = ((Renderer)component).sharedMaterial;
				if ((Object)(object)sharedMaterial == (Object)null)
				{
					continue;
				}
				Texture mainTexture = sharedMaterial.mainTexture;
				if ((Object)(object)mainTexture == (Object)null || (Object)(object)((Component)component).GetComponentInParent<CloakRecolor>() != (Object)null || IsCompassIcon(((Component)component).transform))
				{
					continue;
				}
				string name = ((Object)mainTexture).name;
				string text = null;
				bool flag;
				if ((flag = HornetTextureRegistry.Contains(mainTexture)) && CloakPaletteConfig.SceneScanRegistryDenyPathContains.Length != 0)
				{
					text = GetPath(((Component)component).transform);
					if (MatchesAnyFilter(text, CloakPaletteConfig.SceneScanRegistryDenyPathContains))
					{
						flag = false;
					}
				}
				bool flag2 = !flag && nameFilters != null && nameFilters.Length != 0 && MatchesAnyFilter(name, nameFilters);
				bool flag3 = false;
				if (!flag && !flag2 && pathFilters != null && pathFilters.Length != 0)
				{
					text = GetPath(((Component)component).transform);
					flag3 = MatchesAnyFilter(text, pathFilters);
				}
				if (!flag && !flag2 && !flag3)
				{
					if (CloakPaletteConfig.DebugLogging && _loggedTextureIds.Add(((Object)mainTexture).GetInstanceID()))
					{
						if (text == null)
						{
							text = GetPath(((Component)component).transform);
						}
						Log.Info("[Scanner] Ignored texture (no match): " + name + " " + $"(id={((Object)mainTexture).GetInstanceID()}) on '{text}'");
					}
					continue;
				}
				if (CloakPaletteConfig.DebugLogging && _loggedRendererIds.Add(((Object)component).GetInstanceID()))
				{
					if (text == null)
					{
						text = GetPath(((Component)component).transform);
					}
					string arg = (flag ? "registry" : (flag2 ? "name-filter" : "path-filter"));
					Log.Info("[Scanner] Tinting orphan renderer '" + text + "' " + $"(tex={name}, id={((Object)mainTexture).GetInstanceID()}, via={arg})");
				}
				if ((flag2 || flag3) && HornetTextureRegistry.Register(mainTexture))
				{
					TextureDumper.TryDump(mainTexture, flag2 ? "scanner-name" : "scanner-path");
				}
				CloakMaterialApplier.Apply(component, val, _color, useCloakShader: true, _originalShaderByRenderer);
				num++;
			}
			return num;
		}

		private static bool IsCompassIcon(Transform t)
		{
			if ((Object)(object)t == (Object)null)
			{
				return false;
			}
			return ((Object)t).name.StartsWith("Compass Icon", StringComparison.Ordinal);
		}

		private static bool MatchesAnyFilter(string name, string[] filters)
		{
			foreach (string value in filters)
			{
				if (!string.IsNullOrEmpty(value) && name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static string GetPath(Transform t)
		{
			if ((Object)(object)t == (Object)null)
			{
				return "(null)";
			}
			StringBuilder stringBuilder = new StringBuilder();
			while ((Object)(object)t != (Object)null)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Insert(0, '/');
				}
				stringBuilder.Insert(0, ((Object)t).name);
				t = t.parent;
			}
			return stringBuilder.ToString();
		}
	}
	internal static class CloakShaderManager
	{
		private const string ShaderName = "HornetCloakColor/CloakHueShift";

		private const string ShaderAssetName = "CloakHueShift";

		private const string ResourceName = "HornetCloakColor.Resources.cloakshader.bundle";

		private static bool _attemptedLoad;

		private static AssetBundle? _bundle;

		private static Shader? _shader;

		public const int MaxCloakColors = 16;

		public const int MaxAvoidColors = 16;

		public static readonly int TargetHueId = Shader.PropertyToID("_TargetHue");

		public static readonly int TargetSatId = Shader.PropertyToID("_TargetSat");

		public static readonly int TargetValId = Shader.PropertyToID("_TargetVal");

		public static readonly int SrcColorsId = Shader.PropertyToID("_SrcColors");

		public static readonly int AvoidColorsId = Shader.PropertyToID("_AvoidColors");

		public static readonly int MatchRadiusId = Shader.PropertyToID("_MatchRadius");

		public static readonly int AvoidMatchRadiusId = Shader.PropertyToID("_AvoidMatchRadius");

		public static readonly int StrengthId = Shader.PropertyToID("_Strength");

		public static Shader? Shader
		{
			get
			{
				if (_attemptedLoad)
				{
					return _shader;
				}
				_attemptedLoad = true;
				_shader = LoadShader();
				return _shader;
			}
		}

		public static bool BundleMissing
		{
			get
			{
				if (_attemptedLoad)
				{
					return (Object)(object)_shader == (Object)null;
				}
				return false;
			}
		}

		private static Shader? LoadShader()
		{
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("HornetCloakColor.Resources.cloakshader.bundle");
			if (stream == null)
			{
				Log.Warn("Cloak shader bundle not embedded (HornetCloakColor.Resources.cloakshader.bundle). Cloak-only recolor disabled; falling back to whole-character tint.");
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			memoryStream.Position = 0L;
			try
			{
				_bundle = AssetBundle.LoadFromMemory(memoryStream.ToArray());
			}
			catch (Exception arg)
			{
				Log.Error($"Failed to load cloak shader bundle: {arg}");
				return null;
			}
			if ((Object)(object)_bundle == (Object)null)
			{
				Log.Warn("AssetBundle.LoadFromMemory returned null for the cloak shader bundle.");
				return null;
			}
			Shader val = TryLoadShaderFromBundle(_bundle);
			if ((Object)(object)val == (Object)null)
			{
				Log.Warn("Cloak shader not found in embedded bundle (expected asset name 'CloakHueShift' or runtime name 'HornetCloakColor/CloakHueShift'). Rebuild the bundle from Shaders/CloakHueShift.shader.");
				return null;
			}
			Log.Info("Loaded cloak shader '" + ((Object)val).name + "' from embedded bundle.");
			return val;
		}

		private static Shader? TryLoadShaderFromBundle(AssetBundle bundle)
		{
			Shader val = bundle.LoadAsset<Shader>("CloakHueShift");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			val = bundle.LoadAsset<Shader>("HornetCloakColor/CloakHueShift");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			Shader[] array = bundle.LoadAllAssets<Shader>();
			if (array == null || array.Length == 0)
			{
				return null;
			}
			Shader[] array2 = array;
			foreach (Shader val2 in array2)
			{
				if ((Object)(object)val2 != (Object)null && ((Object)val2).name == "HornetCloakColor/CloakHueShift")
				{
					return val2;
				}
			}
			if (array.Length != 1)
			{
				return null;
			}
			return array[0];
		}
	}
	internal static class HornetTextureRegistry
	{
		private static readonly HashSet<int> _ids = new HashSet<int>();

		private static readonly HashSet<int> _logged = new HashSet<int>();

		public static int Count => _ids.Count;

		public static bool Register(Texture? tex)
		{
			if ((Object)(object)tex == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)tex).GetInstanceID();
			if (!_ids.Add(instanceID))
			{
				return false;
			}
			if (CloakPaletteConfig.DebugLogging && _logged.Add(instanceID))
			{
				Log.Info($"[Registry] Registered Hornet texture '{((Object)tex).name}' (id={instanceID}); total={_ids.Count}.");
			}
			return true;
		}

		public static bool Contains(Texture? tex)
		{
			if ((Object)(object)tex == (Object)null)
			{
				return false;
			}
			return _ids.Contains(((Object)tex).GetInstanceID());
		}
	}
	internal static class MapMaskHarmonyPatcher
	{
		private const string HarmonyId = "hornet.cloak.color.mapmask";

		private static bool _applied;

		internal static void Apply()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Expected O, but got Unknown
			if (_applied)
			{
				return;
			}
			_applied = true;
			Harmony val = new Harmony("hornet.cloak.color.mapmask");
			MethodInfo methodInfo = AccessTools.Method(typeof(GameMap), "PositionCompassAndCorpse", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(MapMaskHarmonyPatcher), "GameMap_PositionCompassAndCorpse_Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			Type type = AccessTools.TypeByName("InventoryWideMap");
			if (type != null)
			{
				MethodInfo methodInfo2 = AccessTools.Method(type, "UpdatePositions", (Type[])null, (Type[])null);
				if (methodInfo2 != null)
				{
					val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(MapMaskHarmonyPatcher), "InventoryWideMap_UpdatePositions_Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.Info("Hooked InventoryWideMap.UpdatePositions");
				}
				else
				{
					Log.Warn("InventoryWideMap.UpdatePositions not found");
				}
				MethodInfo methodInfo3 = AccessTools.Method(type, "PositionIcon", (Type[])null, (Type[])null);
				if (methodInfo3 != null)
				{
					val.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(MapMaskHarmonyPatcher), "InventoryWideMap_PositionIcon_Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.Info("Hooked InventoryWideMap.PositionIcon");
				}
			}
			else
			{
				Log.Warn("InventoryWideMap type not found — wide-map tint disabled");
			}
			Type type2 = AccessTools.TypeByName("SSMP.Game.Client.MapManager");
			Type type3 = AccessTools.TypeByName("SSMP.Math.Vector2");
			if (type2 != null && type3 != null)
			{
				MethodInfo methodInfo4 = AccessTools.Method(type2, "CreatePlayerIcon", new Type[2]
				{
					typeof(ushort),
					type3
				}, (Type[])null);
				if (methodInfo4 != null)
				{
					val.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(MapMaskHarmonyPatcher), "MapManager_CreatePlayerIcon_Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.Info("Hooked SSMP MapManager.CreatePlayerIcon");
				}
				else
				{
					Log.Warn("SSMP MapManager type found but CreatePlayerIcon(ushort, Vector2) not — remote map tint disabled");
				}
			}
			else if (SSMPBridge.IsAvailable)
			{
				Log.Warn("SSMP plugin loaded but MapManager / Math.Vector2 types not found via reflection — remote map tint disabled. SSMP may have changed namespaces.");
			}
			ServerMapStateSyncPatcher.TryApply(val);
			RemoteMapIconVisibility.TryApplyClientManagerHook(val);
			SsmMapCompassBroadcastFixPatcher.TryApply(val);
			MapRemoteIconDeferredCreate.TryApply(val);
		}

		private static void GameMap_PositionCompassAndCorpse_Postfix(GameMap __instance)
		{
			HornetCloakColorPlugin instance = HornetCloakColorPlugin.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				LocalMapMaskTint.Refresh(__instance, instance.ColorConfig.CurrentColor);
				RemoteMapIconVisibility.SyncRemoteMapIconsVisible();
			}
		}

		private static void InventoryWideMap_UpdatePositions_Postfix(object __instance)
		{
			HornetCloakColorPlugin instance = HornetCloakColorPlugin.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			try
			{
				Transform val = ResolveWideMapCompassIcon(__instance);
				if ((Object)(object)val == (Object)null)
				{
					if (CloakPaletteConfig.DebugLogging)
					{
						Log.Warn("InventoryWideMap: could not resolve compass icon transform in UpdatePositions postfix");
					}
					return;
				}
				if (CloakPaletteConfig.DebugLogging)
				{
					Log.Info("InventoryWideMap.UpdatePositions tint -> " + ((Object)val).name);
				}
				LocalMapMaskTint.RefreshObject(((Component)val).gameObject, instance.ColorConfig.CurrentColor);
				RemoteMapIconVisibility.SyncRemoteMapIconsVisible();
			}
			catch (Exception ex)
			{
				Log.Warn("MapMaskHarmonyPatcher: wide-map tint failed: " + ex.Message);
			}
		}

		private static Transform? ResolveWideMapCompassIcon(object instance)
		{
			object? obj = instance.GetType().GetField("compassIcon", BindingFlags.Instance | BindingFlags.Public)?.GetValue(instance);
			Transform val = (Transform)((obj is Transform) ? obj : null);
			if (val != null && (Object)(object)val != (Object)null)
			{
				return val;
			}
			MonoBehaviour val2 = (MonoBehaviour)((instance is MonoBehaviour) ? instance : null);
			if (val2 != null && (Object)(object)val2 != (Object)null)
			{
				Transform val3 = ((Component)val2).transform.Find("Compass Icon");
				if ((Object)(object)val3 != (Object)null)
				{
					return val3;
				}
			}
			return null;
		}

		private static void InventoryWideMap_PositionIcon_Postfix(object __instance, Transform icon, bool isActive)
		{
			HornetCloakColorPlugin instance = HornetCloakColorPlugin.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)icon == (Object)null)
			{
				return;
			}
			try
			{
				Transform val = ResolveWideMapCompassIcon(__instance);
				if (((Object)(object)val != (Object)null) ? (icon == val) : (((Object)icon).name == "Compass Icon"))
				{
					if (CloakPaletteConfig.DebugLogging)
					{
						Log.Info($"InventoryWideMap.PositionIcon compass tint (isActive={isActive}) -> {((Object)icon).name}");
					}
					LocalMapMaskTint.RefreshObject(((Component)icon).gameObject, instance.ColorConfig.CurrentColor);
					if (isActive)
					{
						RemoteMapIconVisibility.SyncRemoteMapIconsVisible();
					}
				}
			}
			catch (Exception ex)
			{
				Log.Warn("MapMaskHarmonyPatcher: PositionIcon tint failed: " + ex.Message);
			}
		}

		private static void MapManager_CreatePlayerIcon_Postfix(ushort id, object __instance)
		{
			GameObject val = TryGetMapIconGameObject(__instance, id);
			if ((Object)(object)val == (Object)null)
			{
				if (CloakPaletteConfig.LogMapIconDiagnostics)
				{
					Log.Warn($"[MapIcon] CreatePlayerIcon postfix: no GameObject on map entry for player {id} (create failed or entry missing).");
				}
				return;
			}
			MapMaskTint mapMaskTint = val.GetComponent<MapMaskTint>();
			if ((Object)(object)mapMaskTint == (Object)null)
			{
				mapMaskTint = val.AddComponent<MapMaskTint>();
			}
			CloakColor remoteMapColorOrDefault = SSMPBridge.GetRemoteMapColorOrDefault(id);
			if (CloakPaletteConfig.LogMapIconDiagnostics)
			{
				Log.Info($"[MapIcon] CreatePlayerIcon → MapMaskTint on '{((Object)val).name}' player {id} (active={val.activeInHierarchy}) color={remoteMapColorOrDefault}");
			}
			mapMaskTint.InitRemote(id, remoteMapColorOrDefault);
		}

		private static GameObject? TryGetMapIconGameObject(object mapManager, ushort id)
		{
			try
			{
				object obj = mapManager.GetType().GetField("_mapEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(mapManager);
				if (obj == null)
				{
					return null;
				}
				MethodInfo method = obj.GetType().GetMethod("TryGetValue");
				if (method == null)
				{
					return null;
				}
				object[] array = new object[2] { id, null };
				object obj2 = method.Invoke(obj, array);
				if (!(obj2 is bool) || !(bool)obj2 || array[1] == null)
				{
					return null;
				}
				object obj3 = array[1];
				object? obj4 = obj3.GetType().GetProperty("GameObject", BindingFlags.Instance | BindingFlags.Public)?.GetValue(obj3);
				return (GameObject?)((obj4 is GameObject) ? obj4 : null);
			}
			catch (Exception ex)
			{
				Log.Warn($"MapMaskHarmonyPatcher: could not read map icon for player {id}: {ex.Message}");
				return null;
			}
		}
	}
	[DefaultExecutionOrder(20000)]
	[DisallowMultipleComponent]
	internal sealed class MapMaskTint : MonoBehaviour
	{
		private static readonly int ColorPropertyId = Shader.PropertyToID("_Color");

		private static readonly HashSet<MapMaskTint> LocalInstances = new HashSet<MapMaskTint>();

		private MaterialPropertyBlock? _block;

		private readonly List<Renderer> _renderers = new List<Renderer>();

		private readonly List<tk2dSprite> _sprites = new List<tk2dSprite>();

		private readonly List<Image> _images = new List<Image>();

		private float _rescanTimer;

		private bool _diagDumped;

		private ushort? _networkPlayerId;

		private CloakColor _color = CloakColor.Default;

		internal static void BroadcastLocalColor(CloakColor color)
		{
			foreach (MapMaskTint localInstance in LocalInstances)
			{
				if (!((Object)(object)localInstance == (Object)null))
				{
					localInstance.SetColor(color);
				}
			}
		}

		public void InitRemote(ushort networkPlayerId, CloakColor color)
		{
			if (_networkPlayerId.HasValue && _networkPlayerId.Value != networkPlayerId)
			{
				PlayerMapMaskTintRegistry.Unregister(_networkPlayerId.Value);
			}
			LocalInstances.Remove(this);
			_networkPlayerId = networkPlayerId;
			_color = color;
			PlayerMapMaskTintRegistry.Register(networkPlayerId, this);
			ApplyNow();
		}

		public void InitLocal(CloakColor color)
		{
			if (_networkPlayerId.HasValue)
			{
				PlayerMapMaskTintRegistry.Unregister(_networkPlayerId.Value);
				_networkPlayerId = null;
			}
			LocalInstances.Add(this);
			_color = color;
			ApplyNow();
		}

		public void SetColor(CloakColor color)
		{
			if (_networkPlayerId.HasValue && !_color.Equals(color) && CloakPaletteConfig.DebugLogging)
			{
				Log.Info($"MapMaskTint: remote player {_networkPlayerId.Value} color updated {_color} -> {color}");
			}
			_color = color;
			ApplyNow();
		}

		private void OnEnable()
		{
			ApplyNow();
		}

		private void LateUpdate()
		{
			_rescanTimer -= Time.unscaledDeltaTime;
			if (_rescanTimer <= 0f)
			{
				_rescanTimer = 0.25f;
				ApplyNow();
			}
			else
			{
				ApplyToCachedTargets();
			}
		}

		private void ApplyNow()
		{
			_renderers.Clear();
			_sprites.Clear();
			_images.Clear();
			((Component)this).GetComponentsInChildren<Renderer>(true, _renderers);
			((Component)this).GetComponentsInChildren<tk2dSprite>(true, _sprites);
			((Component)this).GetComponentsInChildren<Image>(true, _images);
			DumpHierarchyOnce();
			ApplyToCachedTargets();
		}

		private void ApplyToCachedTargets()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			Color val = _color.ToUnityColor();
			if (_renderers.Count > 0)
			{
				if (_block == null)
				{
					_block = new MaterialPropertyBlock();
				}
				foreach (Renderer renderer in _renderers)
				{
					if (!((Object)(object)renderer == (Object)null))
					{
						renderer.GetPropertyBlock(_block);
						_block.SetColor(ColorPropertyId, new Color(val.r, val.g, val.b, 1f));
						renderer.SetPropertyBlock(_block);
					}
				}
			}
			foreach (tk2dSprite sprite in _sprites)
			{
				if (!((Object)(object)sprite == (Object)null))
				{
					Color color = ((tk2dBaseSprite)sprite).color;
					if (color.r != val.r || color.g != val.g || color.b != val.b)
					{
						((tk2dBaseSprite)sprite).color = new Color(val.r, val.g, val.b, color.a);
					}
				}
			}
			foreach (Image image in _images)
			{
				if (!((Object)(object)image == (Object)null))
				{
					Color color2 = ((Graphic)image).color;
					((Graphic)image).color = new Color(val.r, val.g, val.b, color2.a);
				}
			}
		}

		private void DumpHierarchyOnce()
		{
			if (_diagDumped || !CloakPaletteConfig.DebugLogging)
			{
				return;
			}
			_diagDumped = true;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("[HornetCloakColor] MapMaskTint attached to '").Append(((Object)this).name).Append("' — ");
			stringBuilder.Append("renderers=").Append(_renderers.Count).Append(", sprites=")
				.Append(_sprites.Count)
				.Append(", images=")
				.Append(_images.Count);
			stringBuilder.AppendLine();
			for (int i = 0; i < _renderers.Count; i++)
			{
				Renderer val = _renderers[i];
				if (!((Object)(object)val == (Object)null))
				{
					string value = (((Object)(object)val.sharedMaterial != (Object)null && (Object)(object)val.sharedMaterial.shader != (Object)null) ? ((Object)val.sharedMaterial.shader).name : "<null>");
					stringBuilder.Append("  Renderer[").Append(i).Append("] type=")
						.Append(((object)val).GetType().Name)
						.Append(" path=")
						.Append(GetPath(((Component)val).transform))
						.Append(" shader=")
						.Append(value)
						.AppendLine();
				}
			}
			for (int j = 0; j < _sprites.Count; j++)
			{
				tk2dSprite val2 = _sprites[j];
				if (!((Object)(object)val2 == (Object)null))
				{
					stringBuilder.Append("  Sprite[").Append(j).Append("] path=")
						.Append(GetPath(((Component)val2).transform))
						.AppendLine();
				}
			}
			for (int k = 0; k < _images.Count; k++)
			{
				Image val3 = _images[k];
				if (!((Object)(object)val3 == (Object)null))
				{
					stringBuilder.Append("  Image[").Append(k).Append("] path=")
						.Append(GetPath(((Component)val3).transform))
						.AppendLine();
				}
			}
			ManualLogSource? logSource = HornetCloakColorPlugin.LogSource;
			if (logSource != null)
			{
				logSource.LogInfo((object)stringBuilder.ToString());
			}
		}

		private static string GetPath(Transform t)
		{
			StringBuilder stringBuilder = new StringBuilder(((Object)t).name);
			Transform parent = t.parent;
			while ((Object)(object)parent != (Object)null)
			{
				stringBuilder.Insert(0, ((Object)parent).name + "/");
				parent = parent.parent;
			}
			return stringBuilder.ToString();
		}

		private void OnDisable()
		{
			ClearTargets();
		}

		private void OnDestroy()
		{
			if (_networkPlayerId.HasValue)
			{
				PlayerMapMaskTintRegistry.Unregister(_networkPlayerId.Value);
			}
			LocalInstances.Remove(this);
			ClearTargets();
		}

		private void ClearTargets()
		{
			foreach (Renderer renderer in _renderers)
			{
				if ((Object)(object)renderer != (Object)null)
				{
					renderer.SetPropertyBlock((MaterialPropertyBlock)null);
				}
			}
		}
	}
	internal static class PlayerMapMaskTintRegistry
	{
		private static readonly Dictionary<ushort, MapMaskTint> ByPlayer = new Dictionary<ushort, MapMaskTint>();

		internal static void Register(ushort id, MapMaskTint tint)
		{
			ByPlayer[id] = tint;
		}

		internal static void Unregister(ushort id)
		{
			ByPlayer.Remove(id);
		}

		internal static void SetColor(ushort playerId, CloakColor color)
		{
			if (ByPlayer.TryGetValue(playerId, out MapMaskTint value) && (Object)(object)value != (Object)null)
			{
				value.SetColor(color);
			}
		}
	}
	internal static class LocalMapMaskTint
	{
		internal static void Refresh(GameMap? gameMap, CloakColor color)
		{
			if (!((Object)(object)gameMap == (Object)null) && !((Object)(object)gameMap.compassIcon == (Object)null))
			{
				RefreshObject(gameMap.compassIcon, color);
			}
		}

		internal static void RefreshObject(GameObject? icon, CloakColor color)
		{
			if (!((Object)(object)icon == (Object)null))
			{
				(icon.GetComponent<MapMaskTint>() ?? icon.AddComponent<MapMaskTint>()).InitLocal(color);
			}
		}
	}
	internal static class MapRemoteIconDeferredCreate
	{
		private static bool _applied;

		private static float _nextLogPendingNoGameMap;

		internal static void TryApply(Harmony harmony)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			if (_applied || !SSMPBridge.IsAvailable)
			{
				return;
			}
			Type type = AccessTools.TypeByName("SSMP.Game.Client.MapManager");
			if (type == null)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "UpdatePlayerHasIcon", new Type[2]
			{
				typeof(ushort),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(MapRemoteIconDeferredCreate), "UpdatePlayerHasIcon_Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Type type2 = AccessTools.TypeByName("SSMP.Game.Client.ClientManager");
			Type type3 = AccessTools.TypeByName("SSMP.Networking.Packet.Data.ClientPlayerAlreadyInScene");
			if (type2 != null && type3 != null)
			{
				MethodInfo methodInfo2 = AccessTools.Method(type2, "OnPlayerAlreadyInScene", new Type[1] { type3 }, (Type[])null);
				if (methodInfo2 != null)
				{
					harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(MapRemoteIconDeferredCreate), "ClientManager_OnPlayerAlreadyInScene_Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.Info("Hooked SSMP ClientManager.OnPlayerAlreadyInScene — retry remote map icons after late-join roster");
				}
			}
			_applied = true;
			Log.Info("Hooked SSMP MapManager.UpdatePlayerHasIcon — deferred remote map icon materialize");
		}

		private static void ClientManager_OnPlayerAlreadyInScene_Postfix(object __instance)
		{
			if (__instance == null)
			{
				return;
			}
			try
			{
				if (CloakPaletteConfig.LogMapIconDiagnostics)
				{
					Log.Info("[MapIcon] OnPlayerAlreadyInScene finished — running deferred remote icon materialize pass.");
				}
				TryMaterializePendingIcons(__instance.GetType().GetField("_mapManager", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance));
			}
			catch (Exception ex)
			{
				if (CloakPaletteConfig.LogMapIconDiagnostics)
				{
					Log.Warn("[MapIcon] OnPlayerAlreadyInScene materialize hook: " + ex.Message);
				}
			}
		}

		private static void UpdatePlayerHasIcon_Postfix(object __instance, ushort id, bool hasMapIcon)
		{
			if (__instance != null)
			{
				if (CloakPaletteConfig.LogMapIconDiagnostics)
				{
					Log.Info($"[MapIcon] UpdatePlayerHasIcon(player {id}, hasIcon={hasMapIcon}) — deferred materialize pass.");
				}
				TryMaterializePendingIcons(__instance);
			}
		}

		internal static void TryMaterializePendingIcons(object? mapManager)
		{
			if (mapManager == null)
			{
				if (CloakPaletteConfig.LogMapIconDiagnostics)
				{
					Log.Warn("[MapIcon] TryMaterializePendingIcons: MapManager instance null.");
				}
				return;
			}
			try
			{
				Type type = mapManager.GetType();
				object obj = type.GetMethod("GetGameMap", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(mapManager, null);
				MethodInfo method = type.GetMethod("CreatePlayerIcon", BindingFlags.Instance | BindingFlags.NonPublic);
				if (method == null)
				{
					Log.Warn("[MapIcon] TryMaterializePendingIcons: CreatePlayerIcon not found on MapManager.");
					return;
				}
				if (!(type.GetField("_mapEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(mapManager) is IEnumerable enumerable))
				{
					if (CloakPaletteConfig.LogMapIconDiagnostics)
					{
						Log.Warn("[MapIcon] TryMaterializePendingIcons: _mapEntries missing or not enumerable.");
					}
					return;
				}
				List<(ushort, object)> list = new List<(ushort, object)>();
				foreach (object item2 in enumerable)
				{
					if (item2 == null)
					{
						continue;
					}
					Type type2 = item2.GetType();
					PropertyInfo property = type2.GetProperty("Key");
					PropertyInfo property2 = type2.GetProperty("Value");
					if (property == null || property2 == null)
					{
						continue;
					}
					object value = property.GetValue(item2);
					object value2 = property2.GetValue(item2);
					if (!(value is ushort item) || value2 == null)
					{
						continue;
					}
					Type type3 = value2.GetType();
					bool flag = (bool)type3.GetProperty("HasMapIcon").GetValue(value2);
					object value3 = type3.GetProperty("GameObject").GetValue(value2);
					if (flag && value3 == null)
					{
						object value4 = type3.GetProperty("Position").GetValue(value2);
						if (value4 != null)
						{
							list.Add((item, value4));
						}
					}
				}
				if (list.Count == 0)
				{
					return;
				}
				if (obj == null)
				{
					if (CloakPaletteConfig.LogMapIconDiagnostics && Time.realtimeSinceStartup >= _nextLogPendingNoGameMap)
					{
						_nextLogPendingNoGameMap = Time.realtimeSinceStartup + 2f;
						string arg = string.Join(",", list.ConvertAll(((ushort playerId, object pos) x) => x.playerId.ToString()));
						Log.Warn($"[MapIcon] {list.Count} remote map entr(y/ies) need GameObjects (players {arg}) " + "but GetGameMap() is null — will retry when GameMap exists.");
					}
					return;
				}
				int num = 0;
				foreach (var (num2, obj2) in list)
				{
					method.Invoke(mapManager, new object[2] { num2, obj2 });
					num++;
				}
				if (num > 0 && CloakPaletteConfig.LogMapIconDiagnostics)
				{
					Log.Info($"[MapIcon] Deferred CreatePlayerIcon succeeded for {num} remote player(s) (GameMap was ready).");
				}
			}
			catch (Exception ex)
			{
				Log.Warn("[MapIcon] TryMaterializePendingIcons: " + ex.Message);
			}
		}
	}
	internal static class PerfDiagnostics
	{
		private const float FlushIntervalSec = 2f;

		private static float _windowStart = -1f;

		private static int _recolorLateUpdates;

		private static double _recolorMsTotal;

		private static double _recolorMsMax;

		private static string _recolorMaxOwner = "";

		private static int _recolorMeshMax;

		private static long _recolorMeshSum;

		private static int _scannerRuns;

		private static long _scannerSpritesTotal;

		private static int _scannerSpritesMax;

		private static long _scannerAppliedTotal;

		private static double _scannerFindMsTotal;

		private static double _scannerLoopMsTotal;

		private static int _mapSyncCalls;

		private static double _mapSyncMsTotal;

		private static double _mapSyncMsMax;

		public static bool Enabled => CloakPaletteConfig.PerfDiagnostics;

		public static void RecordRecolorLateUpdate(string ownerName, int meshRendererCount, double elapsedMs)
		{
			if (Enabled)
			{
				_recolorLateUpdates++;
				_recolorMsTotal += elapsedMs;
				if (elapsedMs > _recolorMsMax)
				{
					_recolorMsMax = elapsedMs;
					_recolorMaxOwner = ownerName;
				}
				_recolorMeshSum += meshRendererCount;
				if (meshRendererCount > _recolorMeshMax)
				{
					_recolorMeshMax = meshRendererCount;
				}
				MaybeFlushWindow();
			}
		}

		public static void RecordSceneScan(int tk2dSpriteCount, int appliedCount, double findObjectsMs, double loopMs)
		{
			if (Enabled)
			{
				_scannerRuns++;
				_scannerSpritesTotal += tk2dSpriteCount;
				if (tk2dSpriteCount > _scannerSpritesMax)
				{
					_scannerSpritesMax = tk2dSpriteCount;
				}
				_scannerAppliedTotal += appliedCount;
				_scannerFindMsTotal += findObjectsMs;
				_scannerLoopMsTotal += loopMs;
				MaybeFlushWindow();
			}
		}

		public static void RecordMapSyncVisible(double elapsedMs)
		{
			if (Enabled)
			{
				_mapSyncCalls++;
				_mapSyncMsTotal += elapsedMs;
				if (elapsedMs > _mapSyncMsMax)
				{
					_mapSyncMsMax = elapsedMs;
				}
				MaybeFlushWindow();
			}
		}

		private static void MaybeFlushWindow()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_windowStart < 0f)
			{
				_windowStart = realtimeSinceStartup;
			}
			if (!(realtimeSinceStartup - _windowStart < 2f))
			{
				Emit();
				_recolorLateUpdates = 0;
				_recolorMsTotal = 0.0;
				_recolorMsMax = 0.0;
				_recolorMaxOwner = "";
				_recolorMeshMax = 0;
				_recolorMeshSum = 0L;
				_scannerRuns = 0;
				_scannerSpritesTotal = 0L;
				_scannerSpritesMax = 0;
				_scannerAppliedTotal = 0L;
				_scannerFindMsTotal = 0.0;
				_scannerLoopMsTotal = 0.0;
				_mapSyncCalls = 0;
				_mapSyncMsTotal = 0.0;
				_mapSyncMsMax = 0.0;
				_windowStart = realtimeSinceStartup;
			}
		}

		private static void Emit()
		{
			if (_recolorLateUpdates > 0)
			{
				double num = _recolorMsTotal / (double)_recolorLateUpdates;
				double num2 = (double)_recolorMeshSum / (double)_recolorLateUpdates;
				Log.Info($"[HCC/Perf] CloakRecolor: {_recolorLateUpdates} LateUpdate(s) in ~{2f:F0}s " + $"(≈{(float)_recolorLateUpdates / 2f:F0}/s) — total {_recolorMsTotal:F1}ms, " + $"avg {num:F3}ms/update, max {_recolorMsMax:F3}ms on '{_recolorMaxOwner}', " + $"MeshRenderer avg {num2:F1}, max {_recolorMeshMax} " + "(multiple players ⇒ multiple components ⇒ cost scales up).");
			}
			if (_scannerRuns > 0)
			{
				double num3 = (double)_scannerSpritesTotal / (double)_scannerRuns;
				double num4 = (double)_scannerAppliedTotal / (double)_scannerRuns;
				double num5 = _scannerFindMsTotal / (double)_scannerRuns;
				double num6 = _scannerLoopMsTotal / (double)_scannerRuns;
				Log.Info($"[HCC/Perf] CloakSceneScanner: {_scannerRuns} scan(s) — tk2dSprite count avg {num3:F0}, max in one pass {_scannerSpritesMax}, " + $"Apply() calls avg {num4:F1}, FindObjects {num5:F2}ms/scan, loop+Apply {num6:F2}ms/scan " + "(MP adds sprites ⇒ larger FindObjects + longer loop).");
			}
			if (_mapSyncCalls > 0)
			{
				double num7 = _mapSyncMsTotal / (double)_mapSyncCalls;
				Log.Info($"[HCC/Perf] SyncRemoteMapIconsVisible: {_mapSyncCalls} call(s) in ~{2f:F0}s " + $"(≈{(float)_mapSyncCalls / 2f:F0}/s) — total {_mapSyncMsTotal:F2}ms, avg {num7:F3}ms, max {_mapSyncMsMax:F3}ms " + "(reflection into SSMP MapManager; spikes if GameMap updates compass often).");
			}
		}
	}
	internal static class RemoteMapIconVisibility
	{
		private static object? _clientManager;

		private static float _nextSyncLogTime;

		internal static void RegisterClientManager(object clientManager)
		{
			_clientManager = clientManager;
		}

		internal static void TryApplyClientManagerHook(Harmony harmony)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("SSMP.Game.Client.ClientManager");
			Type type2 = AccessTools.TypeByName("SSMP.Game.Server.ServerManager");
			if (!(type == null) && !(type2 == null))
			{
				MethodInfo methodInfo = AccessTools.Method(type, "Initialize", new Type[1] { type2 }, (Type[])null);
				if (!(methodInfo == null))
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(RemoteMapIconVisibility), "ClientManager_Initialize_Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.Info("Hooked SSMP ClientManager.Initialize (inventory map remote icon visibility)");
				}
			}
		}

		private static void ClientManager_Initialize_Postfix(object __instance)
		{
			if (__instance != null)
			{
				RegisterClientManager(__instance);
			}
		}

		internal static void SyncRemoteMapIconsVisible()
		{
			if (_clientManager == null)
			{
				return;
			}
			Stopwatch stopwatch = (PerfDiagnostics.Enabled ? Stopwatch.StartNew() : null);
			try
			{
				object obj = _clientManager.GetType().GetField("_mapManager", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(_clientManager);
				if (obj == null)
				{
					if (CloakPaletteConfig.LogMapIconDiagnostics)
					{
						Log.Warn("[MapIcon] SyncRemoteMapIconsVisible: ClientManager has no _mapManager.");
					}
					return;
				}
				SetDisplayingIconsAndRefresh(obj, showing: true);
				MapRemoteIconDeferredCreate.TryMaterializePendingIcons(obj);
				if (CloakPaletteConfig.LogMapIconDiagnostics && Time.realtimeSinceStartup >= _nextSyncLogTime)
				{
					_nextSyncLogTime = Time.realtimeSinceStartup + 1.25f;
					Log.Info("[MapIcon] SyncRemoteMapIconsVisible: set _displayingIcons=true, UpdateMapIconsActive, materialize pass.");
				}
			}
			catch (Exception ex)
			{
				if (CloakPaletteConfig.LogMapIconDiagnostics)
				{
					Log.Warn("[MapIcon] SyncRemoteMapIconsVisible: " + ex.Message);
				}
			}
			finally
			{
				if (stopwatch != null)
				{
					stopwatch.Stop();
					PerfDiagnostics.RecordMapSyncVisible(stopwatch.Elapsed.TotalMilliseconds);
				}
			}
		}

		private static void SetDisplayingIconsAndRefresh(object mapManager, bool showing)
		{
			try
			{
				Type type = mapManager.GetType();
				type.GetField("_displayingIcons", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(mapManager, showing);
				type.GetMethod("UpdateMapIconsActive", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(mapManager, null);
			}
			catch
			{
			}
		}
	}
	internal static class ServerMapStateSyncPatcher
	{
		private static bool _applied;

		private static bool _warnedMissingUpdatePosMethod;

		internal static void TryApply(Harmony harmony)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			if (_applied || !SSMPBridge.IsAvailable)
			{
				return;
			}
			Type type = AccessTools.TypeByName("SSMP.Game.Server.ServerManager");
			Type type2 = AccessTools.TypeByName("SSMP.Game.Server.ServerPlayerData");
			if (type == null || type2 == null)
			{
				Log.Warn("SSMP server types not found — late-join map icon sync patch skipped");
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "OnClientEnterScene", new Type[1] { type2 }, (Type[])null);
			if (methodInfo == null)
			{
				Log.Warn("SSMP ServerManager.OnClientEnterScene(ServerPlayerData) not found — map sync skipped");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(ServerMapStateSyncPatcher), "OnClientEnterScene_Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_applied = true;
			Log.Info("Hooked SSMP ServerManager.OnClientEnterScene — co-scene map icon replay + peer push");
		}

		private static object? GetInstanceFieldFromHierarchy(object target, string fieldName)
		{
			Type type = target.GetType();
			while (type != null)
			{
				FieldInfo field = type.GetField(fieldName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null)
				{
					return field.GetValue(target);
				}
				type = type.BaseType;
			}
			return null;
		}

		private static void OnClientEnterScene_Postfix(object __instance, object playerData)
		{
			if (__instance == null || playerData == null)
			{
				return;
			}
			try
			{
				object instanceFieldFromHierarchy = GetInstanceFieldFromHierarchy(__instance, "_netServer");
				object instanceFieldFromHierarchy2 = GetInstanceFieldFromHierarchy(__instance, "_playerData");
				if (instanceFieldFromHierarchy == null || instanceFieldFromHierarchy2 == null)
				{
					Log.Warn("[MapIcon] ServerMapStateSync: could not read _netServer or _playerData via reflection " + $"(netServer={instanceFieldFromHierarchy != null}, playerDict={instanceFieldFromHierarchy2 != null}) — late-join replay skipped.");
					return;
				}
				Type type = playerData.GetType();
				ushort num = (ushort)type.GetProperty("Id").GetValue(playerData);
				string text = (string)type.GetProperty("CurrentScene").GetValue(playerData);
				MethodInfo method = instanceFieldFromHierarchy.GetType().GetMethod("GetUpdateManagerForClient", new Type[1] { typeof(ushort) });
				if (method == null)
				{
					Log.Warn("[MapIcon] ServerMapStateSync: GetUpdateManagerForClient not found on NetServer.");
					return;
				}
				object obj = method.Invoke(instanceFieldFromHierarchy, new object[1] { num });
				if (obj == null)
				{
					Log.Warn($"[MapIcon] ServerMapStateSync: no ServerUpdateManager for entering client {num} " + "(scene " + text + ") — late-join map icon replay skipped.");
					return;
				}
				Type type2 = obj.GetType();
				MethodInfo methodInfo = AccessTools.Method(type2, "UpdatePlayerMapIcon", new Type[2]
				{
					typeof(ushort),
					typeof(bool)
				}, (Type[])null);
				if (methodInfo == null)
				{
					Log.Warn("ServerMapStateSync: ServerUpdateManager.UpdatePlayerMapIcon(ushort,bool) not found — late-join map icon sync disabled");
					return;
				}
				Type type3 = AccessTools.TypeByName("SSMP.Math.Vector2");
				MethodInfo methodInfo2 = ((type3 != null) ? AccessTools.Method(type2, "UpdatePlayerMapPosition", new Type[2]
				{
					typeof(ushort),
					type3
				}, (Type[])null) : null);
				if (methodInfo2 == null && !_warnedMissingUpdatePosMethod)
				{
					_warnedMissingUpdatePosMethod = true;
					Log.Warn("[MapIcon] ServerMapStateSync: UpdatePlayerMapPosition not resolved — replaying HasIcon flags only (no stored positions).");
				}
				if (!(instanceFieldFromHierarchy2.GetType().GetProperty("Values")?.GetValue(instanceFieldFromHierarchy2) is IEnumerable enumerable))
				{
					Log.Warn("[MapIcon] ServerMapStateSync: could not enumerate _playerData.Values.");
					return;
				}
				bool flag = (bool)type.GetProperty("HasMapIcon").GetValue(playerData);
				object obj2 = type.GetProperty("MapPosition")?.GetValue(playerData);
				List<string> list = new List<string>();
				List<string> list2 = new List<string>();
				int num2 = 0;
				foreach (object item in enumerable)
				{
					if (item == null)
					{
						continue;
					}
					Type type4 = item.GetType();
					ushort num3 = (ushort)type4.GetProperty("Id").GetValue(item);
					if (num3 == num || !string.Equals((string)type4.GetProperty("CurrentScene").GetValue(item), text, StringComparison.Ordinal))
					{
						continue;
					}
					bool flag2 = (bool)type4.GetProperty("HasMapIcon").GetValue(item);
					methodInfo.Invoke(obj, new object[2] { num3, flag2 });
					bool flag3 = false;
					if (flag2 && methodInfo2 != null)
					{
						object obj3 = type4.GetProperty("MapPosition")?.GetValue(item);
						if (obj3 != null)
						{
							methodInfo2.Invoke(obj, new object[2] { num3, obj3 });
							flag3 = true;
						}
					}
					object obj4 = method.Invoke(instanceFieldFromHierarchy, new object[1] { num3 });
					if (obj4 != null)
					{
						methodInfo.Invoke(obj4, new object[2] { num, flag });
						bool flag4 = false;
						if (flag && methodInfo2 != null && obj2 != null)
						{
							methodInfo2.Invoke(obj4, new object[2] { num, obj2 });
							flag4 = true;
						}
						if (CloakPaletteConfig.LogMapIconDiagnostics)
						{
							list2.Add(string.Format("peer {0} ← entering {1}: HasIcon={2}, pos={3}", num3, num, flag, flag4 ? "sent" : "none"));
						}
					}
					else if (CloakPaletteConfig.LogMapIconDiagnostics)
					{
						list2.Add($"peer {num3}: no ServerUpdateManager (skipped push)");
					}
					num2++;
					if (CloakPaletteConfig.LogMapIconDiagnostics)
					{
						list.Add(string.Format("{0}:HasIcon={1},pos={2}", num3, flag2, flag3 ? "sent" : "none"));
					}
				}
				if (CloakPaletteConfig.LogMapIconDiagnostics && num2 > 0)
				{
					StringBuilder stringBuilder = new StringBuilder();
					stringBuilder.Append($"[MapIcon] Server late-join map replay → client {num} scene={text}: ");
					stringBuilder.Append($"{num2} co-scene peer(s) [");
					stringBuilder.Append(string.Join("; ", list));
					stringBuilder.Append("]; push entering state to peers [");
					stringBuilder.Append(string.Join("; ", list2));
					stringBuilder.Append("].");
					Log.Info(stringBuilder.ToString());
				}
				else if (CloakPaletteConfig.LogMapIconDiagnostics && num2 == 0)
				{
					Log.Info($"[MapIcon] Server late-join map replay → client {num} scene={text}: " + "no other players in this scene on server (nothing to replay).");
				}
			}
			catch (Exception ex)
			{
				Log.Warn("[MapIcon] ServerMapStateSync exception: " + ex.Message);
			}
		}
	}
	internal static class SsmMapCompassBroadcastFixPatcher
	{
		private static class MapBroadcastReflect
		{
			internal static FieldInfo? NetClient;

			internal static PropertyInfo? NetIsConnected;

			internal static FieldInfo? ServerSettings;

			internal static PropertyInfo? AlwaysShowMapIcons;

			internal static PropertyInfo? OnlyBroadcastMapIconWithCompass;

			internal static FieldInfo? LastSentMapIcon;

			internal static FieldInfo? LastPosition;

			internal static MethodInfo? TryGetMapPosition;

			internal static PropertyInfo? NetUpdateManager;

			internal static MethodInfo? UpdatePlayerMapIconBool;

			internal static MethodInfo? UpdatePlayerMapPosition;

			internal static ConstructorInfo? SsmpVecCtorFf;

			internal static PropertyInfo? GameplayCompassTool;

			internal static PropertyInfo? CompassIsEquipped;

			internal static object[]? TryGetMapPosArgs;

			internal static object[]? InvokeOneArg;

			internal static bool Ready { get; private set; }

			internal static bool TryBuild(Type mapManagerType)
			{
				if (Ready)
				{
					return true;
				}
				try
				{
					NetClient = mapManagerType.GetField("_netClient", BindingFlags.Instance | BindingFlags.NonPublic);
					if (NetClient == null)
					{
						return false;
					}
					Type fieldType = NetClient.FieldType;
					NetIsConnected = fieldType.GetProperty("IsConnected", BindingFlags.Instance | BindingFlags.Public);
					if (NetIsConnected == null)
					{
						return false;
					}
					NetUpdateManager = fieldType.GetProperty("UpdateManager", BindingFlags.Instance | BindingFlags.Public);
					if (NetUpdateManager == null)
					{
						return false;
					}
					Type propertyType = NetUpdateManager.PropertyType;
					UpdatePlayerMapIconBool = propertyType.GetMethod("UpdatePlayerMapIcon", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(bool) }, null);
					if (UpdatePlayerMapIconBool == null)
					{
						return false;
					}
					ServerSettings = mapManagerType.GetField("_serverSettings", BindingFlags.Instance | BindingFlags.NonPublic);
					if (ServerSettings == null)
					{
						return false;
					}
					Type fieldType2 = ServerSettings.FieldType;
					AlwaysShowMapIcons = fieldType2.GetProperty("AlwaysShowMapIcons", BindingFlags.Instance | BindingFlags.Public);
					OnlyBroadcastMapIconWithCompass = fieldType2.GetProperty("OnlyBroadcastMapIconWithCompass", BindingFlags.Instance | BindingFlags.Public);
					if (AlwaysShowMapIcons == null || OnlyBroadcastMapIconWithCompass == null)
					{
						return false;
					}
					LastSentMapIcon = mapManagerType.GetField("_lastSentMapIcon", BindingFlags.Instance | BindingFlags.NonPublic);
					LastPosition = mapManagerType.GetField("_lastPosition", BindingFlags.Instance | BindingFlags.NonPublic);
					if (LastSentMapIcon == null || LastPosition == null)
					{
						return false;
					}
					TryGetMapPosition = mapManagerType.GetMethod("TryGetMapPosition", BindingFlags.Instance | BindingFlags.NonPublic);
					if (TryGetMapPosition == null)
					{
						return false;
					}
					Type type = AccessTools.TypeByName("SSMP.Math.Vector2");
					if (type == null)
					{
						return false;
					}
					SsmpVecCtorFf = type.GetConstructor(new Type[2]
					{
						typeof(float),
						typeof(float)
					});
					if (SsmpVecCtorFf == null)
					{
						return false;
					}
					UpdatePlayerMapPosition = propertyType.GetMethod("UpdatePlayerMapPosition", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { type }, null);
					if (UpdatePlayerMapPosition == null)
					{
						return false;
					}
					Type type2 = AccessTools.TypeByName("Gameplay");
					if (type2 == null)
					{
						return false;
					}
					GameplayCompassTool = type2.GetProperty("CompassTool", BindingFlags.Static | BindingFlags.Public);
					if (GameplayCompassTool == null)
					{
						return false;
					}
					CompassIsEquipped = GameplayCompassTool.PropertyType.GetProperty("IsEquipped", BindingFlags.Instance | BindingFlags.Public);
					if (CompassIsEquipped == null)
					{
						return false;
					}
					TryGetMapPosArgs = new object[1];
					InvokeOneArg = new object[1];
					Ready = true;
					return true;
				}
				catch
				{
					return false;
				}
			}
		}

		private static bool _applied;

		private static float _nextLogDeferMapPos;

		private static float _nextLogMapPosSent;

		internal static void TryApply(Harmony harmony)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			if (_applied || !SSMPBridge.IsAvailable)
			{
				return;
			}
			Type type = AccessTools.TypeByName("SSMP.Game.Client.MapManager");
			if (type == null)
			{
				return;
			}
			if (!MapBroadcastReflect.TryBuild(type))
			{
				Log.Warn("SsmMapCompassBroadcastFix: could not cache MapManager reflection — compass broadcast fix skipped.");
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "HeroControllerOnUpdate", new Type[1] { typeof(HeroController) }, (Type[])null);
			if (methodInfo == null)
			{
				Log.Warn("SSMP MapManager.HeroControllerOnUpdate not found — compass broadcast fix skipped");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(SsmMapCompassBroadcastFixPatcher), "HeroControllerOnUpdate_Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_applied = true;
			Log.Info("Hooked SSMP MapManager.HeroControllerOnUpdate — corrected compass map-icon broadcast logic");
		}

		private static bool HeroControllerOnUpdate_Prefix(object __instance, HeroController heroController)
		{
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: 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_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			if (__instance == null || !MapBroadcastReflect.Ready)
			{
				return true;
			}
			try
			{
				object value = MapBroadcastReflect.NetClient.GetValue(__instance);
				if (value == null)
				{
					return true;
				}
				if (!(bool)MapBroadcastReflect.NetIsConnected.GetValue(value))
				{
					return true;
				}
				object value2 = MapBroadcastReflect.ServerSettings.GetValue(__instance);
				if (value2 == null)
				{
					return true;
				}
				bool flag = (bool)MapBroadcastReflect.AlwaysShowMapIcons.GetValue(value2);
				bool flag2 = (bool)MapBroadcastReflect.OnlyBroadcastMapIconWithCompass.GetValue(value2);
				Vector2 mapPosition;
				bool flag3 = TryInvokeTryGetMapPosition(__instance, out mapPosition);
				bool flag4 = true;
				if (!flag)
				{
					if (!flag2)
					{
						flag4 = false;
					}
					else if (!IsCompassEquipped())
					{
						flag4 = false;
					}
				}
				bool flag5 = (bool)MapBroadcastReflect.LastSentMapIcon.GetValue(__instance);
				if (flag4 != flag5)
				{
					MapBroadcastReflect.LastSentMapIcon.SetValue(__instance, flag4);
					object value3 = MapBroadcastReflect.NetUpdateManager.GetValue(value);
					if (value3 == null)
					{
						Log.Warn("[MapIcon] MapManager._netClient.UpdateManager is null — compass broadcast fix skipped for this frame.");
						return true;
					}
					MapBroadcastReflect.UpdatePlayerMapIconBool.Invoke(value3, new object[1] { flag4 });
					if (CloakPaletteConfig.LogMapIconDiagnostics)
					{
						Log.Info($"[MapIcon] Queued PlayerMapUpdate hasIcon={flag4} " + $"(TryGetMapPosition={flag3}, alwaysShow={flag}, onlyCompass={flag2}, compassEquipped={IsCompassEquipped()}).");
					}
					if (!flag4)
					{
						MapBroadcastReflect.LastPosition.SetValue(__instance, Vector2.zero);
					}
				}
				if (!flag4 || (Object)(object)GameManager.instance == (Object)null || GameManager.instance.IsInSceneTransition)
				{
					return false;
				}
				if (!flag3)
				{
					if (CloakPaletteConfig.LogMapIconDiagnostics && Time.realtimeSinceStartup >= _nextLogDeferMapPos)
					{
						_nextLogDeferMapPos = Time.realtimeSinceStartup + 2f;
						Log.Info("[MapIcon] Network HasIcon is true but TryGetMapPosition=false (GameMap often null until the map opens); position packets deferred.");
					}
					return false;
				}
				Vector2 val = (Vector2)MapBroadcastReflect.LastPosition.GetValue(__instance);
				if (mapPosition == val)
				{
					return false;
				}
				object obj = MapBroadcastReflect.SsmpVecCtorFf.Invoke(new object[2] { mapPosition.x, mapPosition.y });
				object value4 = MapBroadcastReflect.NetUpdateManager.GetValue(value);
				if (value4 == null)
				{
					Log.Warn("[MapIcon] UpdateManager null while sending map position.");
					return false;
				}
				MapBroadcastReflect.InvokeOneArg[0] = obj;
				MapBroadcastReflect.UpdatePlayerMapPosition.Invoke(value4, MapBroadcastReflect.InvokeOneArg);
				MapBroadcastReflect.LastPosition.SetValue(__instance, mapPosition);
				if (CloakPaletteConfig.LogMapIconDiagnostics && Time.realtimeSinceStartup >= _nextLogMapPosSent)
				{
					_nextLogMapPosSent = Time.realtimeSinceStartup + 0.75f;
					Log.Info($"[MapIcon] Sent map position update ({mapPosition.x:F1}, {mapPosition.y:F1}).");
				}
				return false;
			}
			catch (Exception ex)
			{
				Log.Warn("SsmMapCompassBroadcastFix: falling back to vanilla MapManager update (" + ex.Message + ")");
				return true;
			}
		}

		private static bool TryInvokeTryGetMapPosition(object mapManager, out Vector2 mapPosition)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			mapPosition = Vector2.zero;
			MapBroadcastReflect.TryGetMapPosArgs[0] = Vector2.zero;
			bool result = (bool)MapBroadcastReflect.TryGetMapPosition.Invoke(mapManager, MapBroadcastReflect.TryGetMapPosArgs);
			mapPosition = (Vector2)MapBroadcastReflect.TryGetMapPosArgs[0];
			return result;
		}

		private static bool IsCompassEquipped()
		{
			object value = MapBroadcastReflect.GameplayCompassTool.GetValue(null);
			if (value == null)
			{
				return false;
			}
			return (bool)MapBroadcastReflect.CompassIsEquipped.GetValue(value);
		}
	}
	internal static class TextureDumper
	{
		private static readonly HashSet<int> _dumped = new HashSet<int>();

		private static string? _dumpDir;

		private static bool _warnedDir;

		public static void TryDump(Texture? tex, string source)
		{
			if (!CloakPaletteConfig.DumpDiscoveredTextures || (Object)(object)tex == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)tex).GetInstanceID();
			if (!_dumped.Add(instanceID))
			{
				return;
			}
			string text = EnsureDumpDir();
			if (text == null)
			{
				return;
			}
			Texture2D val = null;
			try
			{
				val = MakeReadable(tex);
				if ((Object)(object)val == (Object)null)
				{
					Log.Warn($"[Dumper] Could not blit '{((Object)tex).name}' (id={instanceID}, {tex.width}x{tex.height}) to a readable texture.");
					return;
				}
				byte[] array = ImageConversion.EncodeToPNG(val);
				if (array == null || array.Length == 0)
				{
					Log.Warn($"[Dumper] EncodeToPNG returned no bytes for '{((Object)tex).name}' (id={instanceID}).");
					return;
				}
				string path = $"{Sanitize(((Object)tex).name)}_id{instanceID}_{tex.width}x{tex.height}_{Sanitize(source)}.png";
				string text2 = Path.Combine(text, path);
				File.WriteAllBytes(text2, array);
				Log.Info($"[Dumper] Wrote {text2} ({array.Length:N0} bytes).");
			}
			catch (Exception ex)
			{
				Log.Warn($"[Dumper] Failed to dump '{((Object)tex).name}' (id={instanceID}): {ex.Message}");
			}
			finally
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}

		private static Texture2D? MakeReadable(Texture src)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			int width = src.width;
			int height = src.height;
			if (width <= 0 || height <= 0)
			{
				return null;
			}
			RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)1);
			RenderTexture active = RenderTexture.active;
			try
			{
				Graphics.Blit(src, temporary);
				RenderTexture.active = temporary;
				Texture2D val = new Texture2D(width, height, (TextureFormat)4, false);
				val.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0);
				val.Apply(false, false);
				return val;
			}
			finally
			{
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
			}
		}

		private static string? EnsureDumpDir()
		{
			if (_dumpDir != null)
			{
				return _dumpDir;
			}
			try
			{
				string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
				if (string.IsNullOrEmpty(directoryName))
				{
					if (!_warnedDir)
					{
						Log.Warn("[Dumper] Could not resolve assembly directory.");
						_warnedDir = true;
					}
					return null;
				}
				_dumpDir = Path.Combine(directoryName, "TextureDumps");
				Directory.CreateDirectory(_dumpDir);
				return _dumpDir;
			}
			catch (Exception ex)
			{
				if (!_warnedDir)
				{
					Log.Warn("[Dumper] Could not create dump folder: " + ex.Message);
					_warnedDir = true;
				}
				return null;
			}
		}

		private static string Sanitize(string? name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return "tex";
			}
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			return string.Join("_", name.Split(invalidFileNameChars));
		}
	}
}

HornetCloakColor.SSMP.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using HornetCloakColor.Client;
using HornetCloakColor.Server;
using HornetCloakColor.Shared;
using Microsoft.CodeAnalysis;
using SSMP.Api.Client;
using SSMP.Api.Client.Networking;
using SSMP.Api.Server;
using SSMP.Api.Server.Networking;
using SSMP.Networking.Packet;
using UnityEngine;

[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("HornetCloakColor.SSMP")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+721a346d2be5cba92c78662e0edfe9b430b63075")]
[assembly: AssemblyProduct("HornetCloakColor.SSMP")]
[assembly: AssemblyTitle("HornetCloakColor.SSMP")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 HornetCloakColor.SSMPIntegration
{
	public static class SatelliteEntry
	{
		public static void Register()
		{
			ClientAddon clientAddon = new ClientAddon();
			ServerAddon serverAddon = new ServerAddon();
			ClientAddon.RegisterAddon((ClientAddon)(object)clientAddon);
			ServerAddon.RegisterAddon((ServerAddon)(object)serverAddon);
		}

		public static void NotifyLocalColorChanged(CloakColor color)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			ClientAddon.Instance?.SetLocalColor(color);
		}

		public static CloakColor GetRemoteMapColorOrDefault(ushort playerId)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			return ClientAddon.Instance?.GetRemoteMapColorOrDefault(playerId) ?? CloakColor.Default;
		}
	}
}
namespace HornetCloakColor.Shared
{
	internal enum PacketId
	{
		CloakColorUpdate
	}
	internal class CloakColorPacket : IPacketData
	{
		public ushort PlayerId;

		public CloakColor Color;

		public bool IsReliable => true;

		public bool DropReliableDataIfNewerExists => true;

		public void WriteData(IPacket packet)
		{
			packet.Write(PlayerId);
			packet.Write(((CloakColor)(ref Color)).R);
			packet.Write(((CloakColor)(ref Color)).G);
			packet.Write(((CloakColor)(ref Color)).B);
		}

		public void ReadData(IPacket packet)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			PlayerId = packet.ReadUShort();
			byte b = packet.ReadByte();
			byte b2 = packet.ReadByte();
			byte b3 = packet.ReadByte();
			Color = new CloakColor(b, b2, b3);
		}
	}
	internal static class PacketFactory
	{
		public static IPacketData Instantiate(PacketId id)
		{
			if (id == PacketId.CloakColorUpdate)
			{
				return (IPacketData)(object)new CloakColorPacket();
			}
			return (IPacketData)(object)new CloakColorPacket();
		}
	}
}
namespace HornetCloakColor.Server
{
	internal class ServerAddon : ServerAddon
	{
		private readonly Dictionary<ushort, CloakColor> _playerColors = new Dictionary<ushort, CloakColor>();

		private readonly HashSet<ushort> _seededPlayers = new HashSet<ushort>();

		private IServerApi? _api;

		private IServerAddonNetworkSender<PacketId>? _sender;

		protected override string Name => "HornetCloakColor";

		protected override string Version => "1.7.3";

		public override uint ApiVersion => 1u;

		public override bool NeedsNetwork => true;

		public override void Initialize(IServerApi serverApi)
		{
			_api = serverApi;
			_sender = serverApi.NetServer.GetNetworkSender<PacketId>((ServerAddon)(object)this);
			serverApi.NetServer.GetNetworkReceiver<PacketId>((ServerAddon)(object)this, (Func<PacketId, IPacketData>)PacketFactory.Instantiate).RegisterPacketHandler<CloakColorPacket>(PacketId.CloakColorUpdate, (GenericServerPacketHandler<CloakColorPacket>)OnCloakColorUpdate);
			serverApi.ServerManager.PlayerEnterSceneEvent += OnPlayerEnterScene;
			serverApi.ServerManager.PlayerDisconnectEvent += OnPlayerDisconnect;
			Log.Info("Server addon initialized.");
		}

		private void OnPlayerEnterScene(IServerPlayer player)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (_sender == null || !_seededPlayers.Add(player.Id))
			{
				return;
			}
			int num = 0;
			foreach (KeyValuePair<ushort, CloakColor> playerColor in _playerColors)
			{
				if (playerColor.Key != player.Id)
				{
					_sender.SendSingleData(PacketId.CloakColorUpdate, (IPacketData)(object)new CloakColorPacket
					{
						PlayerId = playerColor.Key,
						Color = playerColor.Value
					}, player.Id);
					num++;
				}
			}
			Log.Info($"Seeded {num} cloak color(s) to newly-arrived player {player.Id}.");
		}

		private void OnPlayerDisconnect(IServerPlayer player)
		{
			_playerColors.Remove(player.Id);
			_seededPlayers.Remove(player.Id);
		}

		private void OnCloakColorUpdate(ushort senderId, CloakColorPacket data)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			_playerColors[senderId] = data.Color;
			if (_api == null || _sender == null)
			{
				return;
			}
			foreach (IServerPlayer player in _api.ServerManager.Players)
			{
				if (player.Id != senderId)
				{
					_sender.SendSingleData(PacketId.CloakColorUpdate, (IPacketData)(object)new CloakColorPacket
					{
						PlayerId = senderId,
						Color = data.Color
					}, player.Id);
				}
			}
		}
	}
}
namespace HornetCloakColor.Client
{
	internal class ClientAddon : ClientAddon
	{
		private readonly Dictionary<ushort, CloakColor> _playerColors = new Dictionary<ushort, CloakColor>();

		private IClientApi? _api;

		private IClientAddonNetworkSender<PacketId>? _sender;

		private CloakColor _localColor = CloakColor.Default;

		protected override string Name => "HornetCloakColor";

		protected override string Version => "1.7.3";

		public override uint ApiVersion => 1u;

		public override bool NeedsNetwork => true;

		internal static ClientAddon? Instance { get; private set; }

		public override void Initialize(IClientApi clientApi)
		{
			Instance = this;
			_api = clientApi;
			_sender = clientApi.NetClient.GetNetworkSender<PacketId>((ClientAddon)(object)this);
			clientApi.NetClient.GetNetworkReceiver<PacketId>((ClientAddon)(object)this, (Func<PacketId, IPacketData>)PacketFactory.Instantiate).RegisterPacketHandler<CloakColorPacket>(PacketId.CloakColorUpdate, (GenericClientPacketHandler<CloakColorPacket>)OnCloakColorUpdate);
			clientApi.ClientManager.ConnectEvent += OnConnected;
			clientApi.ClientManager.PlayerEnterSceneEvent += OnPlayerEnterScene;
			clientApi.ClientManager.PlayerDisconnectEvent += OnPlayerDisconnect;
			Log.Info("Client addon initialized.");
		}

		public void SetLocalColor(CloakColor color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			_localColor = color;
			SendLocalColor();
		}

		internal CloakColor GetRemoteMapColorOrDefault(ushort playerId)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (!_playerColors.TryGetValue(playerId, out var value))
			{
				return CloakColor.Default;
			}
			return value;
		}

		private void SendLocalColor()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (_api != null && _sender != null && _api.NetClient.IsConnected)
			{
				_sender.SendSingleData(PacketId.CloakColorUpdate, (IPacketData)(object)new CloakColorPacket
				{
					PlayerId = 0,
					Color = _localColor
				});
			}
		}

		private void OnConnected()
		{
			SendLocalColor();
		}

		private void OnPlayerEnterScene(IClientPlayer player)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (_playerColors.TryGetValue(player.Id, out var value))
			{
				CloakColorApplier.Apply(player.PlayerObject, value);
			}
		}

		private void OnPlayerDisconnect(IClientPlayer player)
		{
			_playerColors.Remove(player.Id);
		}

		private void OnCloakColorUpdate(CloakColorPacket data)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			_playerColors[data.PlayerId] = data.Color;
			PlayerMapMaskTintRegistry.SetColor(data.PlayerId, data.Color);
			IClientApi? api = _api;
			IClientPlayer val = ((api != null) ? api.ClientManager.GetPlayer(data.PlayerId) : null);
			if ((Object)(object)((val != null) ? val.PlayerObject : null) != (Object)null)
			{
				CloakColorApplier.Apply(val.PlayerObject, data.Color);
			}
		}
	}
}