Decompiled source of uiHide v1.2.0

plugins/uiHide.dll

Decompiled a day ago
using System;
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 Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyVersion("0.0.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 uiHide
{
	internal static class BeepPlayer
	{
		private const float Frequency = 880f;

		private const float Duration = 0.08f;

		private const float Amplitude = 0.5f;

		private static AudioClip _clip;

		private static AudioSource _source;

		private static bool _initFailed;

		private static MethodInfo _setDataMethod;

		internal static void Cleanup()
		{
			if ((Object)(object)_clip != (Object)null)
			{
				Object.Destroy((Object)(object)_clip);
				_clip = null;
			}
			if ((Object)(object)_source != (Object)null)
			{
				Object.Destroy((Object)(object)_source);
				_source = null;
			}
			_initFailed = false;
			_setDataMethod = null;
		}

		internal static void Play(bool hidden)
		{
			if (!_initFailed)
			{
				EnsureInit();
				if (!((Object)(object)_source == (Object)null) && !((Object)(object)_clip == (Object)null))
				{
					_source.pitch = (hidden ? 0.8f : 1.3f);
					_source.PlayOneShot(_clip);
				}
			}
		}

		private static void EnsureInit()
		{
			if ((Object)(object)_clip == (Object)null)
			{
				_clip = CreateBeepClip();
			}
			if ((Object)(object)_source == (Object)null && (Object)(object)Plugin.Instance != (Object)null)
			{
				_source = ((Component)Plugin.Instance).GetComponent<AudioSource>();
				if ((Object)(object)_source == (Object)null)
				{
					_source = ((Component)Plugin.Instance).gameObject.AddComponent<AudioSource>();
				}
				_source.playOnAwake = false;
				_source.loop = false;
				_source.volume = 0.5f;
				_source.clip = _clip;
			}
			if ((Object)(object)_source == (Object)null)
			{
				_initFailed = true;
			}
		}

		private static AudioClip CreateBeepClip()
		{
			int num = 44100;
			int num2 = (int)((float)num * 0.08f);
			float[] array = new float[num2];
			for (int i = 0; i < num2; i++)
			{
				float num3 = (float)i / (float)num;
				float num4 = 1f - num3 / 0.08f;
				array[i] = Mathf.Sin((float)Math.PI * 1760f * num3) * num4 * 0.5f;
			}
			AudioClip obj = AudioClip.Create("uiHideBeep", num2, 1, num, false);
			SetDataViaReflection(obj, array, 0);
			return obj;
		}

		private static void SetDataViaReflection(AudioClip clip, float[] data, int offset)
		{
			if (_setDataMethod == null)
			{
				_setDataMethod = typeof(AudioClip).GetMethod("SetData", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
				{
					typeof(float[]),
					typeof(int)
				}, null);
			}
			if (_setDataMethod != null)
			{
				_setDataMethod.Invoke(clip, new object[2] { data, offset });
			}
			else if (Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)"[uiHide] AudioClip.SetData(float[], int) 未找到,提示音将静默");
			}
		}
	}
	[BepInPlugin("uiHide", "uiHide", "1.2.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static Plugin Instance;

		internal static ConfigEntry<KeyboardShortcut> HideHotkey;

		internal static ConfigEntry<bool> AggressiveSuppress;

		internal static ConfigEntry<bool> KeepNamesAndDistance;

		internal static ConfigEntry<string> KeepNamePatterns;

		internal static ConfigEntry<bool> ReleaseCursor;

		internal static ConfigEntry<bool> PlayBeep;

		private void Awake()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			BindConfig();
			SceneManager.sceneLoaded += OnSceneLoaded;
			Log.LogInfo((object)string.Format("[uiHide] v{0} 已加载,热键 {1},每帧压制 {2}", "1.2.0", HideHotkey.Value, AggressiveSuppress.Value ? "开" : "关"));
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			if (UISuppressor.IsHidden)
			{
				Log.LogInfo((object)"[uiHide] 卸载中恢复 UI");
				UISuppressor.Restore();
			}
			BeepPlayer.Cleanup();
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			UISuppressor.OnSceneLoaded();
		}

		private void BindConfig()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			HideHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "HideHotkey", new KeyboardShortcut((KeyCode)112, (KeyCode[])(object)new KeyCode[1] { (KeyCode)305 }), "切换显示/隐藏所有 UI 的组合键。主键在前,修饰键在后;在配置文件中修改后保存重启生效");
			AggressiveSuppress = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AggressiveSuppress", true, "每帧重新压制 Canvas,防止某些 mod 在 Update 里强制重开自己的 UI。开销极小,建议开启");
			KeepNamesAndDistance = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "KeepNamesAndDistance", false, "隐藏模式下保留队友名字和距离指示。默认 false 一并隐藏;设 true 用于合影时仍想看到队友身份");
			KeepNamePatterns = ((BaseUnityPlugin)this).Config.Bind<string>("General", "KeepNamePatterns", "playername,playernames,nametag,playerlabel,distance,overheaddistance", "保留名单的 GameObject 名匹配模式(逗号分隔,不区分大小写,子串匹配)。仅当 KeepNamesAndDistance=true 时生效。可增删以适配你安装的 mod");
			ReleaseCursor = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ReleaseCursor", true, "隐藏 UI 时释放鼠标光标,方便移动视角截图");
			PlayBeep = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PlayBeep", true, "切换时播放短促提示音。设 false 可完全静默");
			KeepNamePatterns.SettingChanged += delegate
			{
				UISuppressor.MarkPatternsDirty();
			};
		}

		private void Update()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = HideHotkey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				UISuppressor.Toggle();
			}
			if (UISuppressor.IsHidden)
			{
				UISuppressor.MaintainSuppression();
			}
		}
	}
	internal static class PluginInfo
	{
		public const string Guid = "uiHide";

		public const string Name = "uiHide";

		public const string Version = "1.2.0";
	}
	internal static class UISuppressor
	{
		private struct CanvasEntry
		{
			public Canvas canvas;

			public bool wasEnabled;
		}

		private struct RendererEntry
		{
			public Renderer renderer;

			public bool wasEnabled;
		}

		private static readonly List<CanvasEntry> _canvases = new List<CanvasEntry>();

		private static readonly List<MonoBehaviour> _onGuiBehaviours = new List<MonoBehaviour>();

		private static readonly List<RendererEntry> _renderers = new List<RendererEntry>();

		private static readonly HashSet<Type> _hasOnGuiCache = new HashSet<Type>();

		private static readonly HashSet<Type> _noOnGuiCache = new HashSet<Type>();

		private static readonly List<string> _namePatterns = new List<string>();

		private static bool _patternsDirty = true;

		private const int RescanInterval = 15;

		private static int _rescanCounter;

		internal static bool IsHidden { get; private set; }

		internal static void Toggle()
		{
			if (IsHidden)
			{
				Restore();
			}
			else
			{
				Suppress();
			}
			if (Plugin.PlayBeep.Value)
			{
				BeepPlayer.Play(IsHidden);
			}
		}

		internal static void OnSceneLoaded()
		{
			_canvases.Clear();
			_onGuiBehaviours.Clear();
			_renderers.Clear();
			_rescanCounter = 0;
			if (IsHidden)
			{
				SuppressCanvases(scanAll: true);
				SuppressBehaviours();
				if (Plugin.Log != null)
				{
					Plugin.Log.LogInfo((object)"[uiHide] 场景加载,已重新压制");
				}
			}
		}

		internal static void MarkPatternsDirty()
		{
			_patternsDirty = true;
		}

		private static void Suppress()
		{
			try
			{
				IsHidden = true;
				_canvases.Clear();
				_onGuiBehaviours.Clear();
				_renderers.Clear();
				_rescanCounter = 0;
				RefreshPatternsIfNeeded();
				SuppressCanvases(scanAll: true);
				SuppressBehaviours();
				if (Plugin.ReleaseCursor.Value)
				{
					Cursor.lockState = (CursorLockMode)0;
					Cursor.visible = true;
				}
			}
			catch (Exception ex)
			{
				if (Plugin.Log != null)
				{
					Plugin.Log.LogError((object)("[uiHide] Suppress 异常: " + ex.Message));
				}
			}
		}

		internal static void Restore()
		{
			try
			{
				IsHidden = false;
				for (int i = 0; i < _canvases.Count; i++)
				{
					Canvas canvas = _canvases[i].canvas;
					if ((Object)(object)canvas != (Object)null)
					{
						((Behaviour)canvas).enabled = _canvases[i].wasEnabled;
					}
				}
				_canvases.Clear();
				for (int j = 0; j < _onGuiBehaviours.Count; j++)
				{
					MonoBehaviour val = _onGuiBehaviours[j];
					if ((Object)(object)val != (Object)null)
					{
						((Behaviour)val).enabled = true;
					}
				}
				_onGuiBehaviours.Clear();
				for (int k = 0; k < _renderers.Count; k++)
				{
					Renderer renderer = _renderers[k].renderer;
					if ((Object)(object)renderer != (Object)null)
					{
						renderer.enabled = _renderers[k].wasEnabled;
					}
				}
				_renderers.Clear();
			}
			catch (Exception ex)
			{
				if (Plugin.Log != null)
				{
					Plugin.Log.LogError((object)("[uiHide] Restore 异常: " + ex.Message));
				}
			}
		}

		internal static void MaintainSuppression()
		{
			if (++_rescanCounter >= 15)
			{
				_rescanCounter = 0;
				SuppressCanvases(scanAll: false);
			}
			if (!Plugin.AggressiveSuppress.Value)
			{
				return;
			}
			for (int num = _canvases.Count - 1; num >= 0; num--)
			{
				Canvas canvas = _canvases[num].canvas;
				if ((Object)(object)canvas == (Object)null)
				{
					_canvases.RemoveAt(num);
				}
				else if (((Behaviour)canvas).enabled)
				{
					((Behaviour)canvas).enabled = false;
				}
			}
			for (int num2 = _onGuiBehaviours.Count - 1; num2 >= 0; num2--)
			{
				MonoBehaviour val = _onGuiBehaviours[num2];
				if ((Object)(object)val == (Object)null)
				{
					_onGuiBehaviours.RemoveAt(num2);
				}
				else if (((Behaviour)val).enabled)
				{
					((Behaviour)val).enabled = false;
				}
			}
			for (int num3 = _renderers.Count - 1; num3 >= 0; num3--)
			{
				Renderer renderer = _renderers[num3].renderer;
				if ((Object)(object)renderer == (Object)null)
				{
					_renderers.RemoveAt(num3);
				}
				else if (renderer.enabled)
				{
					renderer.enabled = false;
				}
			}
		}

		private static void SuppressCanvases(bool scanAll)
		{
			bool value = Plugin.KeepNamesAndDistance.Value;
			RefreshPatternsIfNeeded();
			Canvas[] array = Object.FindObjectsByType<Canvas>((FindObjectsInactive)1, (FindObjectsSortMode)1);
			foreach (Canvas val in array)
			{
				if (!((Object)(object)val == (Object)null) && ((Behaviour)val).enabled && (!value || !IsKeepTarget(((Component)val).gameObject)) && (scanAll || !ContainsCanvas(val)))
				{
					_canvases.Add(new CanvasEntry
					{
						canvas = val,
						wasEnabled = true
					});
					((Behaviour)val).enabled = false;
				}
			}
		}

		private static void SuppressBehaviours()
		{
			bool value = Plugin.KeepNamesAndDistance.Value;
			MonoBehaviour[] array = Object.FindObjectsByType<MonoBehaviour>((FindObjectsInactive)1, (FindObjectsSortMode)1);
			Assembly assembly = typeof(Plugin).Assembly;
			foreach (MonoBehaviour val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Type type = ((object)val).GetType();
				if (type.Assembly == assembly)
				{
					continue;
				}
				if (HasOnGui(type))
				{
					if (((Behaviour)val).enabled)
					{
						_onGuiBehaviours.Add(val);
						((Behaviour)val).enabled = false;
					}
					continue;
				}
				string name = type.Name;
				if ((name == "TextMeshPro" || name == "TextMesh") && (!value || !IsKeepTarget(((Component)val).gameObject)))
				{
					Renderer component = ((Component)val).GetComponent<Renderer>();
					if (!((Object)(object)component == (Object)null) && component.enabled)
					{
						_renderers.Add(new RendererEntry
						{
							renderer = component,
							wasEnabled = true
						});
						component.enabled = false;
					}
				}
			}
		}

		private static bool ContainsCanvas(Canvas c)
		{
			for (int i = 0; i < _canvases.Count; i++)
			{
				if ((Object)(object)_canvases[i].canvas == (Object)(object)c)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsKeepTarget(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return false;
			}
			if (IsNameMatch(((Object)go).name.ToLowerInvariant()))
			{
				return true;
			}
			Transform parent = go.transform.parent;
			int num = 0;
			while ((Object)(object)parent != (Object)null && num < 4)
			{
				if (IsNameMatch(((Object)parent).name.ToLowerInvariant()))
				{
					return true;
				}
				parent = parent.parent;
				num++;
			}
			return false;
		}

		private static bool IsNameMatch(string lower)
		{
			if (string.IsNullOrEmpty(lower))
			{
				return false;
			}
			for (int i = 0; i < _namePatterns.Count; i++)
			{
				if (lower.Contains(_namePatterns[i]))
				{
					return true;
				}
			}
			return false;
		}

		private static void RefreshPatternsIfNeeded()
		{
			if (!_patternsDirty)
			{
				return;
			}
			_patternsDirty = false;
			_namePatterns.Clear();
			if (Plugin.KeepNamePatterns == null)
			{
				return;
			}
			string[] array = Plugin.KeepNamePatterns.Value.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim().ToLowerInvariant();
				if (text.Length > 0)
				{
					_namePatterns.Add(text);
				}
			}
		}

		private static bool HasOnGui(Type type)
		{
			if (_hasOnGuiCache.Contains(type))
			{
				return true;
			}
			if (_noOnGuiCache.Contains(type))
			{
				return false;
			}
			bool num = type.GetMethod("OnGUI", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) != null;
			if (num)
			{
				_hasOnGuiCache.Add(type);
				return num;
			}
			_noOnGuiCache.Add(type);
			return num;
		}
	}
}