Decompiled source of MineshaftElevatorMusic v1.0.6

BepInEx\plugins\MineshaftElevatorMusic\MineshaftElevatorMusic.dll

Decompiled 5 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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 Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MineshaftElevatorMusic")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.6.0")]
[assembly: AssemblyInformationalVersion("1.0.6")]
[assembly: AssemblyProduct("MineshaftElevatorMusic")]
[assembly: AssemblyTitle("MineshaftElevatorMusic")]
[assembly: AssemblyVersion("1.0.6.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 MineshaftElevatorMusic
{
	[BepInPlugin("pl.mineshaft.elevator.music", "Mineshaft Elevator Music", "1.0.6")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <LoadClipIfNeeded>d__50 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public Plugin <>4__this;

			public string fileName;

			private UnityWebRequest <req>5__2;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<req>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
				//IL_0103: Invalid comparison between Unknown and I4
				//IL_0087: 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)
				//IL_008e: 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)
				bool result;
				try
				{
					int num = <>1__state;
					Plugin plugin = <>4__this;
					switch (num)
					{
					default:
						result = false;
						break;
					case 0:
					{
						<>1__state = -1;
						if (plugin._clipCache.ContainsKey(fileName))
						{
							result = false;
							break;
						}
						string text = Path.Combine(plugin._musicFolder, fileName);
						if (!File.Exists(text))
						{
							plugin._log.LogWarning((object)("File not found: " + text));
							result = false;
							break;
						}
						string text2 = Path.GetExtension(text).ToLowerInvariant();
						AudioType val = plugin.GuessAudioType(text2);
						if ((int)val == 0)
						{
							plugin._log.LogWarning((object)("Unsupported audio extension: " + text2));
							result = false;
							break;
						}
						<req>5__2 = UnityWebRequestMultimedia.GetAudioClip(new Uri(text).AbsoluteUri, val);
						<>1__state = -3;
						<>2__current = <req>5__2.SendWebRequest();
						<>1__state = 1;
						result = true;
						break;
					}
					case 1:
						<>1__state = -3;
						if ((int)<req>5__2.result != 1)
						{
							plugin._log.LogWarning((object)("Failed to load " + fileName + ": " + <req>5__2.error));
							result = false;
						}
						else
						{
							AudioClip content = DownloadHandlerAudioClip.GetContent(<req>5__2);
							if (!((Object)(object)content == (Object)null))
							{
								plugin._clipCache[fileName] = content;
								plugin._log.LogInfo((object)("Loaded clip: " + fileName));
								<>m__Finally1();
								<req>5__2 = null;
								result = false;
								break;
							}
							plugin._log.LogWarning((object)("Loaded clip is null: " + fileName));
							result = false;
						}
						<>m__Finally1();
						break;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
				return result;
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<req>5__2 != null)
				{
					((IDisposable)<req>5__2).Dispose();
				}
			}

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

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

			private object <>2__current;

			public Plugin <>4__this;

			public string fileName;

			public double startServerTime;

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

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

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

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

			private bool MoveNext()
			{
				//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
				int num = <>1__state;
				Plugin plugin = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if ((Object)(object)plugin._elevatorSource == (Object)null)
					{
						plugin._elevatorSource = plugin.FindElevatorAudioSource();
					}
					if ((Object)(object)plugin._elevatorSource == (Object)null)
					{
						plugin._log.LogWarning((object)"Could not find elevator audio source to play custom music.");
						return false;
					}
					<>2__current = plugin.LoadClipIfNeeded(fileName);
					<>1__state = 1;
					return true;
				case 1:
				{
					<>1__state = -1;
					if (!plugin._clipCache.TryGetValue(fileName, out var value) || (Object)(object)value == (Object)null)
					{
						plugin._log.LogWarning((object)("Missing clip " + fileName + "."));
						return false;
					}
					double num2;
					if (!((Object)(object)NetworkManager.Singleton != (Object)null))
					{
						num2 = 0.0;
					}
					else
					{
						NetworkTime serverTime = NetworkManager.Singleton.ServerTime;
						num2 = ((NetworkTime)(ref serverTime)).Time;
					}
					double num3 = num2;
					double num4 = Math.Max(0.0, startServerTime - num3);
					double num5 = AudioSettings.dspTime + num4;
					plugin._elevatorSource.Stop();
					plugin._elevatorSource.clip = value;
					plugin._elevatorSource.PlayScheduled(num5);
					plugin._isPlayingCustom = true;
					plugin._currentTrackName = fileName;
					return false;
				}
				}
			}

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

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

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

			private object <>2__current;

			public Plugin <>4__this;

			private List<string>.Enumerator <>7__wrap1;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>7__wrap1 = default(List<string>.Enumerator);
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					int num = <>1__state;
					Plugin plugin = <>4__this;
					switch (num)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<>7__wrap1 = plugin._files.GetEnumerator();
						<>1__state = -3;
						break;
					case 1:
						<>1__state = -3;
						break;
					}
					if (<>7__wrap1.MoveNext())
					{
						string current = <>7__wrap1.Current;
						<>2__current = plugin.LoadClipIfNeeded(Path.GetFileName(current));
						<>1__state = 1;
						return true;
					}
					<>m__Finally1();
					<>7__wrap1 = default(List<string>.Enumerator);
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				((IDisposable)<>7__wrap1).Dispose();
			}

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

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

			private object <>2__current;

			public Plugin <>4__this;

			private int <i>5__2;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Expected O, but got Unknown
				int num = <>1__state;
				Plugin plugin = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<i>5__2 = 0;
					break;
				case 1:
					<>1__state = -1;
					<i>5__2++;
					break;
				}
				if (<i>5__2 < 50)
				{
					if (plugin.TryRegisterLethalConfig())
					{
						return false;
					}
					<>2__current = (object)new WaitForSeconds(0.2f);
					<>1__state = 1;
					return true;
				}
				return false;
			}

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

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

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

			private object <>2__current;

			public Plugin <>4__this;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0066: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: Expected O, but got Unknown
				int num = <>1__state;
				Plugin plugin = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if ((Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.CustomMessagingManager == null)
				{
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("MineshaftElevatorMusic.Sync", new HandleNamedMessageDelegate(plugin.OnSyncMessage));
				return false;
			}

			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 const string ModGuid = "pl.mineshaft.elevator.music";

		private const string ModName = "Mineshaft Elevator Music";

		private const string ModVersion = "1.0.6";

		private const string MessageName = "MineshaftElevatorMusic.Sync";

		private string[] _vanillaNameKeywords = new string[3] { "elevator", "mineshaft", "mine" };

		private string[] _clipNameWhitelist = Array.Empty<string>();

		private string[] _objectNameWhitelist = Array.Empty<string>();

		private static readonly string[] SupportedExtensions = new string[3] { ".ogg", ".wav", ".mp3" };

		private readonly Dictionary<string, AudioClip> _clipCache = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase);

		private readonly List<string> _files = new List<string>();

		private readonly Queue<string> _shuffleQueue = new Queue<string>();

		private ManualLogSource _log;

		private string _musicFolder;

		private AudioSource _elevatorSource;

		private bool _isPlayingCustom;

		private string _currentTrackName;

		private int _lastVanillaClipId = -1;

		private double _lastVanillaStartDspTime;

		private float _vanillaStartWindow = 0.15f;

		private bool _requireElevatorActive = true;

		private bool _replaceAnyPlayingInMineshaft;

		private bool _enableDebugLogs;

		private string _lastCandidateName;

		private float _nextDebugLogTime;

		private bool _loggedNoSource;

		private ConfigEntry<string> _vanillaKeywordsEntry;

		private ConfigEntry<string> _clipWhitelistEntry;

		private ConfigEntry<string> _objectWhitelistEntry;

		private ConfigEntry<float> _startWindowEntry;

		private ConfigEntry<bool> _requirePlayingEntry;

		private ConfigEntry<bool> _replaceAnyEntry;

		private ConfigEntry<bool> _debugEntry;

		private void Awake()
		{
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			_log = ((BaseUnityPlugin)this).Logger;
			_musicFolder = Path.Combine(Paths.ConfigPath, "MineshaftElevatorMusic");
			Directory.CreateDirectory(_musicFolder);
			_log.LogInfo((object)"Mineshaft Elevator Music 1.0.6 loaded.");
			_vanillaKeywordsEntry = ((BaseUnityPlugin)this).Config.Bind<string>("General", "VanillaNameKeywords", "elevator,mineshaft,mine", "Comma-separated keywords used to detect the vanilla mineshaft elevator audio source.");
			_vanillaNameKeywords = SplitKeywords(_vanillaKeywordsEntry.Value);
			_clipWhitelistEntry = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ClipNameWhitelist", "", "Optional comma-separated list of exact clip name fragments to match (higher priority than keywords).");
			_clipNameWhitelist = SplitKeywords(_clipWhitelistEntry.Value);
			_objectWhitelistEntry = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ObjectNameWhitelist", "", "Optional comma-separated list of object name fragments to match (higher priority than keywords).");
			_objectNameWhitelist = SplitKeywords(_objectWhitelistEntry.Value);
			_startWindowEntry = ((BaseUnityPlugin)this).Config.Bind<float>("General", "VanillaStartWindowSeconds", 0.15f, "Only triggers replacement when the vanilla clip time is within this start window.");
			_vanillaStartWindow = Mathf.Clamp(_startWindowEntry.Value, 0.01f, 1f);
			_requirePlayingEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RequireElevatorAudioPlaying", true, "If true, only attempts to replace when the vanilla elevator audio is actively playing.");
			_requireElevatorActive = _requirePlayingEntry.Value;
			_replaceAnyEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ReplaceAnyPlayingInMineshaft", false, "If true, replaces the first playing audio source in the mineshaft regardless of name match.");
			_replaceAnyPlayingInMineshaft = _replaceAnyEntry.Value;
			_debugEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDebugLogs", false, "If true, logs candidate audio sources and matches to help troubleshooting.");
			_enableDebugLogs = _debugEntry.Value;
			_vanillaKeywordsEntry.SettingChanged += delegate
			{
				_vanillaNameKeywords = SplitKeywords(_vanillaKeywordsEntry.Value);
			};
			_clipWhitelistEntry.SettingChanged += delegate
			{
				_clipNameWhitelist = SplitKeywords(_clipWhitelistEntry.Value);
			};
			_objectWhitelistEntry.SettingChanged += delegate
			{
				_objectNameWhitelist = SplitKeywords(_objectWhitelistEntry.Value);
			};
			_startWindowEntry.SettingChanged += delegate
			{
				_vanillaStartWindow = Mathf.Clamp(_startWindowEntry.Value, 0.01f, 1f);
			};
			_requirePlayingEntry.SettingChanged += delegate
			{
				_requireElevatorActive = _requirePlayingEntry.Value;
			};
			_replaceAnyEntry.SettingChanged += delegate
			{
				_replaceAnyPlayingInMineshaft = _replaceAnyEntry.Value;
			};
			_debugEntry.SettingChanged += delegate
			{
				_enableDebugLogs = _debugEntry.Value;
			};
			SceneManager.sceneLoaded += OnSceneLoaded;
			new Harmony("pl.mineshaft.elevator.music").PatchAll();
			RegisterNetcodeHandler();
			RefreshFileList();
			((MonoBehaviour)this).StartCoroutine(PreloadAllClips());
			((MonoBehaviour)this).StartCoroutine(WaitForLethalConfig());
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
		}

		private void RegisterNetcodeHandler()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			if ((Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.CustomMessagingManager == null)
			{
				((MonoBehaviour)this).StartCoroutine(WaitForNetworkManager());
			}
			else
			{
				NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("MineshaftElevatorMusic.Sync", new HandleNamedMessageDelegate(OnSyncMessage));
			}
		}

		[IteratorStateMachine(typeof(<WaitForNetworkManager>d__35))]
		private IEnumerator WaitForNetworkManager()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WaitForNetworkManager>d__35(0)
			{
				<>4__this = this
			};
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			if (IsMineshaftLoaded())
			{
				_elevatorSource = null;
				_isPlayingCustom = false;
				_currentTrackName = null;
				_lastVanillaClipId = -1;
				_lastVanillaStartDspTime = 0.0;
				_loggedNoSource = false;
				_nextDebugLogTime = 0f;
			}
		}

		private void Update()
		{
			if (!IsMineshaftLoaded())
			{
				_elevatorSource = null;
				_isPlayingCustom = false;
				_currentTrackName = null;
				return;
			}
			if ((Object)(object)_elevatorSource == (Object)null)
			{
				_elevatorSource = FindElevatorAudioSource();
				if ((Object)(object)_elevatorSource != (Object)null)
				{
					_loggedNoSource = false;
				}
			}
			if (!((Object)(object)_elevatorSource == (Object)null))
			{
				if (_requireElevatorActive && !_elevatorSource.isPlaying)
				{
					_isPlayingCustom = false;
					_currentTrackName = null;
				}
				else if (!_isPlayingCustom && (Object)(object)_elevatorSource.clip != (Object)null && IsCandidateSource(_elevatorSource) && IsHost() && ShouldTriggerReplacement(_elevatorSource))
				{
					TryStartAndSync();
				}
			}
		}

		private bool IsHost()
		{
			if ((Object)(object)NetworkManager.Singleton != (Object)null)
			{
				return NetworkManager.Singleton.IsHost;
			}
			return false;
		}

		private bool IsMineshaftLoaded()
		{
			//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)
			for (int i = 0; i < SceneManager.sceneCount; i++)
			{
				Scene sceneAt = SceneManager.GetSceneAt(i);
				if (((Scene)(ref sceneAt)).isLoaded && ((Scene)(ref sceneAt)).name.IndexOf("mineshaft", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private AudioSource FindElevatorAudioSource()
		{
			AudioSource[] array = Object.FindObjectsOfType<AudioSource>(true);
			foreach (AudioSource val in array)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)val.clip == (Object)null)
				{
					continue;
				}
				string text = ((Object)val.clip).name ?? string.Empty;
				string text2 = (((Object)(object)((Component)val).gameObject != (Object)null) ? ((Object)((Component)val).gameObject).name : string.Empty);
				if (!IsCandidateNames(text, text2) && !_replaceAnyPlayingInMineshaft)
				{
					continue;
				}
				string text3 = text2 + " / " + text;
				if (_lastCandidateName != text3)
				{
					_lastCandidateName = text3;
					if (_enableDebugLogs)
					{
						_log.LogInfo((object)("Found candidate elevator audio source: " + text3));
					}
				}
				return val;
			}
			if (_enableDebugLogs && Time.time >= _nextDebugLogTime && !_loggedNoSource)
			{
				_nextDebugLogTime = Time.time + 2f;
				LogPlayingSources();
			}
			return null;
		}

		private bool IsVanillaClip(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return false;
			}
			string text = name.ToLowerInvariant();
			string[] vanillaNameKeywords = _vanillaNameKeywords;
			foreach (string value in vanillaNameKeywords)
			{
				if (text.Contains(value))
				{
					return true;
				}
			}
			return false;
		}

		private bool IsCandidateSource(AudioSource source)
		{
			if (_replaceAnyPlayingInMineshaft)
			{
				return true;
			}
			if ((Object)(object)source == (Object)null)
			{
				return false;
			}
			string clipName = (((Object)(object)source.clip != (Object)null) ? ((Object)source.clip).name : string.Empty);
			string objName = (((Object)(object)((Component)source).gameObject != (Object)null) ? ((Object)((Component)source).gameObject).name : string.Empty);
			return IsCandidateNames(clipName, objName);
		}

		private bool IsCandidateNames(string clipName, string objName)
		{
			if (MatchesAny(clipName, _clipNameWhitelist))
			{
				return true;
			}
			if (MatchesAny(objName, _objectNameWhitelist))
			{
				return true;
			}
			if (!IsVanillaClip(clipName))
			{
				return IsVanillaClip(objName);
			}
			return true;
		}

		private void RefreshFileList()
		{
			_files.Clear();
			string[] supportedExtensions = SupportedExtensions;
			foreach (string text in supportedExtensions)
			{
				string[] files = Directory.GetFiles(_musicFolder, "*" + text, SearchOption.TopDirectoryOnly);
				_files.AddRange(files);
			}
			_log.LogInfo((object)$"Found {_files.Count} music files in {_musicFolder}");
			RefillShuffleQueue();
		}

		[IteratorStateMachine(typeof(<PreloadAllClips>d__45))]
		private IEnumerator PreloadAllClips()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PreloadAllClips>d__45(0)
			{
				<>4__this = this
			};
		}

		private void TryStartAndSync()
		{
			//IL_0056: 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)
			if (_files.Count == 0)
			{
				RefreshFileList();
			}
			if (_files.Count == 0)
			{
				_log.LogWarning((object)"No music files found. Put .ogg/.wav/.mp3 in the folder.");
				return;
			}
			string nextTrackName = GetNextTrackName();
			if (string.IsNullOrWhiteSpace(nextTrackName))
			{
				_log.LogWarning((object)"Could not pick a track. Check the music folder.");
				return;
			}
			NetworkTime serverTime = NetworkManager.Singleton.ServerTime;
			double startServerTime = ((NetworkTime)(ref serverTime)).Time + 0.10000000149011612;
			SendSync(nextTrackName, startServerTime);
			((MonoBehaviour)this).StartCoroutine(PlayFromSync(nextTrackName, startServerTime));
		}

		private void SendSync(string fileName, double startServerTime)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)NetworkManager.Singleton == (Object)null)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(1024, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe(fileName, false);
				((FastBufferWriter)(ref val)).WriteValueSafe<double>(ref startServerTime, default(ForPrimitives));
				foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds)
				{
					if (connectedClientsId != NetworkManager.Singleton.LocalClientId)
					{
						NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("MineshaftElevatorMusic.Sync", connectedClientsId, val, (NetworkDelivery)3);
					}
				}
			}
			finally
			{
				((IDisposable)(FastBufferWriter)(ref val)).Dispose();
			}
		}

		private void OnSyncMessage(ulong clientId, FastBufferReader reader)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			string fileName = default(string);
			((FastBufferReader)(ref reader)).ReadValueSafe(ref fileName, false);
			double startServerTime = default(double);
			((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref startServerTime, default(ForPrimitives));
			((MonoBehaviour)this).StartCoroutine(PlayFromSync(fileName, startServerTime));
		}

		[IteratorStateMachine(typeof(<PlayFromSync>d__49))]
		private IEnumerator PlayFromSync(string fileName, double startServerTime)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PlayFromSync>d__49(0)
			{
				<>4__this = this,
				fileName = fileName,
				startServerTime = startServerTime
			};
		}

		[IteratorStateMachine(typeof(<LoadClipIfNeeded>d__50))]
		private IEnumerator LoadClipIfNeeded(string fileName)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadClipIfNeeded>d__50(0)
			{
				<>4__this = this,
				fileName = fileName
			};
		}

		private AudioType GuessAudioType(string ext)
		{
			return (AudioType)(ext switch
			{
				".ogg" => 14, 
				".wav" => 20, 
				".mp3" => 13, 
				_ => 0, 
			});
		}

		private bool ShouldTriggerReplacement(AudioSource source)
		{
			if ((Object)(object)source.clip == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)source.clip).GetInstanceID();
			double dspTime = AudioSettings.dspTime;
			if ((instanceID != _lastVanillaClipId || source.time <= _vanillaStartWindow) && source.time <= _vanillaStartWindow)
			{
				_lastVanillaClipId = instanceID;
				_lastVanillaStartDspTime = dspTime;
				if (_enableDebugLogs)
				{
					_log.LogInfo((object)$"Trigger replacement for clip {((Object)source.clip).name} at time {source.time:0.000}s.");
				}
				return true;
			}
			return false;
		}

		private string GetNextTrackName()
		{
			if (_shuffleQueue.Count == 0)
			{
				RefillShuffleQueue();
			}
			if (_shuffleQueue.Count == 0)
			{
				return null;
			}
			return _shuffleQueue.Dequeue();
		}

		private void RefillShuffleQueue()
		{
			_shuffleQueue.Clear();
			if (_files.Count == 0)
			{
				return;
			}
			List<string> list = new List<string>(_files);
			for (int num = list.Count - 1; num > 0; num--)
			{
				int index = Random.Range(0, num + 1);
				string value = list[num];
				list[num] = list[index];
				list[index] = value;
			}
			foreach (string item in list)
			{
				_shuffleQueue.Enqueue(Path.GetFileName(item));
			}
		}

		private string[] SplitKeywords(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return new string[3] { "elevator", "mineshaft", "mine" };
			}
			string[] array = value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = array[i].Trim().ToLowerInvariant();
			}
			if (array.Length != 0)
			{
				return array;
			}
			return new string[3] { "elevator", "mineshaft", "mine" };
		}

		private bool MatchesAny(string value, string[] tokens)
		{
			if (string.IsNullOrWhiteSpace(value) || tokens == null || tokens.Length == 0)
			{
				return false;
			}
			string text = value.ToLowerInvariant();
			foreach (string value2 in tokens)
			{
				if (!string.IsNullOrWhiteSpace(value2) && text.Contains(value2))
				{
					return true;
				}
			}
			return false;
		}

		private void LogPlayingSources()
		{
			try
			{
				AudioSource[] array = Object.FindObjectsOfType<AudioSource>(true);
				int num = 0;
				_log.LogInfo((object)"No elevator source found yet. Listing up to 10 playing AudioSources:");
				AudioSource[] array2 = array;
				foreach (AudioSource val in array2)
				{
					if (!((Object)(object)val == (Object)null) && val.isPlaying)
					{
						string arg = (((Object)(object)((Component)val).gameObject != (Object)null) ? ((Object)((Component)val).gameObject).name : "<null>");
						string arg2 = (((Object)(object)val.clip != (Object)null) ? ((Object)val.clip).name : "<null>");
						_log.LogInfo((object)$"  Playing: {arg} / {arg2} (time={val.time:0.00})");
						num++;
						if (num >= 10)
						{
							break;
						}
					}
				}
				if (num == 0)
				{
					_log.LogInfo((object)"  (No playing AudioSources found)");
				}
				_loggedNoSource = true;
			}
			catch (Exception ex)
			{
				_log.LogDebug((object)("Failed to log playing sources: " + ex.Message));
			}
		}

		[IteratorStateMachine(typeof(<WaitForLethalConfig>d__58))]
		private IEnumerator WaitForLethalConfig()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WaitForLethalConfig>d__58(0)
			{
				<>4__this = this
			};
		}

		private bool TryRegisterLethalConfig()
		{
			try
			{
				Type lethalConfigManagerType = GetLethalConfigManagerType();
				if (lethalConfigManagerType == null)
				{
					return false;
				}
				if (lethalConfigManagerType.GetMethod("AddConfigItem", new Type[1] { typeof(object) }) == null)
				{
					return false;
				}
				lethalConfigManagerType.GetMethod("QueueCustomConfigFileForAutoGeneration", new Type[1] { typeof(ConfigFile) })?.Invoke(null, new object[1] { ((BaseUnityPlugin)this).Config });
				lethalConfigManagerType.GetMethod("SetModDescription", new Type[1] { typeof(string) })?.Invoke(null, new object[1] { "Replaces the mineshaft elevator ride music with synced random tracks from a local folder." });
				AddTextItem("VanillaNameKeywords", "Comma-separated keywords used to detect the vanilla mineshaft elevator audio source.", _vanillaKeywordsEntry);
				AddTextItem("ClipNameWhitelist", "Optional comma-separated clip name fragments to match.", _clipWhitelistEntry);
				AddTextItem("ObjectNameWhitelist", "Optional comma-separated object name fragments to match.", _objectWhitelistEntry);
				AddFloatSlider("VanillaStartWindowSeconds", "Only triggers replacement when the vanilla clip time is within this start window.", _startWindowEntry, 0.01f, 1f);
				AddBoolItem("RequireElevatorAudioPlaying", "Only attempts to replace when the vanilla elevator audio is actively playing.", _requirePlayingEntry);
				AddBoolItem("ReplaceAnyPlayingInMineshaft", "Replace the first playing audio source in the mineshaft (troubleshooting).", _replaceAnyEntry);
				AddBoolItem("EnableDebugLogs", "Log candidate audio sources and triggers (troubleshooting).", _debugEntry);
				_log.LogInfo((object)"LethalConfig integration registered.");
				return true;
			}
			catch (Exception ex)
			{
				_log.LogDebug((object)("LethalConfig integration skipped: " + ex.Message));
				return false;
			}
		}

		private void AddTextItem(string name, string description, ConfigEntry<string> entry)
		{
			TryAddConfigItem("LethalConfig.ConfigItems.TextInputFieldConfigItem", "LethalConfig.ConfigItems.TextInputFieldOptions", entry, name, description);
		}

		private void AddBoolItem(string name, string description, ConfigEntry<bool> entry)
		{
			TryAddConfigItem("LethalConfig.ConfigItems.BoolCheckBoxConfigItem", "LethalConfig.ConfigItems.BoolCheckBoxOptions", entry, name, description);
		}

		private void AddFloatSlider(string name, string description, ConfigEntry<float> entry, float min, float max)
		{
			TryAddConfigItem("LethalConfig.ConfigItems.FloatSliderConfigItem", "LethalConfig.ConfigItems.FloatSliderOptions", entry, name, description, min, max);
		}

		private void TryAddConfigItem(string itemTypeName, string optionsTypeName, object entry, string name, string description, float? min = null, float? max = null)
		{
			Type type = Type.GetType(itemTypeName + ", LethalConfig");
			Type type2 = Type.GetType(optionsTypeName + ", LethalConfig");
			MethodInfo methodInfo = GetLethalConfigManagerType()?.GetMethod("AddConfigItem", new Type[1] { typeof(object) });
			if (!(type == null) && !(type2 == null) && !(methodInfo == null))
			{
				object obj = Activator.CreateInstance(type2);
				SetProp(type2, obj, "Section", "General");
				SetProp(type2, obj, "Name", name);
				SetProp(type2, obj, "Description", description);
				SetProp(type2, obj, "RequiresRestart", false);
				if (min.HasValue)
				{
					SetProp(type2, obj, "Min", min.Value);
				}
				if (max.HasValue)
				{
					SetProp(type2, obj, "Max", max.Value);
				}
				object obj2 = Activator.CreateInstance(type, entry, obj);
				methodInfo.Invoke(null, new object[1] { obj2 });
			}
		}

		private Type GetLethalConfigManagerType()
		{
			Type type = Type.GetType("LethalConfig.LethalConfigManager, LethalConfig");
			if (type != null)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (string.Equals(assembly.GetName().Name, "LethalConfig", StringComparison.OrdinalIgnoreCase))
				{
					type = assembly.GetType("LethalConfig.LethalConfigManager", throwOnError: false);
					if (type != null)
					{
						return type;
					}
				}
			}
			return null;
		}

		private void SetProp(Type type, object instance, string prop, object value)
		{
			PropertyInfo property = type.GetProperty(prop);
			if (property != null && property.CanWrite)
			{
				property.SetValue(instance, value, null);
			}
		}
	}
}