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 _nameplatePlayerCache = new Dictionary(); private readonly Dictionary _deadFieldCache = new Dictionary(); 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 Range { get; } internal ConfigEntry Alpha { get; } internal ConfigEntry BaseFontSize { get; } internal ConfigEntry IncludeDeadPlayers { get; } internal ConfigEntry TextColor { get; } internal ConfigEntry OutlineEnabled { get; } internal ConfigEntry OutlineColor { get; } internal ConfigEntry OutlineWidth { get; } internal ConfigEntry OcclusionEnabled { get; } internal ConfigEntry 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("Display", "Range", 20f, new ConfigDescription("Maximum distance from the local player to reveal names.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 500f), Array.Empty())); Alpha = config.Bind("Display", "Alpha", 1f, new ConfigDescription("Text alpha for revealed names.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); BaseFontSize = config.Bind("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(0f, 100f), Array.Empty())); IncludeDeadPlayers = config.Bind("Filters", "IncludeDeadPlayers", true, "If true, include players that expose a dead/downed flag such as deadSet."); TextColor = config.Bind("Text", "Color", "White", new ConfigDescription("Text color while GlowNames is active.", (AcceptableValueBase)(object)new AcceptableValueList(ColorOptions), Array.Empty())); OutlineEnabled = config.Bind("Outline", "Enabled", false, "If true, apply TMP outline while GlowNames is active. Disabled by default for plain white text."); OutlineColor = config.Bind("Outline", "Color", "White", new ConfigDescription("TMP outline color while GlowNames is active. Ignored when Outline.Enabled is false.", (AcceptableValueBase)(object)new AcceptableValueList(ColorOptions), Array.Empty())); OutlineWidth = config.Bind("Outline", "Width", 0f, new ConfigDescription("TMP outline width while GlowNames is active. Ignored when Outline.Enabled is false.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); OcclusionEnabled = config.Bind("Occlusion", "Enabled", true, "If true, names hidden behind walls or large physics objects will not be revealed."); OcclusionCheckInterval = config.Bind("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(0.02f, 1f), Array.Empty())); } 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 _nextLogTimeByKey = new Dictionary(); 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 _originalStateByText = new Dictionary(); 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 list = new List(_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 _stateByNameplate = new Dictionary(); 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); } } }