Decompiled source of LCMicRecovery v0.3.5

BepInEx/plugins/LCMicRecovery/LCMicRecovery.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dissonance;
using Dissonance.Audio.Capture;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("LCMicRecovery")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LCMicRecovery")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("a1b2c3d4-e5f6-4789-abcd-1234567890ab")]
[assembly: AssemblyFileVersion("0.3.5.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.3.5.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LCMicRecovery
{
	[BepInPlugin("com.yourname.lcmicrecovery", "LC Mic Recovery", "0.3.5")]
	public class Plugin : BaseUnityPlugin
	{
		internal static Plugin Instance;

		internal static Harmony HarmonyInstance;

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			HarmonyInstance = new Harmony("com.yourname.lcmicrecovery");
			HarmonyInstance.PatchAll();
			Log.LogInfo((object)"LC Mic Recovery loaded.");
			Log.LogInfo((object)"LC Mic Recovery version: 0.3.5");
			if ((Object)(object)Object.FindObjectOfType<MicRecoveryWatcher>() == (Object)null)
			{
				GameObject val = new GameObject("LCMicRecovery_Watcher");
				Object.DontDestroyOnLoad((Object)val);
				((Object)val).hideFlags = (HideFlags)61;
				val.AddComponent<MicRecoveryWatcher>();
				Log.LogInfo((object)"LCMicRecovery_Watcher created successfully.");
			}
			else
			{
				Log.LogInfo((object)"LCMicRecovery_Watcher already exists, skipping duplicate creation.");
			}
			Log.LogInfo((object)"Config entries bound. Check LethalConfig in-game to see if they appear.");
		}
	}
	internal static class PluginConfig
	{
		internal static ConfigEntry<bool> EnableMod;

		internal static ConfigEntry<bool> EnableDebugLog;

		internal static ConfigEntry<bool> EnableStateLogs;

		internal static ConfigEntry<bool> EnableHeartbeatLog;

		internal static ConfigEntry<bool> ShowFiveStepRecoveryLogs;

		internal static ConfigEntry<bool> EnablePreRoundSkipLog;

		internal static ConfigEntry<bool> LogPreRoundSkipOnlyOnce;

		internal static ConfigEntry<bool> SuspendAutoRecoveryWhenNoDevices;

		internal static ConfigEntry<bool> EnableNoDeviceSuspendLog;

		internal static ConfigEntry<bool> LogNoDeviceSuspendOnlyOnce;

		internal static ConfigEntry<bool> AllowManualRecoveryWhenNoDevices;

		internal static ConfigEntry<bool> SuspendAutoRecoveryDuringMenuOrTeardown;

		internal static ConfigEntry<float> LobbyExitSuspendSeconds;

		internal static ConfigEntry<bool> EnableTeardownSuspendLog;

		internal static ConfigEntry<bool> LogTeardownSuspendOnlyOnce;

		internal static ConfigEntry<bool> EnableManualRecoveryKey;

		internal static ConfigEntry<Key> ManualRecoveryKey;

		internal static ConfigEntry<bool> EnableAutoRecovery;

		internal static ConfigEntry<bool> EnablePreRoundRecovery;

		internal static ConfigEntry<float> AutoCheckIntervalSeconds;

		internal static ConfigEntry<float> RecoveryCooldownSeconds;

		internal static ConfigEntry<float> PostRecoveryGraceSeconds;

		internal static ConfigEntry<string> PreferredDeviceKeywords;

		internal static ConfigEntry<bool> PreferCurrentDeviceIfStillExists;

		internal static ConfigEntry<bool> RecoverWhenMicNameEmpty;

		internal static ConfigEntry<bool> RecoverWhenDeviceMissing;

		internal static ConfigEntry<bool> RecoverWhenUnityNotRecording;

		internal static bool DebugEnabled
		{
			get
			{
				if (EnableDebugLog != null)
				{
					return EnableDebugLog.Value;
				}
				return false;
			}
		}

		internal static bool StateLogsEnabled
		{
			get
			{
				if (EnableStateLogs != null)
				{
					return EnableStateLogs.Value;
				}
				return false;
			}
		}

		internal static bool HeartbeatEnabled
		{
			get
			{
				if (EnableHeartbeatLog != null)
				{
					return EnableHeartbeatLog.Value;
				}
				return false;
			}
		}

		internal static void Bind(ConfigFile config)
		{
			EnableMod = config.Bind<bool>("General", "EnableMod", true, "总开关。关闭后模组不执行任何恢复逻辑。");
			EnableDebugLog = config.Bind<bool>("Logging", "EnableDebugLog", false, "是否输出调试日志。正常游玩建议关闭。");
			EnableStateLogs = config.Bind<bool>("Logging", "EnableStateLogs", false, "是否输出状态检测日志(当前麦克风、IsRecording 等,容易刷屏)。正常游玩建议关闭。");
			EnableHeartbeatLog = config.Bind<bool>("Logging", "EnableHeartbeatLog", false, "是否输出 watcher 心跳日志。正常游玩建议关闭。");
			ShowFiveStepRecoveryLogs = config.Bind<bool>("Logging", "ShowFiveStepRecoveryLogs", true, "恢复时是否输出 1/5 到 5/5 的明显日志。正常游玩建议开启。");
			EnablePreRoundSkipLog = config.Bind<bool>("Logging", "EnablePreRoundSkipLog", false, "在 StartOfRound 尚未就绪时,是否输出“跳过自动恢复检测”的日志。正常游玩建议关闭。");
			LogPreRoundSkipOnlyOnce = config.Bind<bool>("Logging", "LogPreRoundSkipOnlyOnce", true, "是否只在每次进入局内前打印一次“StartOfRound 尚未就绪”的日志。");
			SuspendAutoRecoveryWhenNoDevices = config.Bind<bool>("No Device Handling", "SuspendAutoRecoveryWhenNoDevices", true, "当没有检测到任何录音设备时,暂停自动恢复,避免反复刷恢复日志。");
			EnableNoDeviceSuspendLog = config.Bind<bool>("No Device Handling", "EnableNoDeviceSuspendLog", false, "当没有录音设备时,是否输出“已暂停自动恢复”的提示日志。");
			LogNoDeviceSuspendOnlyOnce = config.Bind<bool>("No Device Handling", "LogNoDeviceSuspendOnlyOnce", true, "没有录音设备时,是否只打印一次“已暂停自动恢复”的提示。");
			AllowManualRecoveryWhenNoDevices = config.Bind<bool>("No Device Handling", "AllowManualRecoveryWhenNoDevices", false, "当没有录音设备时,是否允许手动按键继续强制恢复。");
			SuspendAutoRecoveryDuringMenuOrTeardown = config.Bind<bool>("Teardown Handling", "SuspendAutoRecoveryDuringMenuOrTeardown", true, "当处于主菜单/大厅,或房间正在退出销毁时,暂停自动恢复。建议开启。");
			LobbyExitSuspendSeconds = config.Bind<float>("Teardown Handling", "LobbyExitSuspendSeconds", 8f, "退出房间或切换到大厅后,暂停自动恢复的秒数。建议 8。");
			EnableTeardownSuspendLog = config.Bind<bool>("Teardown Handling", "EnableTeardownSuspendLog", false, "当因为大厅/退出阶段而暂停自动恢复时,是否输出提示日志。");
			LogTeardownSuspendOnlyOnce = config.Bind<bool>("Teardown Handling", "LogTeardownSuspendOnlyOnce", true, "是否只打印一次大厅/退出阶段挂起日志。");
			EnableManualRecoveryKey = config.Bind<bool>("Manual Recovery", "EnableManualRecoveryKey", true, "是否启用手动按键恢复。");
			ManualRecoveryKey = config.Bind<Key>("Manual Recovery", "ManualRecoveryKey", (Key)101, "手动触发恢复的按键。");
			EnableAutoRecovery = config.Bind<bool>("Auto Recovery", "EnableAutoRecovery", true, "是否启用自动恢复。");
			EnablePreRoundRecovery = config.Bind<bool>("Auto Recovery", "EnablePreRoundRecovery", false, "是否允许在 StartOfRound 出现前就触发恢复。建议关闭。");
			AutoCheckIntervalSeconds = config.Bind<float>("Auto Recovery", "AutoCheckIntervalSeconds", 3f, "自动检测间隔(秒)。");
			RecoveryCooldownSeconds = config.Bind<float>("Auto Recovery", "RecoveryCooldownSeconds", 10f, "两次恢复之间的冷却时间(秒)。");
			PostRecoveryGraceSeconds = config.Bind<float>("Auto Recovery", "PostRecoveryGraceSeconds", 4f, "恢复完成后的宽限时间(秒)。");
			PreferredDeviceKeywords = config.Bind<string>("Device", "PreferredDeviceKeywords", "Maxwell,Audeze", "优先匹配的设备关键词,多个用英文逗号分隔。");
			PreferCurrentDeviceIfStillExists = config.Bind<bool>("Device", "PreferCurrentDeviceIfStillExists", true, "如果当前设备仍存在,恢复时优先保持当前设备。");
			RecoverWhenMicNameEmpty = config.Bind<bool>("Recovery Conditions", "RecoverWhenMicNameEmpty", true, "当前麦克风名称为空时是否触发恢复。");
			RecoverWhenDeviceMissing = config.Bind<bool>("Recovery Conditions", "RecoverWhenDeviceMissing", true, "当前麦克风不在设备列表中时是否触发恢复。");
			RecoverWhenUnityNotRecording = config.Bind<bool>("Recovery Conditions", "RecoverWhenUnityNotRecording", true, "Unity 报告当前麦克风未在录音时是否触发恢复。");
		}
	}
	public class MicRecoveryWatcher : MonoBehaviour
	{
		private float _checkTimer;

		private float _heartbeatTimer;

		private float _nextCommsSearchTime;

		private float _suspendRecoveryUntil = -999f;

		private bool _preRoundSkipLogged;

		private bool _noDeviceSuspendLogged;

		private bool _teardownSuspendLogged;

		private int _autoDeviceListFailureCount;

		private DissonanceComms _cachedComms;

		private readonly List<string> _deviceBuffer = new List<string>(8);

		private void OnEnable()
		{
			SceneManager.activeSceneChanged += OnActiveSceneChanged;
		}

		private void OnDisable()
		{
			SceneManager.activeSceneChanged -= OnActiveSceneChanged;
		}

		private void OnActiveSceneChanged(Scene oldScene, Scene newScene)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			_cachedComms = null;
			_nextCommsSearchTime = 0f;
			_preRoundSkipLogged = false;
			_noDeviceSuspendLogged = false;
			_teardownSuspendLogged = false;
			_autoDeviceListFailureCount = 0;
			if (PluginConfig.SuspendAutoRecoveryDuringMenuOrTeardown != null && PluginConfig.SuspendAutoRecoveryDuringMenuOrTeardown.Value && IsMenuScene(newScene))
			{
				SuspendRecoveryForTeardown("切换到主菜单/大厅场景,已暂停自动恢复。");
			}
		}

		private static bool IsMenuScene(Scene scene)
		{
			if (!((Scene)(ref scene)).IsValid())
			{
				return true;
			}
			return (((Scene)(ref scene)).name ?? string.Empty).IndexOf("menu", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private bool IsRecoveryTemporarilySuspended()
		{
			return Time.unscaledTime < _suspendRecoveryUntil;
		}

		private void SuspendRecoveryForTeardown(string reason)
		{
			float num = ((PluginConfig.LobbyExitSuspendSeconds != null) ? Mathf.Max(0f, PluginConfig.LobbyExitSuspendSeconds.Value) : 8f);
			float num2 = Time.unscaledTime + num;
			if (num2 > _suspendRecoveryUntil)
			{
				_suspendRecoveryUntil = num2;
			}
			if (PluginConfig.EnableTeardownSuspendLog != null && PluginConfig.EnableTeardownSuspendLog.Value && (PluginConfig.LogTeardownSuspendOnlyOnce == null || !PluginConfig.LogTeardownSuspendOnlyOnce.Value || !_teardownSuspendLogged))
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[MicRecovery] " + reason));
				}
				_teardownSuspendLogged = true;
			}
		}

		private void Update()
		{
			if (PluginConfig.EnableMod != null && !PluginConfig.EnableMod.Value)
			{
				return;
			}
			HandleManualRecoveryKey();
			HandleHeartbeat();
			if (PluginConfig.EnableAutoRecovery == null || PluginConfig.EnableAutoRecovery.Value)
			{
				float num = ((PluginConfig.AutoCheckIntervalSeconds != null) ? Mathf.Max(0.5f, PluginConfig.AutoCheckIntervalSeconds.Value) : 3f);
				_checkTimer += Time.unscaledDeltaTime;
				if (!(_checkTimer < num))
				{
					_checkTimer = 0f;
					AutoCheckMic();
				}
			}
		}

		private void HandleManualRecoveryKey()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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)
			if (PluginConfig.EnableManualRecoveryKey == null || !PluginConfig.EnableManualRecoveryKey.Value)
			{
				return;
			}
			try
			{
				Keyboard current = Keyboard.current;
				if (current == null)
				{
					return;
				}
				Key value = PluginConfig.ManualRecoveryKey.Value;
				if (!((ButtonControl)current[value]).wasPressedThisFrame)
				{
					return;
				}
				if (PluginConfig.SuspendAutoRecoveryDuringMenuOrTeardown != null && PluginConfig.SuspendAutoRecoveryDuringMenuOrTeardown.Value && MicRecoveryCore.IsMenuSceneActive())
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)"[MicRecovery] 当前位于主菜单/大厅,已跳过手动恢复。");
						}
					}
					return;
				}
				if (PluginConfig.SuspendAutoRecoveryDuringMenuOrTeardown != null && PluginConfig.SuspendAutoRecoveryDuringMenuOrTeardown.Value && !MicRecoveryCore.IsGameSideResetSafe())
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogInfo((object)"[MicRecovery] 当前处于退房、切场景或对象不稳定阶段,已跳过手动恢复。");
						}
					}
					return;
				}
				DissonanceComms comms = GetComms();
				if ((Object)(object)comms == (Object)null)
				{
					return;
				}
				_deviceBuffer.Clear();
				bool flag = false;
				try
				{
					comms.GetMicrophoneDevices(_deviceBuffer);
				}
				catch (Exception ex)
				{
					flag = true;
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log3 = Plugin.Log;
						if (log3 != null)
						{
							log3.LogWarning((object)("[MicRecovery] 手动恢复获取录音设备列表失败,将继续尝试强制恢复:" + ex.Message));
						}
					}
				}
				if (!flag && _deviceBuffer.Count == 0 && PluginConfig.AllowManualRecoveryWhenNoDevices != null && !PluginConfig.AllowManualRecoveryWhenNoDevices.Value)
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log4 = Plugin.Log;
						if (log4 != null)
						{
							log4.LogInfo((object)"[MicRecovery] 当前没有录音设备,已跳过手动恢复。");
						}
					}
				}
				else
				{
					MicRecoveryCore.TryRecover("手动按键触发恢复", ignoreCooldown: true);
				}
			}
			catch (Exception ex2)
			{
				if (PluginConfig.DebugEnabled)
				{
					ManualLogSource log5 = Plugin.Log;
					if (log5 != null)
					{
						log5.LogWarning((object)("[MicRecovery] 手动按键检测失败:" + ex2.Message));
					}
				}
			}
		}

		private void HandleHeartbeat()
		{
			if (!PluginConfig.HeartbeatEnabled)
			{
				return;
			}
			_heartbeatTimer += Time.unscaledDeltaTime;
			if (!(_heartbeatTimer < 15f))
			{
				_heartbeatTimer = 0f;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[MicRecovery] Watcher 正在运行。");
				}
			}
		}

		private DissonanceComms GetComms()
		{
			Object cachedComms = (Object)(object)_cachedComms;
			if (cachedComms != null && cachedComms == (Object)null)
			{
				_cachedComms = null;
			}
			if ((Object)(object)_cachedComms != (Object)null)
			{
				return _cachedComms;
			}
			if (Time.unscaledTime < _nextCommsSearchTime)
			{
				return null;
			}
			_nextCommsSearchTime = Time.unscaledTime + 1f;
			_cachedComms = Object.FindObjectOfType<DissonanceComms>();
			return _cachedComms;
		}

		private void AutoCheckMic()
		{
			try
			{
				if (PluginConfig.SuspendAutoRecoveryDuringMenuOrTeardown != null && PluginConfig.SuspendAutoRecoveryDuringMenuOrTeardown.Value)
				{
					if (MicRecoveryCore.IsMenuSceneActive())
					{
						SuspendRecoveryForTeardown("当前处于主菜单/大厅场景,已暂停自动恢复。");
						return;
					}
					if (IsRecoveryTemporarilySuspended())
					{
						return;
					}
				}
				DissonanceComms comms = GetComms();
				if ((Object)(object)comms == (Object)null)
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)"[MicRecovery] 当前未找到 DissonanceComms,跳过检测。");
						}
					}
					return;
				}
				if (!PluginConfig.EnablePreRoundRecovery.Value && !MicRecoveryCore.IsStartOfRoundReady())
				{
					if (PluginConfig.EnablePreRoundSkipLog.Value && (!PluginConfig.LogPreRoundSkipOnlyOnce.Value || !_preRoundSkipLogged))
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogInfo((object)"[MicRecovery] StartOfRound 尚未就绪,跳过自动恢复检测。");
						}
						_preRoundSkipLogged = true;
					}
					return;
				}
				_preRoundSkipLogged = false;
				if (PluginConfig.SuspendAutoRecoveryDuringMenuOrTeardown.Value && !MicRecoveryCore.IsGameSideResetSafe())
				{
					SuspendRecoveryForTeardown("检测到房间正在退出或语音上下文正在销毁,已暂停自动恢复。");
					return;
				}
				_teardownSuspendLogged = false;
				_deviceBuffer.Clear();
				try
				{
					comms.GetMicrophoneDevices(_deviceBuffer);
				}
				catch (Exception ex)
				{
					_autoDeviceListFailureCount++;
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log3 = Plugin.Log;
						if (log3 != null)
						{
							log3.LogWarning((object)$"[MicRecovery] 自动检测获取录音设备列表失败({_autoDeviceListFailureCount}/2):{ex.Message}");
						}
					}
					if (_autoDeviceListFailureCount >= 2)
					{
						_autoDeviceListFailureCount = 0;
						MicRecoveryCore.TryRecover("自动检测连续获取录音设备列表失败");
					}
					return;
				}
				_autoDeviceListFailureCount = 0;
				if (PluginConfig.SuspendAutoRecoveryWhenNoDevices.Value && _deviceBuffer.Count == 0)
				{
					if (PluginConfig.EnableNoDeviceSuspendLog.Value && (!PluginConfig.LogNoDeviceSuspendOnlyOnce.Value || !_noDeviceSuspendLogged))
					{
						ManualLogSource log4 = Plugin.Log;
						if (log4 != null)
						{
							log4.LogInfo((object)"[MicRecovery] 未检测到任何录音设备,已暂停自动恢复与相关状态日志。");
						}
						_noDeviceSuspendLogged = true;
					}
					return;
				}
				_noDeviceSuspendLogged = false;
				string currentMicName;
				try
				{
					currentMicName = comms.MicrophoneName;
				}
				catch (Exception ex2)
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log5 = Plugin.Log;
						if (log5 != null)
						{
							log5.LogWarning((object)("[MicRecovery] 自动检测读取当前麦克风名称失败:" + ex2.Message));
						}
					}
					MicRecoveryCore.TryRecover("自动检测读取当前麦克风名称失败");
					return;
				}
				if (PluginConfig.RecoverWhenMicNameEmpty.Value && string.IsNullOrWhiteSpace(currentMicName))
				{
					MicRecoveryCore.TryRecover("当前 Dissonance 麦克风名称为空");
					return;
				}
				bool isInPostRecoveryGracePeriod = MicRecoveryCore.IsInPostRecoveryGracePeriod;
				if (!isInPostRecoveryGracePeriod)
				{
					try
					{
						IMicrophoneCapture microphoneCapture = comms.MicrophoneCapture;
						if (microphoneCapture == null)
						{
							MicRecoveryCore.TryRecover("Dissonance 麦克风采集管线为空");
							return;
						}
						if (!microphoneCapture.IsRecording)
						{
							MicRecoveryCore.TryRecover("Dissonance 麦克风采集管线未在录音");
							return;
						}
					}
					catch (Exception ex3)
					{
						if (PluginConfig.DebugEnabled)
						{
							ManualLogSource log6 = Plugin.Log;
							if (log6 != null)
							{
								log6.LogWarning((object)("[MicRecovery] 读取 Dissonance 麦克风采集管线失败:" + ex3.Message));
							}
						}
					}
				}
				else if (PluginConfig.DebugEnabled)
				{
					ManualLogSource log7 = Plugin.Log;
					if (log7 != null)
					{
						log7.LogInfo((object)"[MicRecovery] 当前处于恢复宽限期,跳过 Dissonance 麦克风采集管线判定。");
					}
				}
				bool flag = _deviceBuffer.Any((string d) => string.Equals(d, currentMicName, StringComparison.Ordinal));
				if (PluginConfig.StateLogsEnabled)
				{
					ManualLogSource log8 = Plugin.Log;
					if (log8 != null)
					{
						log8.LogInfo((object)$"[MicRecovery] 当前麦克风:{currentMicName} | 设备数:{_deviceBuffer.Count} | 设备仍存在:{flag}");
					}
				}
				if (PluginConfig.RecoverWhenDeviceMissing.Value && !flag)
				{
					MicRecoveryCore.TryRecover("当前麦克风已不在设备列表中:" + currentMicName);
					return;
				}
				if (isInPostRecoveryGracePeriod)
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log9 = Plugin.Log;
						if (log9 != null)
						{
							log9.LogInfo((object)"[MicRecovery] 当前处于恢复宽限期,跳过 Unity.IsRecording 判定。");
						}
					}
					return;
				}
				bool flag2 = false;
				try
				{
					if (!string.IsNullOrWhiteSpace(currentMicName))
					{
						flag2 = Microphone.IsRecording(currentMicName);
					}
				}
				catch (Exception ex4)
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log10 = Plugin.Log;
						if (log10 != null)
						{
							log10.LogWarning((object)("[MicRecovery] 调用 Microphone.IsRecording 失败:" + ex4.Message));
						}
					}
				}
				if (PluginConfig.StateLogsEnabled)
				{
					ManualLogSource log11 = Plugin.Log;
					if (log11 != null)
					{
						log11.LogInfo((object)$"[MicRecovery] Unity.IsRecording({currentMicName}) = {flag2}");
					}
				}
				if (PluginConfig.RecoverWhenUnityNotRecording.Value && !flag2)
				{
					MicRecoveryCore.TryRecover("Unity 报告麦克风未在录音:" + currentMicName);
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log12 = Plugin.Log;
				if (log12 != null)
				{
					log12.LogWarning((object)$"[MicRecovery] AutoCheckMic 异常:{arg}");
				}
				_cachedComms = null;
				_nextCommsSearchTime = 0f;
			}
		}
	}
	internal static class MicRecoveryCore
	{
		private enum GameSideResetResult
		{
			Invoked,
			SkippedUnsafe,
			MethodNotFound,
			Failed
		}

		[CompilerGenerated]
		private sealed class <SafeRunGameSideResetCoroutine>d__19 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public IEnumerator routine;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SafeRunGameSideResetCoroutine>d__19(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
				}
				else
				{
					<>1__state = -1;
				}
				bool flag = false;
				bool flag2 = false;
				object obj;
				try
				{
					flag = routine.MoveNext();
					obj = (flag ? routine.Current : null);
				}
				catch (Exception ex)
				{
					flag2 = true;
					if (!_gameSideResetCoroutineWarningLogged)
					{
						_gameSideResetCoroutineWarningLogged = true;
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogWarning((object)("[MicRecovery] 游戏侧 ResetDissonanceCommsComponent 协程执行失败,已停止该协程并保留本地 ResetMicrophoneCapture 结果:" + ex.Message));
						}
					}
					else if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogWarning((object)("[MicRecovery] 游戏侧 ResetDissonanceCommsComponent 协程再次失败:" + ex.Message));
						}
					}
					obj = null;
				}
				if (flag2 || !flag)
				{
					return false;
				}
				<>2__current = obj;
				<>1__state = 1;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly BindingFlags StaticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static float _lastRecoveryTime = -999f;

		private static float _postRecoveryGraceUntil = -999f;

		private static bool _gameSideResetCompatibilityWarningLogged;

		private static bool _gameSideResetCoroutineWarningLogged;

		internal static bool IsInPostRecoveryGracePeriod => Time.unscaledTime < _postRecoveryGraceUntil;

		internal static bool TryRecover(string reason)
		{
			return TryRecover(reason, ignoreCooldown: false);
		}

		internal static bool TryRecover(string reason, bool ignoreCooldown)
		{
			float num = ((PluginConfig.RecoveryCooldownSeconds != null) ? Mathf.Max(0f, PluginConfig.RecoveryCooldownSeconds.Value) : 10f);
			if (!ignoreCooldown && Time.unscaledTime - _lastRecoveryTime < num)
			{
				if (PluginConfig.DebugEnabled)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("[MicRecovery] 恢复冷却中,跳过本次恢复。reason=" + reason));
					}
				}
				return false;
			}
			LogRecoveryStep(1, "检测到疑似麦克风失效,开始执行恢复");
			LogRecoveryStep(2, "触发原因:" + reason);
			try
			{
				DissonanceComms val = Object.FindObjectOfType<DissonanceComms>();
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogError((object)"========== [麦克风修复 3/5] 未找到 DissonanceComms,无法执行恢复 ==========");
					}
					ManualLogSource log3 = Plugin.Log;
					if (log3 != null)
					{
						log3.LogError((object)"========== [麦克风修复 4/5] 本次恢复失败 ==========");
					}
					ManualLogSource log4 = Plugin.Log;
					if (log4 != null)
					{
						log4.LogError((object)"========== [麦克风修复 5/5] 请检查当前场景是否已进入可语音状态 ==========");
					}
					return false;
				}
				string text = null;
				try
				{
					text = val.MicrophoneName;
				}
				catch (Exception ex)
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log5 = Plugin.Log;
						if (log5 != null)
						{
							log5.LogWarning((object)("[MicRecovery] 读取当前麦克风名称失败:" + ex.Message));
						}
					}
				}
				List<string> list = new List<string>();
				try
				{
					val.GetMicrophoneDevices(list);
				}
				catch (Exception ex2)
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log6 = Plugin.Log;
						if (log6 != null)
						{
							log6.LogWarning((object)("[MicRecovery] 获取麦克风列表失败:" + ex2.Message));
						}
					}
				}
				string[] array = list.ToArray();
				LogRecoveryStep(3, string.Format("当前麦克风:{0} | 设备数:{1}", string.IsNullOrEmpty(text) ? "<空>" : text, array.Length));
				string text2 = PickPreferredDevice(array, text);
				if (!string.IsNullOrEmpty(text2))
				{
					if (!string.Equals(text, text2, StringComparison.Ordinal))
					{
						val.MicrophoneName = text2;
						if (PluginConfig.DebugEnabled)
						{
							ManualLogSource log7 = Plugin.Log;
							if (log7 != null)
							{
								log7.LogInfo((object)("[MicRecovery] 已切换麦克风到首选设备:" + text2));
							}
						}
					}
					else if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log8 = Plugin.Log;
						if (log8 != null)
						{
							log8.LogInfo((object)("[MicRecovery] 当前已是首选设备:" + text2));
						}
					}
				}
				else
				{
					ManualLogSource log9 = Plugin.Log;
					if (log9 != null)
					{
						log9.LogWarning((object)"[MicRecovery] 没找到可用的首选设备,将沿用当前设备继续恢复。");
					}
				}
				val.ResetMicrophoneCapture();
				_lastRecoveryTime = Time.unscaledTime;
				GameSideResetResult gameSideResetResult = TryInvokeGameSideReset();
				float num2 = ((PluginConfig.PostRecoveryGraceSeconds != null) ? Mathf.Max(0f, PluginConfig.PostRecoveryGraceSeconds.Value) : 4f);
				_postRecoveryGraceUntil = Time.unscaledTime + num2;
				switch (gameSideResetResult)
				{
				case GameSideResetResult.Invoked:
					LogRecoveryStep(4, "已执行 ResetMicrophoneCapture,已启动游戏侧重置");
					break;
				case GameSideResetResult.SkippedUnsafe:
					LogRecoveryStep(4, "已执行 ResetMicrophoneCapture,已跳过游戏侧重置(当前阶段不安全)");
					break;
				case GameSideResetResult.MethodNotFound:
					LogRecoveryStep(4, "已执行 ResetMicrophoneCapture,未执行游戏侧重置(未找到重置方法)");
					break;
				default:
					LogRecoveryStep(4, "已执行 ResetMicrophoneCapture,未执行游戏侧重置(调用失败)");
					break;
				}
				LogRecoveryStep(5, $"麦克风恢复流程已触发,进入 {num2:0.##} 秒恢复宽限期,请立刻测试语音是否恢复");
				return true;
			}
			catch (Exception arg)
			{
				ManualLogSource log10 = Plugin.Log;
				if (log10 != null)
				{
					log10.LogError((object)"========== [麦克风修复 3/5] 恢复过程中出现异常 ==========");
				}
				ManualLogSource log11 = Plugin.Log;
				if (log11 != null)
				{
					log11.LogError((object)$"========== [麦克风修复 4/5] 异常:{arg} ==========");
				}
				ManualLogSource log12 = Plugin.Log;
				if (log12 != null)
				{
					log12.LogError((object)"========== [麦克风修复 5/5] 本次恢复失败,请把日志贴回来 ==========");
				}
				return false;
			}
		}

		internal static bool IsMenuSceneActive()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Scene activeScene = SceneManager.GetActiveScene();
				if (!((Scene)(ref activeScene)).IsValid())
				{
					return true;
				}
				return (((Scene)(ref activeScene)).name ?? string.Empty).IndexOf("menu", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			catch
			{
				return true;
			}
		}

		private static bool TryGetStartOfRoundInstance(out Type startOfRoundType, out object startOfRoundInstance)
		{
			startOfRoundType = Type.GetType("StartOfRound, Assembly-CSharp");
			startOfRoundInstance = null;
			if (startOfRoundType == null)
			{
				return false;
			}
			PropertyInfo property = startOfRoundType.GetProperty("Instance", StaticFlags);
			if (property != null)
			{
				startOfRoundInstance = property.GetValue(null);
			}
			if (startOfRoundInstance == null)
			{
				FieldInfo field = startOfRoundType.GetField("Instance", StaticFlags);
				if (field != null)
				{
					startOfRoundInstance = field.GetValue(null);
				}
			}
			object obj = startOfRoundInstance;
			Object val = (Object)((obj is Object) ? obj : null);
			if (val != null && val == (Object)null)
			{
				startOfRoundInstance = null;
			}
			return startOfRoundInstance != null;
		}

		internal static bool IsStartOfRoundReady()
		{
			Type startOfRoundType;
			object startOfRoundInstance;
			return TryGetStartOfRoundInstance(out startOfRoundType, out startOfRoundInstance);
		}

		internal static bool IsGameSideResetSafe()
		{
			try
			{
				if (IsMenuSceneActive())
				{
					return false;
				}
				if (!TryGetStartOfRoundInstance(out var startOfRoundType, out var startOfRoundInstance))
				{
					return false;
				}
				FieldInfo field = startOfRoundType.GetField("localPlayerController", InstanceFlags);
				if (field != null)
				{
					object value = field.GetValue(startOfRoundInstance);
					if (value == null)
					{
						return false;
					}
					Object val = (Object)((value is Object) ? value : null);
					if (val != null && val == (Object)null)
					{
						return false;
					}
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void LogRecoveryStep(int step, string message)
		{
			if (PluginConfig.ShowFiveStepRecoveryLogs != null && PluginConfig.ShowFiveStepRecoveryLogs.Value)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)$"========== [麦克风修复 {step}/5] {message} ==========");
				}
			}
			else if (PluginConfig.DebugEnabled)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"[MicRecovery] Step {step}/5: {message}");
				}
			}
		}

		private static string PickPreferredDevice(string[] devices, string currentMicName)
		{
			if (devices == null || devices.Length == 0)
			{
				return null;
			}
			if (PluginConfig.PreferCurrentDeviceIfStillExists != null && PluginConfig.PreferCurrentDeviceIfStillExists.Value && !string.IsNullOrWhiteSpace(currentMicName) && devices.Any((string d) => string.Equals(d, currentMicName, StringComparison.Ordinal)))
			{
				return currentMicName;
			}
			string[] array = (from x in ((PluginConfig.PreferredDeviceKeywords != null) ? PluginConfig.PreferredDeviceKeywords.Value : "Maxwell,Audeze").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
				select x.Trim() into x
				where !string.IsNullOrWhiteSpace(x)
				select x).ToArray();
			foreach (string keyword in array)
			{
				string text = devices.FirstOrDefault((string d) => !string.IsNullOrEmpty(d) && d.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0);
				if (!string.IsNullOrEmpty(text))
				{
					return text;
				}
			}
			return devices.FirstOrDefault((string d) => !string.IsNullOrWhiteSpace(d));
		}

		private static GameSideResetResult TryInvokeGameSideReset()
		{
			try
			{
				if (!IsGameSideResetSafe())
				{
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)"[MicRecovery] 当前不适合调用游戏侧重置入口,已跳过 ResetDissonanceCommsComponent。");
						}
					}
					return GameSideResetResult.SkippedUnsafe;
				}
				if (!TryGetStartOfRoundInstance(out var startOfRoundType, out var startOfRoundInstance))
				{
					LogGameSideResetCompatibilityWarningOnce("无法获取 StartOfRound 实例");
					return GameSideResetResult.SkippedUnsafe;
				}
				MethodInfo method = startOfRoundType.GetMethod("ResetDissonanceCommsComponent", InstanceFlags);
				if (method == null)
				{
					LogGameSideResetCompatibilityWarningOnce("未找到 ResetDissonanceCommsComponent 方法");
					if (PluginConfig.DebugEnabled)
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogWarning((object)"[MicRecovery] 未找到 ResetDissonanceCommsComponent 方法。");
						}
					}
					return GameSideResetResult.MethodNotFound;
				}
				if (method.Invoke(startOfRoundInstance, null) is IEnumerator routine && (Object)(object)Plugin.Instance != (Object)null)
				{
					((MonoBehaviour)Plugin.Instance).StartCoroutine(SafeRunGameSideResetCoroutine(routine));
				}
				return GameSideResetResult.Invoked;
			}
			catch (Exception ex)
			{
				if (PluginConfig.DebugEnabled)
				{
					ManualLogSource log3 = Plugin.Log;
					if (log3 != null)
					{
						log3.LogWarning((object)("[MicRecovery] 调用游戏侧重置入口失败:" + ex.Message));
					}
				}
				return GameSideResetResult.Failed;
			}
		}

		private static void LogGameSideResetCompatibilityWarningOnce(string reason)
		{
			if (!_gameSideResetCompatibilityWarningLogged)
			{
				_gameSideResetCompatibilityWarningLogged = true;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[MicRecovery] 游戏侧重置入口不可用(" + reason + "),已降级为仅执行本地 ResetMicrophoneCapture。"));
				}
			}
		}

		[IteratorStateMachine(typeof(<SafeRunGameSideResetCoroutine>d__19))]
		private static IEnumerator SafeRunGameSideResetCoroutine(IEnumerator routine)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SafeRunGameSideResetCoroutine>d__19(0)
			{
				routine = routine
			};
		}
	}
}