Decompiled source of GlowNames v1.1.0

GlowNames.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("GlowNames")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+33956dea114c4d68693291e47a8b8666dfba1d34")]
[assembly: AssemblyProduct("GlowNames")]
[assembly: AssemblyTitle("GlowNames")]
[assembly: AssemblyVersion("1.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace GlowNames
{
	internal sealed class GameReflection
	{
		private static readonly string[] DeadFieldNames = new string[8] { "deadSet", "isDead", "dead", "isDeath", "death", "isKilled", "isDowned", "downed" };

		private readonly GlowNamesPlugin _plugin;

		private readonly Dictionary<object, object> _nameplatePlayerCache = new Dictionary<object, object>();

		private readonly Dictionary<string, FieldInfo> _deadFieldCache = new Dictionary<string, FieldInfo>();

		private Type _gameDirectorType;

		private Type _playerAvatarType;

		private Type _nameplateType;

		private Type _deathHeadType;

		private FieldInfo _gameDirectorInstanceField;

		private PropertyInfo _gameDirectorInstanceProperty;

		private FieldInfo _playerListField;

		private PropertyInfo _playerListProperty;

		private FieldInfo _playerIsLocalField;

		private FieldInfo _playerIsDisabledField;

		private FieldInfo _playerNameplateField;

		private FieldInfo _playerVisualsField;

		private FieldInfo _playerDeathHeadField;

		private FieldInfo _nameplatePlayerField;

		private FieldInfo _nameplateTextField;

		private FieldInfo _nameplateShowTimerField;

		private FieldInfo _nameplateWorldPositionField;

		private FieldInfo _deathHeadTriggeredField;

		private FieldInfo _deathHeadPhysGrabObjectField;

		private FieldInfo _physGrabObjectCenterPointField;

		private MethodInfo _nameplateShowMethod;

		internal Type PlayerAvatarType => _playerAvatarType;

		internal Type NameplateType => _nameplateType;

		internal GameReflection(GlowNamesPlugin plugin)
		{
			_plugin = plugin;
		}

		internal void CacheMembers()
		{
			_gameDirectorType = AccessTools.TypeByName("GameDirector");
			_playerAvatarType = AccessTools.TypeByName("PlayerAvatar");
			_nameplateType = AccessTools.TypeByName("WorldSpaceUIPlayerName");
			_deathHeadType = AccessTools.TypeByName("PlayerDeathHead");
			Type type = AccessTools.TypeByName("PhysGrabObject");
			if (_gameDirectorType != null)
			{
				_gameDirectorInstanceField = AccessTools.Field(_gameDirectorType, "instance");
				_gameDirectorInstanceProperty = AccessTools.Property(_gameDirectorType, "instance");
				_playerListField = AccessTools.Field(_gameDirectorType, "PlayerList");
				_playerListProperty = AccessTools.Property(_gameDirectorType, "PlayerList");
			}
			if (_playerAvatarType != null)
			{
				_playerIsLocalField = AccessTools.Field(_playerAvatarType, "isLocal");
				_playerIsDisabledField = AccessTools.Field(_playerAvatarType, "isDisabled");
				_playerNameplateField = AccessTools.Field(_playerAvatarType, "worldSpaceUIPlayerName");
				_playerVisualsField = AccessTools.Field(_playerAvatarType, "playerAvatarVisuals");
				_playerDeathHeadField = AccessTools.Field(_playerAvatarType, "playerDeathHead");
			}
			if (_nameplateType != null)
			{
				_nameplatePlayerField = AccessTools.Field(_nameplateType, "playerAvatar");
				_nameplateTextField = AccessTools.Field(_nameplateType, "text");
				_nameplateShowTimerField = AccessTools.Field(_nameplateType, "showTimer");
				_nameplateWorldPositionField = AccessTools.Field(_nameplateType, "worldPosition") ?? AccessTools.Field(_nameplateType.BaseType, "worldPosition");
				_nameplateShowMethod = AccessTools.Method(_nameplateType, "Show", (Type[])null, (Type[])null);
			}
			if (_deathHeadType != null)
			{
				_deathHeadTriggeredField = AccessTools.Field(_deathHeadType, "triggered");
				_deathHeadPhysGrabObjectField = AccessTools.Field(_deathHeadType, "physGrabObject");
			}
			if (type != null)
			{
				_physGrabObjectCenterPointField = AccessTools.Field(type, "centerPoint");
			}
			WarnMissing(_gameDirectorType, "GameDirector type");
			WarnMissing(_playerAvatarType, "PlayerAvatar type");
			WarnMissing(_nameplateType, "WorldSpaceUIPlayerName type");
			WarnMissing(_playerIsLocalField, "PlayerAvatar.isLocal");
			WarnMissing(_playerIsDisabledField, "PlayerAvatar.isDisabled");
			WarnMissing(_playerNameplateField, "PlayerAvatar.worldSpaceUIPlayerName");
			WarnMissing(_playerVisualsField, "PlayerAvatar.playerAvatarVisuals");
			WarnMissing(_playerDeathHeadField, "PlayerAvatar.playerDeathHead");
			WarnMissing(_nameplatePlayerField, "WorldSpaceUIPlayerName.playerAvatar");
			WarnMissing(_nameplateTextField, "WorldSpaceUIPlayerName.text");
			WarnMissing(_nameplateShowTimerField, "WorldSpaceUIPlayerName.showTimer");
			WarnMissing(_nameplateWorldPositionField, "WorldSpaceUIChild.worldPosition");
			WarnMissing(_nameplateShowMethod, "WorldSpaceUIPlayerName.Show");
			WarnMissing(_deathHeadType, "PlayerDeathHead type");
			WarnMissing(_deathHeadTriggeredField, "PlayerDeathHead.triggered");
			WarnMissing(_deathHeadPhysGrabObjectField, "PlayerDeathHead.physGrabObject");
			WarnMissing(_physGrabObjectCenterPointField, "PhysGrabObject.centerPoint");
		}

		internal IList GetPlayerList()
		{
			try
			{
				object obj = ((_gameDirectorInstanceField != null) ? _gameDirectorInstanceField.GetValue(null) : _gameDirectorInstanceProperty?.GetValue(null, null));
				if (obj == null)
				{
					return null;
				}
				return ((_playerListField != null) ? _playerListField.GetValue(obj) : _playerListProperty?.GetValue(obj, null)) as IList;
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("player-list", "Failed to read GameDirector.instance.PlayerList: " + ex.Message, 10f, (LogLevel)4);
				return null;
			}
		}

		internal object FindLocalPlayer(IList players)
		{
			int num = 0;
			while (players != null && num < players.Count)
			{
				object obj = players[num];
				if (!IsNullOrDestroyed(obj) && IsLocalPlayer(obj))
				{
					return obj;
				}
				num++;
			}
			return null;
		}

		internal bool IsLocalPlayer(object player)
		{
			return GetBoolField(player, _playerIsLocalField, fallback: false);
		}

		internal bool IsDisabledPlayer(object player)
		{
			return GetBoolField(player, _playerIsDisabledField, fallback: false);
		}

		internal object GetNameplate(object player)
		{
			if (IsNullOrDestroyed(player) || _playerNameplateField == null)
			{
				return null;
			}
			try
			{
				object value = _playerNameplateField.GetValue(player);
				return IsNullOrDestroyed(value) ? null : value;
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("get-nameplate", "Failed to read PlayerAvatar.worldSpaceUIPlayerName: " + ex.Message, 10f, (LogLevel)4);
				return null;
			}
		}

		internal object GetPlayerForNameplate(object nameplate)
		{
			if (IsNullOrDestroyed(nameplate))
			{
				return null;
			}
			if (_nameplatePlayerCache.TryGetValue(nameplate, out var value) && !IsNullOrDestroyed(value))
			{
				return value;
			}
			if (_nameplatePlayerField != null)
			{
				try
				{
					object value2 = _nameplatePlayerField.GetValue(nameplate);
					if (!IsNullOrDestroyed(value2))
					{
						_nameplatePlayerCache[nameplate] = value2;
						return value2;
					}
				}
				catch (Exception ex)
				{
					_plugin.LogThrottled("get-nameplate-player", "Failed to read WorldSpaceUIPlayerName.playerAvatar: " + ex.Message, 10f, (LogLevel)4);
				}
			}
			IList playerList = GetPlayerList();
			int num = 0;
			while (playerList != null && num < playerList.Count)
			{
				object obj = playerList[num];
				if (!IsNullOrDestroyed(obj) && GetNameplate(obj) == nameplate)
				{
					_nameplatePlayerCache[nameplate] = obj;
					return obj;
				}
				num++;
			}
			return null;
		}

		internal TextMeshProUGUI GetText(object nameplate)
		{
			if (IsNullOrDestroyed(nameplate) || _nameplateTextField == null)
			{
				return null;
			}
			try
			{
				object? value = _nameplateTextField.GetValue(nameplate);
				return (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null);
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("read-text", "Failed to read WorldSpaceUIPlayerName.text: " + ex.Message, 10f, (LogLevel)4);
				return null;
			}
		}

		internal void ShowNameplate(object nameplate)
		{
			if (!IsNullOrDestroyed(nameplate))
			{
				_nameplateShowMethod?.Invoke(nameplate, null);
			}
		}

		internal void SetShowTimer(object nameplate, float value)
		{
			if (IsNullOrDestroyed(nameplate) || _nameplateShowTimerField == null)
			{
				return;
			}
			try
			{
				_nameplateShowTimerField.SetValue(nameplate, Mathf.Max(0f, value));
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("set-show-timer", "Failed to set WorldSpaceUIPlayerName.showTimer: " + ex.Message, 10f, (LogLevel)4);
			}
		}

		internal Vector3 GetPosition(object obj)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Component val = (Component)((obj is Component) ? obj : null);
				return ((Object)(object)val != (Object)null) ? val.transform.position : Vector3.zero;
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("get-position", "Failed to read object position: " + ex.Message, 10f, (LogLevel)4);
				return Vector3.zero;
			}
		}

		internal Vector3 GetFallbackOrigin()
		{
			//IL_001b: 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)
			Camera main = Camera.main;
			if (!((Object)(object)main != (Object)null))
			{
				return Vector3.zero;
			}
			return ((Component)main).transform.position;
		}

		internal Vector3 GetLabelWorldPosition(object player, object nameplate)
		{
			//IL_000b: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (TryGetDeathHeadLabelPosition(player, out var position))
			{
				return position;
			}
			if (!IsNullOrDestroyed(nameplate) && _nameplateWorldPositionField != null)
			{
				try
				{
					object value = _nameplateWorldPositionField.GetValue(nameplate);
					if (value is Vector3)
					{
						return (Vector3)value;
					}
				}
				catch (Exception ex)
				{
					_plugin.LogThrottled("get-label-position", "Failed to read nameplate worldPosition: " + ex.Message, 10f, (LogLevel)4);
				}
			}
			return GetPosition(player) + Vector3.up * 1.8f;
		}

		internal Vector3 GetTrackingPosition(object player)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (!TryGetDeathHeadLabelPosition(player, out var position))
			{
				return GetPosition(player);
			}
			return position;
		}

		internal bool HasValidDeathHead(object player)
		{
			Vector3 position;
			return TryGetDeathHeadLabelPosition(player, out position);
		}

		internal void UpdateNameplateWorldPosition(object player, object nameplate)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (IsNullOrDestroyed(nameplate) || _nameplateWorldPositionField == null || !TryGetDeathHeadLabelPosition(player, out var position))
			{
				return;
			}
			try
			{
				_nameplateWorldPositionField.SetValue(nameplate, position);
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("set-label-position", "Failed to set nameplate worldPosition: " + ex.Message, 10f, (LogLevel)4);
			}
		}

		internal bool IsDeadPlayer(object player)
		{
			if (IsNullOrDestroyed(player))
			{
				return false;
			}
			Type type = player.GetType();
			string key = type.FullName ?? type.Name;
			if (!_deadFieldCache.TryGetValue(key, out var value))
			{
				value = null;
				for (int i = 0; i < DeadFieldNames.Length; i++)
				{
					value = AccessTools.Field(type, DeadFieldNames[i]);
					if (value != null && value.FieldType == typeof(bool))
					{
						break;
					}
					value = null;
				}
				_deadFieldCache[key] = value;
			}
			if (value != null)
			{
				return GetBoolField(player, value, fallback: false);
			}
			return false;
		}

		internal bool IsPlayerCollider(Collider collider, object player)
		{
			if ((Object)(object)collider == (Object)null || IsNullOrDestroyed(player))
			{
				return false;
			}
			Component val = (Component)((player is Component) ? player : null);
			if ((Object)(object)val != (Object)null && ((Component)collider).transform.IsChildOf(val.transform))
			{
				return true;
			}
			Transform playerVisualsTransform = GetPlayerVisualsTransform(player);
			if ((Object)(object)playerVisualsTransform != (Object)null && ((Component)collider).transform.IsChildOf(playerVisualsTransform))
			{
				return true;
			}
			Transform playerDeathHeadTransform = GetPlayerDeathHeadTransform(player);
			if ((Object)(object)playerDeathHeadTransform != (Object)null && ((Component)collider).transform.IsChildOf(playerDeathHeadTransform))
			{
				return true;
			}
			if (_playerAvatarType != null)
			{
				Component componentInParent = ((Component)collider).GetComponentInParent(_playerAvatarType);
				if ((Object)(object)componentInParent != (Object)null && componentInParent == player)
				{
					return true;
				}
			}
			return false;
		}

		private bool TryGetDeathHeadLabelPosition(object player, out Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			if (!IsDisabledPlayer(player) || !IsDeadPlayer(player))
			{
				return false;
			}
			object playerDeathHead = GetPlayerDeathHead(player);
			if (IsNullOrDestroyed(playerDeathHead) || !GetBoolField(playerDeathHead, _deathHeadTriggeredField, fallback: false))
			{
				return false;
			}
			object obj = null;
			if (_deathHeadPhysGrabObjectField != null)
			{
				try
				{
					obj = _deathHeadPhysGrabObjectField.GetValue(playerDeathHead);
				}
				catch (Exception ex)
				{
					_plugin.LogThrottled("get-deathhead-physgrab", "Failed to read PlayerDeathHead.physGrabObject: " + ex.Message, 10f, (LogLevel)4);
				}
			}
			if (!IsNullOrDestroyed(obj) && _physGrabObjectCenterPointField != null)
			{
				try
				{
					if (_physGrabObjectCenterPointField.GetValue(obj) is Vector3 val)
					{
						position = val + Vector3.up * 0.45f;
						return true;
					}
				}
				catch (Exception ex2)
				{
					_plugin.LogThrottled("get-deathhead-center", "Failed to read PhysGrabObject.centerPoint: " + ex2.Message, 10f, (LogLevel)4);
				}
			}
			Component val2 = (Component)((playerDeathHead is Component) ? playerDeathHead : null);
			if ((Object)(object)val2 != (Object)null)
			{
				position = val2.transform.position + Vector3.up * 0.5f;
				return true;
			}
			return false;
		}

		private object GetPlayerDeathHead(object player)
		{
			if (IsNullOrDestroyed(player) || _playerDeathHeadField == null)
			{
				return null;
			}
			try
			{
				object value = _playerDeathHeadField.GetValue(player);
				return IsNullOrDestroyed(value) ? null : value;
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("get-deathhead", "Failed to read PlayerAvatar.playerDeathHead: " + ex.Message, 10f, (LogLevel)4);
				return null;
			}
		}

		private Transform GetPlayerDeathHeadTransform(object player)
		{
			object playerDeathHead = GetPlayerDeathHead(player);
			Component val = (Component)((playerDeathHead is Component) ? playerDeathHead : null);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.transform;
		}

		private Transform GetPlayerVisualsTransform(object player)
		{
			if (IsNullOrDestroyed(player) || _playerVisualsField == null)
			{
				return null;
			}
			try
			{
				object? value = _playerVisualsField.GetValue(player);
				Component val = (Component)((value is Component) ? value : null);
				return ((Object)(object)val != (Object)null) ? val.transform : null;
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("get-player-visuals", "Failed to read PlayerAvatar.playerAvatarVisuals: " + ex.Message, 10f, (LogLevel)4);
				return null;
			}
		}

		internal static bool IsNullOrDestroyed(object obj)
		{
			if (obj == null)
			{
				return true;
			}
			Object val = (Object)((obj is Object) ? obj : null);
			if (val != null)
			{
				return val == (Object)null;
			}
			return false;
		}

		private bool GetBoolField(object instance, FieldInfo field, bool fallback)
		{
			if (IsNullOrDestroyed(instance) || field == null)
			{
				return fallback;
			}
			try
			{
				return (field.GetValue(instance) is bool flag) ? flag : fallback;
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("bool-field-" + field.Name, "Failed to read " + field.DeclaringType?.Name + "." + field.Name + ": " + ex.Message, 10f, (LogLevel)4);
				return fallback;
			}
		}

		private void WarnMissing(object value, string name)
		{
			if (value == null)
			{
				_plugin.LogWarning("Could not find " + name + "; GlowNames will use fallbacks where possible.");
			}
		}
	}
	internal sealed class GlowNamesConfig
	{
		private static readonly string[] ColorOptions = new string[10] { "White", "Black", "Yellow", "Cyan", "Red", "Green", "Blue", "Orange", "Purple", "Pink" };

		internal ConfigEntry<float> Range { get; }

		internal ConfigEntry<float> Alpha { get; }

		internal ConfigEntry<float> BaseFontSize { get; }

		internal ConfigEntry<bool> IncludeDeadPlayers { get; }

		internal ConfigEntry<string> TextColor { get; }

		internal ConfigEntry<bool> OutlineEnabled { get; }

		internal ConfigEntry<string> OutlineColor { get; }

		internal ConfigEntry<float> OutlineWidth { get; }

		internal ConfigEntry<bool> OcclusionEnabled { get; }

		internal ConfigEntry<float> OcclusionCheckInterval { get; }

		internal GlowNamesConfig(ConfigFile config)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Expected O, but got Unknown
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Expected O, but got Unknown
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Expected O, but got Unknown
			Range = config.Bind<float>("Display", "Range", 20f, new ConfigDescription("Maximum distance from the local player to reveal names.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 500f), Array.Empty<object>()));
			Alpha = config.Bind<float>("Display", "Alpha", 1f, new ConfigDescription("Text alpha for revealed names.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			BaseFontSize = config.Bind<float>("Display", "BaseFontSize", 20f, new ConfigDescription("Base value for the distance size formula: fontSize = BaseFontSize - camera distance. Applies to living-player and death-head names.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			IncludeDeadPlayers = config.Bind<bool>("Filters", "IncludeDeadPlayers", true, "If true, include players that expose a dead/downed flag such as deadSet.");
			TextColor = config.Bind<string>("Text", "Color", "White", new ConfigDescription("Text color while GlowNames is active.", (AcceptableValueBase)(object)new AcceptableValueList<string>(ColorOptions), Array.Empty<object>()));
			OutlineEnabled = config.Bind<bool>("Outline", "Enabled", false, "If true, apply TMP outline while GlowNames is active. Disabled by default for plain white text.");
			OutlineColor = config.Bind<string>("Outline", "Color", "White", new ConfigDescription("TMP outline color while GlowNames is active. Ignored when Outline.Enabled is false.", (AcceptableValueBase)(object)new AcceptableValueList<string>(ColorOptions), Array.Empty<object>()));
			OutlineWidth = config.Bind<float>("Outline", "Width", 0f, new ConfigDescription("TMP outline width while GlowNames is active. Ignored when Outline.Enabled is false.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			OcclusionEnabled = config.Bind<bool>("Occlusion", "Enabled", true, "If true, names hidden behind walls or large physics objects will not be revealed.");
			OcclusionCheckInterval = config.Bind<float>("Occlusion", "CheckInterval", 0.1f, new ConfigDescription("Seconds between line-of-sight checks per nameplate. Lower values react faster but do more raycasts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 1f), Array.Empty<object>()));
		}

		internal Color GetOutlineColor()
		{
			//IL_000b: 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)
			return GetNamedColor(OutlineColor.Value, Color.white);
		}

		internal Color GetTextColor()
		{
			//IL_000b: 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)
			return GetNamedColor(TextColor.Value, Color.white);
		}

		private static Color GetNamedColor(string value, Color fallback)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: 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)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(value))
			{
				return fallback;
			}
			value = value.Trim();
			switch (value.ToLowerInvariant())
			{
			case "white":
				return Color.white;
			case "black":
				return Color.black;
			case "yellow":
				return Color.yellow;
			case "cyan":
				return Color.cyan;
			case "red":
				return Color.red;
			case "green":
				return Color.green;
			case "blue":
				return Color.blue;
			case "orange":
				return new Color(1f, 0.5f, 0f, 1f);
			case "purple":
				return new Color(0.65f, 0.35f, 1f, 1f);
			case "pink":
				return new Color(1f, 0.35f, 0.75f, 1f);
			default:
			{
				if (!value.StartsWith("#", StringComparison.Ordinal))
				{
					value = "#" + value;
				}
				Color result = default(Color);
				if (!ColorUtility.TryParseHtmlString(value, ref result))
				{
					return fallback;
				}
				return result;
			}
			}
		}
	}
	internal static class HarmonyPatches
	{
		internal static void PatchNameplateUpdate(Harmony harmony, Type nameplateType, ManualLogSource logger)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = ((nameplateType != null) ? AccessTools.Method(nameplateType, "Update", (Type[])null, (Type[])null) : null);
				MethodInfo methodInfo2 = AccessTools.Method(typeof(HarmonyPatches), "WorldSpaceUIPlayerNameUpdatePostfix", (Type[])null, (Type[])null);
				if (methodInfo == null || methodInfo2 == null)
				{
					logger.LogWarning((object)"Could not patch WorldSpaceUIPlayerName.Update; active refresh will still call Show(), but styling may be overwritten by the game.");
				}
				else
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				logger.LogWarning((object)("Could not patch WorldSpaceUIPlayerName.Update: " + ex.Message + ". Active refresh will still call Show(), but styling may be overwritten by the game."));
			}
		}

		private static void WorldSpaceUIPlayerNameUpdatePostfix(object __instance)
		{
			GlowNamesPlugin.Instance?.OnNameplateUpdated(__instance);
		}
	}
	internal sealed class LogThrottle
	{
		private readonly ManualLogSource _log;

		private readonly Dictionary<string, float> _nextLogTimeByKey = new Dictionary<string, float>();

		internal LogThrottle(ManualLogSource log)
		{
			_log = log;
		}

		internal void Log(string key, string message, float intervalSeconds, LogLevel level)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (!_nextLogTimeByKey.TryGetValue(key, out var value) || !(realtimeSinceStartup < value))
			{
				_nextLogTimeByKey[key] = realtimeSinceStartup + Mathf.Max(0.1f, intervalSeconds);
				ManualLogSource log = _log;
				if (log != null)
				{
					log.Log(level, (object)message);
				}
			}
		}
	}
	internal sealed class NameplateHighlighter
	{
		private struct TextState
		{
			public Color Color;

			public float FontSize;

			public Color OutlineColor;

			public float OutlineWidth;
		}

		private readonly GlowNamesPlugin _plugin;

		private readonly Dictionary<TextMeshProUGUI, TextState> _originalStateByText = new Dictionary<TextMeshProUGUI, TextState>();

		internal NameplateHighlighter(GlowNamesPlugin plugin)
		{
			_plugin = plugin;
		}

		internal void Apply(TextMeshProUGUI text, GlowNamesConfig settings, float? fontSizeOverride)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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)
			if (!((Object)(object)text == (Object)null))
			{
				CacheOriginalState(text);
				Color textColor = settings.GetTextColor();
				textColor.a = Mathf.Clamp01(settings.Alpha.Value);
				((Graphic)text).color = textColor;
				if (fontSizeOverride.HasValue)
				{
					((TMP_Text)text).fontSize = fontSizeOverride.Value;
				}
				if (settings.OutlineEnabled.Value)
				{
					((TMP_Text)text).outlineColor = Color32.op_Implicit(settings.GetOutlineColor());
					((TMP_Text)text).outlineWidth = Mathf.Clamp01(settings.OutlineWidth.Value);
				}
				else
				{
					RestoreOutline(text);
				}
			}
		}

		internal void Hide(TextMeshProUGUI text)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)text == (Object)null))
			{
				CacheOriginalState(text);
				Color color = ((Graphic)text).color;
				color.a = 0f;
				((Graphic)text).color = color;
			}
		}

		internal void RestoreOutline(TextMeshProUGUI text)
		{
			//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)
			if ((Object)(object)text == (Object)null || !_originalStateByText.TryGetValue(text, out var value))
			{
				return;
			}
			try
			{
				((TMP_Text)text).outlineColor = Color32.op_Implicit(value.OutlineColor);
				((TMP_Text)text).outlineWidth = value.OutlineWidth;
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("restore-outline", "Failed to restore TMP outline: " + ex.Message, 10f, (LogLevel)4);
			}
		}

		internal void RestoreAll()
		{
			if (_originalStateByText.Count != 0)
			{
				List<TextMeshProUGUI> list = new List<TextMeshProUGUI>(_originalStateByText.Keys);
				for (int i = 0; i < list.Count; i++)
				{
					Restore(list[i]);
				}
				_originalStateByText.Clear();
			}
		}

		internal void Restore(TextMeshProUGUI text)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)text == (Object)null || !_originalStateByText.TryGetValue(text, out var value))
			{
				return;
			}
			try
			{
				((Graphic)text).color = value.Color;
				((TMP_Text)text).fontSize = value.FontSize;
				((TMP_Text)text).outlineColor = Color32.op_Implicit(value.OutlineColor);
				((TMP_Text)text).outlineWidth = value.OutlineWidth;
			}
			catch (Exception ex)
			{
				_plugin.LogThrottled("restore-text-state", "Failed to restore TMP text state: " + ex.Message, 10f, (LogLevel)4);
			}
			finally
			{
				_originalStateByText.Remove(text);
			}
		}

		private void CacheOriginalState(TextMeshProUGUI text)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			if (!((Object)(object)text == (Object)null) && !_originalStateByText.ContainsKey(text))
			{
				_originalStateByText[text] = new TextState
				{
					Color = ((Graphic)text).color,
					FontSize = ((TMP_Text)text).fontSize,
					OutlineColor = Color32.op_Implicit(((TMP_Text)text).outlineColor),
					OutlineWidth = ((TMP_Text)text).outlineWidth
				};
			}
		}
	}
	internal sealed class NameplateOcclusion
	{
		private struct OcclusionState
		{
			public bool IsOccluded;

			public float NextCheckTime;
		}

		private static readonly string[] OcclusionLayerNames = new string[5] { "Default", "StaticGrabObject", "PhysGrabObject", "PhysGrabObjectCart", "PhysGrabObjectHinge" };

		private readonly GlowNamesPlugin _plugin;

		private readonly Dictionary<object, OcclusionState> _stateByNameplate = new Dictionary<object, OcclusionState>();

		private readonly int _occlusionMask;

		internal NameplateOcclusion(GlowNamesPlugin plugin)
		{
			_plugin = plugin;
			_occlusionMask = BuildOcclusionMask();
		}

		internal bool IsVisible(object player, object nameplate)
		{
			if (!_plugin.Settings.OcclusionEnabled.Value || _occlusionMask == 0)
			{
				return true;
			}
			object obj = nameplate ?? player;
			if (obj == null)
			{
				return true;
			}
			if (!_stateByNameplate.TryGetValue(obj, out var value) || Time.time >= value.NextCheckTime)
			{
				value.IsOccluded = IsOccluded(player, nameplate);
				value.NextCheckTime = Time.time + Mathf.Max(0.02f, _plugin.Settings.OcclusionCheckInterval.Value);
				_stateByNameplate[obj] = value;
			}
			return !value.IsOccluded;
		}

		internal void ClearCache()
		{
			_stateByNameplate.Clear();
		}

		private bool IsOccluded(object player, object nameplate)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return false;
			}
			Vector3 labelWorldPosition = _plugin.Game.GetLabelWorldPosition(player, nameplate);
			Vector3 position = ((Component)main).transform.position;
			Vector3 val = labelWorldPosition - position;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude <= Mathf.Epsilon)
			{
				return false;
			}
			RaycastHit[] array = Physics.RaycastAll(position, ((Vector3)(ref val)).normalized, magnitude, _occlusionMask, (QueryTriggerInteraction)1);
			for (int i = 0; i < array.Length; i++)
			{
				Collider collider = ((RaycastHit)(ref array[i])).collider;
				if (!((Object)(object)collider == (Object)null) && !_plugin.Game.IsPlayerCollider(collider, player))
				{
					return true;
				}
			}
			return false;
		}

		private static int BuildOcclusionMask()
		{
			int num = 0;
			for (int i = 0; i < OcclusionLayerNames.Length; i++)
			{
				int num2 = LayerMask.NameToLayer(OcclusionLayerNames[i]);
				if (num2 >= 0)
				{
					num |= 1 << num2;
				}
			}
			return num;
		}
	}
	[BepInPlugin("com.zhuanban.glownames", "GlowNames", "1.1.0")]
	public sealed class GlowNamesPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.zhuanban.glownames";

		public const string PluginName = "GlowNames";

		public const string PluginVersion = "1.1.0";

		private NameplateHighlighter _highlighter;

		private NameplateOcclusion _occlusion;

		private LogThrottle _logThrottle;

		private Harmony _harmony;

		private const float ForcedShowTimer = 0.5f;

		internal static GlowNamesPlugin Instance { get; private set; }

		internal GlowNamesConfig Settings { get; private set; }

		internal GameReflection Game { get; private set; }

		private void Awake()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			Instance = this;
			_logThrottle = new LogThrottle(((BaseUnityPlugin)this).Logger);
			Settings = new GlowNamesConfig(((BaseUnityPlugin)this).Config);
			Game = new GameReflection(this);
			_highlighter = new NameplateHighlighter(this);
			_occlusion = new NameplateOcclusion(this);
			Game.CacheMembers();
			_harmony = new Harmony("com.zhuanban.glownames");
			HarmonyPatches.PatchNameplateUpdate(_harmony, Game.NameplateType, ((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"GlowNames 1.1.0 loaded. Player names are always shown while visible and in range.");
		}

		private void OnDestroy()
		{
			_highlighter?.RestoreAll();
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to unpatch Harmony cleanly: " + ex.Message));
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void Update()
		{
			try
			{
				RefreshVisibleNameplates();
			}
			catch (Exception arg)
			{
				LogThrottled("update", $"GlowNames Update failed: {arg}", 5f, (LogLevel)4);
			}
		}

		private void RefreshVisibleNameplates()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			IList playerList = Game.GetPlayerList();
			if (playerList == null || playerList.Count == 0)
			{
				return;
			}
			object obj = Game.FindLocalPlayer(playerList);
			Vector3 origin = ((obj != null) ? Game.GetPosition(obj) : Game.GetFallbackOrigin());
			for (int i = 0; i < playerList.Count; i++)
			{
				object player = playerList[i];
				if (!IsEligiblePlayer(player, obj, origin))
				{
					continue;
				}
				object nameplate = Game.GetNameplate(player);
				if (nameplate == null)
				{
					continue;
				}
				Game.UpdateNameplateWorldPosition(player, nameplate);
				if (!_occlusion.IsVisible(player, nameplate))
				{
					HideNameplate(nameplate);
					continue;
				}
				try
				{
					Game.ShowNameplate(nameplate);
					Game.SetShowTimer(nameplate, 0.5f);
					ApplyHighlight(player, nameplate);
				}
				catch (Exception ex)
				{
					LogThrottled("show-nameplate", "Failed to show a player nameplate: " + ex.Message, 5f, (LogLevel)4);
				}
			}
		}

		internal void OnNameplateUpdated(object nameplate)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (nameplate == null)
				{
					RestoreOutline(nameplate);
					return;
				}
				object playerForNameplate = Game.GetPlayerForNameplate(nameplate);
				if (playerForNameplate == null)
				{
					RestoreOutline(nameplate);
					return;
				}
				IList playerList = Game.GetPlayerList();
				object obj = ((playerList != null) ? Game.FindLocalPlayer(playerList) : null);
				Vector3 origin = ((obj != null) ? Game.GetPosition(obj) : Game.GetFallbackOrigin());
				if (!IsEligiblePlayer(playerForNameplate, obj, origin))
				{
					RestoreOutline(nameplate);
					return;
				}
				Game.UpdateNameplateWorldPosition(playerForNameplate, nameplate);
				if (!_occlusion.IsVisible(playerForNameplate, nameplate))
				{
					HideNameplate(nameplate);
					return;
				}
				Game.SetShowTimer(nameplate, 0.5f);
				ApplyHighlight(playerForNameplate, nameplate);
			}
			catch (Exception ex)
			{
				LogThrottled("postfix", "WorldSpaceUIPlayerName.Update postfix failed: " + ex.Message, 5f, (LogLevel)4);
			}
		}

		private bool IsEligiblePlayer(object player, object localPlayer, Vector3 origin)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (player == null)
			{
				return false;
			}
			if (player == localPlayer || Game.IsLocalPlayer(player))
			{
				return false;
			}
			bool flag = Game.IsDeadPlayer(player);
			bool flag2 = Game.IsDisabledPlayer(player);
			if (flag2 && (!Settings.IncludeDeadPlayers.Value || !flag || !Game.HasValidDeathHead(player)))
			{
				return false;
			}
			if (!flag2 && !Settings.IncludeDeadPlayers.Value && flag)
			{
				return false;
			}
			float num = Mathf.Max(0f, Settings.Range.Value);
			if (num <= 0f)
			{
				return false;
			}
			Vector3 val = Game.GetTrackingPosition(player) - origin;
			return ((Vector3)(ref val)).sqrMagnitude <= num * num;
		}

		private void ApplyHighlight(object player, object nameplate)
		{
			TextMeshProUGUI text = Game.GetText(nameplate);
			if ((Object)(object)text != (Object)null)
			{
				_highlighter.Apply(text, Settings, GetFontSizeOverride(player, nameplate));
			}
		}

		private float? GetFontSizeOverride(object player, object nameplate)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return null;
			}
			float num = Vector3.Distance(Game.GetLabelWorldPosition(player, nameplate), ((Component)main).transform.position);
			return Settings.BaseFontSize.Value - num;
		}

		private void HideNameplate(object nameplate)
		{
			Game.SetShowTimer(nameplate, 0f);
			TextMeshProUGUI text = Game.GetText(nameplate);
			if ((Object)(object)text != (Object)null)
			{
				_highlighter.Hide(text);
				_highlighter.RestoreOutline(text);
			}
		}

		private void RestoreOutline(object nameplate)
		{
			TextMeshProUGUI text = Game.GetText(nameplate);
			if ((Object)(object)text != (Object)null)
			{
				_highlighter.Restore(text);
			}
		}

		internal void LogThrottled(string key, string message, float intervalSeconds, LogLevel level)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			_logThrottle.Log(key, message, intervalSeconds, level);
		}

		internal void LogWarning(string message)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)message);
		}
	}
}