Decompiled source of BoomboxCartUpgrade v1.3.2

BoomBoxCartUpgradeMod.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BoomBoxCartMod.Patches;
using BoomBoxCartMod.Util;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("BoomboxCartUpgrade")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BoomboxCartUpgrade")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("37e852e0-b511-4315-8182-68c0a54e1ba9")]
[assembly: AssemblyFileVersion("1.3.2.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.2.0")]
[module: UnverifiableCode]
[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 BoomBoxCartMod
{
	public class Boombox : MonoBehaviourPunCallbacks
	{
		[Serializable]
		private class SharedBoomboxState
		{
			public int version;

			public string key;

			public bool isPlaying;

			public bool pendingPlaybackStart;

			public float absVolume;

			public bool loopQueue;

			public int currentSongIndex = -1;

			public int playbackStartTimestamp;

			public int playbackTime;

			public string[] queueTitles = Array.Empty<string>();

			public string[] queueUrls = Array.Empty<string>();

			public int[] queueStartTimes = Array.Empty<int>();

			public SharedAudioEntry[] queue = Array.Empty<SharedAudioEntry>();
		}

		[Serializable]
		private class SharedAudioEntry
		{
			public string title;

			public string url;

			public int startTime;
		}

		public class BoomboxData
		{
			public string key = Guid.NewGuid().ToString();

			public AudioEntry currentSong = null;

			public List<AudioEntry> playbackQueue = new List<AudioEntry>();

			public bool isPlaying = false;

			public float absVolume = 0.6f;

			public float personalVolumePercentage = 0.35f;

			public bool loopQueue = false;

			public bool underglowEnabled = false;

			public bool visualizerEnabled = true;

			public bool pendingPlaybackStart = false;

			public int stateVersion = 0;

			public int playbackTime = 0;

			public int playbackStartTimestamp = 0;
		}

		public class AudioEntry
		{
			public string Title;

			public string Url;

			public int StartTime = 0;

			public AudioEntry(string title, string url)
			{
				Title = title;
				Url = url;
			}

			public int PeekStartTime(Boombox boombox)
			{
				if (boombox.data != null && boombox.data.playbackTime != 0)
				{
					return boombox.data.playbackTime;
				}
				return StartTime;
			}

			public int UseStartTime(Boombox boombox)
			{
				int result = PeekStartTime(boombox);
				if (boombox.data != null && boombox.data.playbackTime != 0)
				{
					boombox.data.playbackTime = 0;
				}
				else if (Instance.UseTimeStampOnce.Value)
				{
					StartTime = 0;
				}
				return result;
			}

			public AudioClip GetAudioClip()
			{
				return DownloadHelper.downloadedClips.ContainsKey(Url) ? DownloadHelper.downloadedClips[Url] : null;
			}
		}

		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		public PhotonView photonView;

		public AudioSource audioSource;

		public Visualizer visualizer;

		public DownloadHelper downloadHelper = null;

		private bool syncFinished = false;

		public float minDistance = 3f;

		public float maxDistanceBase = 15f;

		public float maxDistanceAddition = 30f;

		public bool isAwaitingSyncPlayback = false;

		public bool startPlayBackOnDownload = true;

		public BoomboxData data = new BoomboxData();

		private AudioLowPassFilter lowPassFilter;

		public static int qualityLevel = 4;

		private static bool mutePressed = false;

		private const string SharedStatePropertyPrefix = "boomboxState.";

		private bool isApplyingSharedState = false;

		private string lastSharedStateJson = null;

		private string pendingCurrentSongDownloadUrl = null;

		private int lastAppliedSharedStateVersion = -1;

		private float monsterAttractTimer = 0f;

		private float monsterAttractInterval = 1f;

		private static bool applyQualityToDownloads = false;

		private static bool monstersCanHearMusic = false;

		private static ManualLogSource Logger => Instance.logger;

		public static bool ApplyQualityToDownloads
		{
			get
			{
				return applyQualityToDownloads;
			}
			set
			{
				applyQualityToDownloads = value;
			}
		}

		public static bool MonstersCanHearMusic
		{
			get
			{
				return monstersCanHearMusic;
			}
			set
			{
				monstersCanHearMusic = value;
			}
		}

		public bool LoopQueue
		{
			get
			{
				return data.loopQueue;
			}
			set
			{
				data.loopQueue = value;
			}
		}

		private void Awake()
		{
			audioSource = ((Component)this).gameObject.AddComponent<AudioSource>();
			audioSource.volume = 0.15f;
			audioSource.spatialBlend = 1f;
			audioSource.playOnAwake = false;
			audioSource.rolloffMode = (AudioRolloffMode)2;
			audioSource.spread = 90f;
			audioSource.dopplerLevel = 0f;
			audioSource.reverbZoneMix = 1f;
			audioSource.spatialize = true;
			audioSource.loop = false;
			audioSource.mute = Instance.baseListener.audioMuted;
			lowPassFilter = ((Component)this).gameObject.AddComponent<AudioLowPassFilter>();
			((Behaviour)lowPassFilter).enabled = false;
			downloadHelper = ((Component)this).gameObject.AddComponent<DownloadHelper>();
			UpdateAudioRangeBasedOnVolume(audioSource.volume);
			photonView = ((Component)this).GetComponent<PhotonView>();
			isAwaitingSyncPlayback = data.pendingPlaybackStart;
			if ((Object)(object)photonView == (Object)null)
			{
				Logger.LogError((object)"PhotonView not found on Boombox object.");
				return;
			}
			if ((Object)(object)((Component)this).GetComponent<BoomboxController>() == (Object)null)
			{
				((Component)this).gameObject.AddComponent<BoomboxController>();
			}
			if ((Object)(object)((Component)this).GetComponent<Visualizer>() == (Object)null)
			{
				visualizer = ((Component)this).gameObject.AddComponent<Visualizer>();
				visualizer.audioSource = audioSource;
			}
			PersistentData.SetBoomboxViewInitialized(photonView.ViewID);
			Logger.LogInfo((object)$"Boombox initialized on this cart. AudioSource: {audioSource}, PhotonView: {photonView}");
		}

		private void Start()
		{
			ApplyVisualStateFromData();
			LoadSharedStateFromRoom();
		}

		private void Update()
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.IsMasterClient && data.isPlaying && MonstersCanHearMusic && (Object)(object)EnemyDirector.instance != (Object)null)
			{
				monsterAttractTimer += Time.deltaTime;
				if (monsterAttractTimer >= monsterAttractInterval)
				{
					EnemyDirector.instance.SetInvestigate(((Component)this).transform.position, 5f, false);
					monsterAttractTimer = 0f;
				}
			}
			else
			{
				monsterAttractTimer = 0f;
			}
			if (Instance.baseListener.audioMuted != audioSource.mute)
			{
				audioSource.mute = Instance.baseListener.audioMuted;
			}
			bool flag = (Object)(object)audioSource.clip != (Object)null && audioSource.time >= Math.Max(0f, audioSource.clip.length - 0.05f);
			if (!(syncFinished && data.isPlaying && !audioSource.isPlaying && flag) || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			int currentSongIndex = GetCurrentSongIndex();
			if (currentSongIndex == -1)
			{
				DismissQueueLocal();
			}
			else if (currentSongIndex + 1 >= data.playbackQueue.Count)
			{
				if (LoopQueue && data.playbackQueue.Count > 0)
				{
					SelectSongIndex(0);
					return;
				}
				data.currentSong = null;
				data.isPlaying = false;
				SetPlaybackReferenceFromSeconds(0f);
				PublishSharedState();
			}
			else
			{
				SelectSongIndex(currentSongIndex + 1);
			}
		}

		public void TogglePlaying(bool value)
		{
			data.isPlaying = value;
		}

		public static long GetCurrentTimeMilliseconds()
		{
			return PhotonNetwork.ServerTimestamp;
		}

		public long GetRelativePlaybackMilliseconds()
		{
			return GetCurrentTimeMilliseconds() - (long)Math.Round(GetTrackedPlaybackSeconds() * 1000f);
		}

		public static string GetSongTitle(string url)
		{
			if (DownloadHelper.songTitles.ContainsKey(url))
			{
				return DownloadHelper.songTitles[url];
			}
			return null;
		}

		public int GetCurrentSongIndex()
		{
			if (data.currentSong == null)
			{
				return -1;
			}
			return data.playbackQueue.IndexOf(data.currentSong);
		}

		private string GetSharedStatePropertyKey()
		{
			return "boomboxState." + photonView.ViewID;
		}

		private SharedBoomboxState CreateSharedState()
		{
			return new SharedBoomboxState
			{
				version = data.stateVersion,
				key = data.key,
				isPlaying = (data.currentSong != null && data.isPlaying),
				pendingPlaybackStart = (data.currentSong != null && data.pendingPlaybackStart),
				absVolume = data.absVolume,
				loopQueue = data.loopQueue,
				currentSongIndex = GetCurrentSongIndex(),
				playbackStartTimestamp = data.playbackStartTimestamp,
				playbackTime = data.playbackTime,
				queueTitles = data.playbackQueue.Select((AudioEntry entry) => entry.Title).ToArray(),
				queueUrls = data.playbackQueue.Select((AudioEntry entry) => entry.Url).ToArray(),
				queueStartTimes = data.playbackQueue.Select((AudioEntry entry) => entry.StartTime).ToArray(),
				queue = data.playbackQueue.Select((AudioEntry entry) => new SharedAudioEntry
				{
					title = entry.Title,
					url = entry.Url,
					startTime = entry.StartTime
				}).ToArray()
			};
		}

		private List<AudioEntry> CreateQueueFromState(SharedBoomboxState state)
		{
			if (state.queueUrls != null && state.queueUrls.Length != 0)
			{
				List<AudioEntry> list = new List<AudioEntry>(state.queueUrls.Length);
				for (int i = 0; i < state.queueUrls.Length; i++)
				{
					string text = state.queueUrls[i];
					string text2 = ((state.queueTitles != null && i < state.queueTitles.Length && !string.IsNullOrWhiteSpace(state.queueTitles[i])) ? state.queueTitles[i] : "Unknown Title");
					int startTime = ((state.queueStartTimes != null && i < state.queueStartTimes.Length) ? state.queueStartTimes[i] : 0);
					if (!string.IsNullOrWhiteSpace(text) && !DownloadHelper.songTitles.ContainsKey(text))
					{
						DownloadHelper.songTitles[text] = text2;
					}
					list.Add(new AudioEntry(text2, text)
					{
						StartTime = startTime
					});
				}
				return list;
			}
			if (state.queue == null)
			{
				return new List<AudioEntry>();
			}
			return state.queue.Select(delegate(SharedAudioEntry entry)
			{
				string text3 = (string.IsNullOrWhiteSpace(entry.title) ? "Unknown Title" : entry.title);
				string url = entry.url;
				if (!string.IsNullOrWhiteSpace(url) && !DownloadHelper.songTitles.ContainsKey(url))
				{
					DownloadHelper.songTitles[url] = text3;
				}
				return new AudioEntry(text3, url)
				{
					StartTime = entry.startTime
				};
			}).ToList();
		}

		private void LoadSharedStateFromRoom()
		{
			if (PhotonNetwork.IsConnected && PhotonNetwork.CurrentRoom != null && !((Object)(object)photonView == (Object)null))
			{
				if (((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)GetSharedStatePropertyKey(), out object value) && value is string text && !string.IsNullOrWhiteSpace(text))
				{
					ApplySharedStateJson(text);
				}
				else if (PhotonNetwork.IsMasterClient)
				{
					PublishSharedState(force: true);
				}
			}
		}

		public override void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
		{
			((MonoBehaviourPunCallbacks)this).OnRoomPropertiesUpdate(propertiesThatChanged);
			if (!((Object)(object)photonView == (Object)null) && propertiesThatChanged != null && ((Dictionary<object, object>)(object)propertiesThatChanged).TryGetValue((object)GetSharedStatePropertyKey(), out object value) && value is string text && !string.IsNullOrWhiteSpace(text))
			{
				ApplySharedStateJson(text);
			}
		}

		private void ApplySharedStateJson(string json)
		{
			SharedBoomboxState sharedBoomboxState;
			try
			{
				sharedBoomboxState = JsonUtility.FromJson<SharedBoomboxState>(json);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Logger;
				PhotonView obj = photonView;
				logger.LogError((object)$"Failed to deserialize boombox state for cart {((obj != null) ? new int?(obj.ViewID) : null)}: {ex.Message}");
				return;
			}
			if (sharedBoomboxState != null && sharedBoomboxState.version >= lastAppliedSharedStateVersion)
			{
				lastSharedStateJson = json;
				lastAppliedSharedStateVersion = sharedBoomboxState.version;
				ApplySharedState(sharedBoomboxState);
			}
		}

		private void ApplySharedState(SharedBoomboxState state)
		{
			int currentSongIndex = GetCurrentSongIndex();
			string text = data.currentSong?.Url;
			isApplyingSharedState = true;
			try
			{
				data.key = (string.IsNullOrWhiteSpace(state.key) ? data.key : state.key);
				data.stateVersion = Math.Max(data.stateVersion, state.version);
				data.playbackQueue = CreateQueueFromState(state);
				data.absVolume = Mathf.Clamp01(state.absVolume);
				data.loopQueue = state.loopQueue;
				data.playbackTime = Math.Max(0, state.playbackTime);
				data.playbackStartTimestamp = state.playbackStartTimestamp;
				if (state.currentSongIndex >= 0 && state.currentSongIndex < data.playbackQueue.Count)
				{
					data.currentSong = data.playbackQueue[state.currentSongIndex];
				}
				else
				{
					data.currentSong = null;
				}
				data.pendingPlaybackStart = data.currentSong != null && state.pendingPlaybackStart;
				data.isPlaying = data.currentSong != null && state.isPlaying;
				float volume = data.absVolume * data.personalVolumePercentage;
				audioSource.volume = volume;
				UpdateAudioRangeBasedOnVolume(volume);
				ApplyVisualStateFromData();
				bool songChanged = currentSongIndex != state.currentSongIndex || text != data.currentSong?.Url;
				ApplySharedPlaybackState(songChanged);
				((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
				UpdateStatusFromState();
				syncFinished = true;
			}
			finally
			{
				isApplyingSharedState = false;
			}
		}

		private void ApplySharedPlaybackState(bool songChanged)
		{
			if (data.currentSong?.Url == null)
			{
				isAwaitingSyncPlayback = false;
				data.pendingPlaybackStart = false;
				StopLocalPlayback(updateSharedFlag: false);
				UpdateUIStatus("Ready to play music! Enter a Video URL");
				return;
			}
			AudioClip audioClip = data.currentSong.GetAudioClip();
			if ((Object)(object)audioClip == (Object)null)
			{
				StopLocalPlayback(updateSharedFlag: false);
				isAwaitingSyncPlayback = data.pendingPlaybackStart;
				startPlayBackOnDownload = data.pendingPlaybackStart;
				if (songChanged || data.pendingPlaybackStart)
				{
					UpdateUIStatus("Loading: " + data.currentSong.Title);
				}
				if (!data.pendingPlaybackStart)
				{
					EnsureCurrentSongDownloaded();
				}
				return;
			}
			isAwaitingSyncPlayback = data.pendingPlaybackStart;
			if ((Object)(object)audioSource.clip != (Object)(object)audioClip)
			{
				audioSource.clip = audioClip;
				SetQuality(qualityLevel);
				UpdateAudioRangeBasedOnVolume(audioSource.volume);
			}
			SetPlaybackTime(data.playbackStartTimestamp);
			if (data.pendingPlaybackStart)
			{
				if (audioSource.isPlaying)
				{
					audioSource.Pause();
				}
				UpdateUIStatus("Loading: " + data.currentSong.Title);
			}
			else if (data.isPlaying)
			{
				if (!audioSource.isPlaying)
				{
					audioSource.Play();
				}
				UpdateUIStatus("Now playing: " + data.currentSong.Title);
			}
			else
			{
				if (audioSource.isPlaying)
				{
					audioSource.Pause();
				}
				UpdateUIStatus("Ready to play: " + data.currentSong.Title);
			}
		}

		public void ApplyVisualStateFromData()
		{
			VisualEffects visualEffects = ((Component)this).GetComponent<VisualEffects>();
			if ((Object)(object)visualEffects == (Object)null)
			{
				visualEffects = ((Component)this).gameObject.AddComponent<VisualEffects>();
			}
			visualEffects.SetLights(data.underglowEnabled);
			Visualizer visualizer = ((Component)this).GetComponent<Visualizer>();
			if (data.visualizerEnabled)
			{
				if ((Object)(object)visualizer == (Object)null)
				{
					visualizer = ((Component)this).gameObject.AddComponent<Visualizer>();
				}
				visualizer.audioSource = audioSource;
				this.visualizer = visualizer;
			}
			else if ((Object)(object)visualizer != (Object)null)
			{
				Object.Destroy((Object)(object)visualizer);
				this.visualizer = null;
			}
		}

		private async void EnsureCurrentSongDownloaded()
		{
			string url = data.currentSong?.Url;
			if (string.IsNullOrWhiteSpace(url) || (Object)(object)downloadHelper == (Object)null || DownloadHelper.downloadedClips.ContainsKey(url) || pendingCurrentSongDownloadUrl == url)
			{
				return;
			}
			pendingCurrentSongDownloadUrl = url;
			try
			{
				await downloadHelper.StartAudioDownload(url);
			}
			finally
			{
				if (pendingCurrentSongDownloadUrl == url)
				{
					pendingCurrentSongDownloadUrl = null;
				}
			}
			if (!((Object)(object)this == (Object)null) && !(data.currentSong?.Url != url))
			{
				ApplySharedPlaybackState(songChanged: false);
			}
		}

		private void UpdateStatusFromState()
		{
			if (data.currentSong == null)
			{
				UpdateUIStatus("Ready to play music! Enter a Video URL");
			}
			else if (data.pendingPlaybackStart)
			{
				UpdateUIStatus("Loading: " + data.currentSong.Title);
			}
			else if (data.isPlaying)
			{
				UpdateUIStatus("Now playing: " + data.currentSong.Title);
			}
			else
			{
				UpdateUIStatus("Ready to play: " + data.currentSong.Title);
			}
		}

		public void PublishSharedState(bool force = false)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			//IL_00ad: Expected O, but got Unknown
			if (isApplyingSharedState || !PhotonNetwork.IsConnected || PhotonNetwork.CurrentRoom == null || (Object)(object)photonView == (Object)null)
			{
				return;
			}
			if (PhotonNetwork.IsMasterClient)
			{
				data.stateVersion++;
			}
			SharedBoomboxState sharedBoomboxState = CreateSharedState();
			string text = JsonUtility.ToJson((object)sharedBoomboxState);
			if (!force && text == lastSharedStateJson)
			{
				if (PhotonNetwork.IsMasterClient)
				{
					data.stateVersion--;
				}
				return;
			}
			Hashtable val = new Hashtable();
			((Dictionary<object, object>)val).Add((object)GetSharedStatePropertyKey(), (object)text);
			Hashtable val2 = val;
			PhotonNetwork.CurrentRoom.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null);
			lastSharedStateJson = text;
			syncFinished = true;
			if (PhotonNetwork.IsMasterClient)
			{
				lastAppliedSharedStateVersion = sharedBoomboxState.version;
				ApplySharedPlaybackState(songChanged: false);
				((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
				UpdateStatusFromState();
			}
		}

		private void StopLocalPlayback(bool updateSharedFlag)
		{
			if (audioSource.isPlaying)
			{
				audioSource.Stop();
			}
			if (updateSharedFlag)
			{
				TogglePlaying(value: false);
			}
			audioSource.clip = null;
		}

		private void SetPlaybackReferenceFromSeconds(float seconds)
		{
			data.playbackStartTimestamp = (int)((double)GetCurrentTimeMilliseconds() - Math.Round(Math.Max(0f, seconds) * 1000f));
		}

		private float GetTrackedPlaybackSeconds()
		{
			AudioSource obj = audioSource;
			if ((Object)(object)((obj != null) ? obj.clip : null) != (Object)null)
			{
				return audioSource.time;
			}
			return Math.Max(0f, (float)(GetCurrentTimeMilliseconds() - data.playbackStartTimestamp) / 1000f);
		}

		private void CleanupCurrentPlayback()
		{
			StopLocalPlayback(updateSharedFlag: true);
		}

		private bool ShouldRequestMasterMutation()
		{
			return PhotonNetwork.IsConnected && PhotonNetwork.CurrentRoom != null && !PhotonNetwork.IsMasterClient;
		}

		public void StartPlayBack()
		{
			AudioClip val = data.currentSong?.GetAudioClip();
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogError((object)"Clip not found for current song");
				CleanupCurrentPlayback();
				return;
			}
			if ((Object)(object)audioSource.clip != (Object)(object)val)
			{
				CleanupCurrentPlayback();
				audioSource.clip = val;
				SetQuality(qualityLevel);
				UpdateAudioRangeBasedOnVolume(audioSource.volume);
				audioSource.Play();
			}
			audioSource.Play();
			TogglePlaying(value: true);
		}

		public void PausePlayBack()
		{
			audioSource.Pause();
			TogglePlaying(value: false);
		}

		public void SetPlaybackTime(long startTimeMillis)
		{
			audioSource.time = Math.Max(0f, GetCurrentTimeMilliseconds() - startTimeMillis) / 1000f;
		}

		public void EnqueueSongLocal(string url, int seconds)
		{
			if (string.IsNullOrWhiteSpace(url))
			{
				return;
			}
			if (ShouldRequestMasterMutation())
			{
				BaseListener.RPC(photonView, "RequestEnqueueSong", (RpcTarget)2, url, seconds, PhotonNetwork.LocalPlayer.ActorNumber);
				return;
			}
			string title = (DownloadHelper.songTitles.ContainsKey(url) ? DownloadHelper.songTitles[url] : "Unknown Title");
			AudioEntry audioEntry = new AudioEntry(title, url)
			{
				StartTime = seconds
			};
			data.playbackQueue.Add(audioEntry);
			if (data.currentSong == null)
			{
				data.currentSong = audioEntry;
				data.isPlaying = false;
				data.pendingPlaybackStart = true;
				isAwaitingSyncPlayback = PhotonNetwork.IsMasterClient;
				startPlayBackOnDownload = true;
				SetPlaybackReferenceFromSeconds(audioEntry.PeekStartTime(this));
			}
			PublishSharedState();
			if (PhotonNetwork.IsMasterClient)
			{
				downloadHelper.EnqueueDownload(url);
				downloadHelper.StartDownloadJob();
			}
		}

		public void CommitPlaybackSeek()
		{
			if (data.currentSong != null)
			{
				if (ShouldRequestMasterMutation())
				{
					BaseListener.RPC(photonView, "RequestCommitPlaybackSeek", (RpcTarget)2, audioSource.time, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				else
				{
					SetPlaybackReferenceFromSeconds(audioSource.time);
					PublishSharedState();
				}
			}
		}

		public void SetPlaybackStateLocal(bool startPlaying)
		{
			if (data.currentSong != null)
			{
				if (ShouldRequestMasterMutation())
				{
					BaseListener.RPC(photonView, "RequestSetPlaybackState", (RpcTarget)2, startPlaying, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				else if ((Object)(object)data.currentSong.GetAudioClip() == (Object)null)
				{
					startPlayBackOnDownload = startPlaying;
					data.isPlaying = false;
					data.pendingPlaybackStart = startPlaying;
					isAwaitingSyncPlayback = PhotonNetwork.IsMasterClient && data.pendingPlaybackStart;
					PublishSharedState();
				}
				else
				{
					SetPlaybackReferenceFromSeconds(GetTrackedPlaybackSeconds());
					data.isPlaying = startPlaying;
					data.pendingPlaybackStart = false;
					PublishSharedState();
				}
			}
		}

		public void JumpPlaybackBySeconds(float seconds)
		{
			if (data.currentSong == null)
			{
				return;
			}
			if (ShouldRequestMasterMutation())
			{
				BaseListener.RPC(photonView, "RequestJumpPlaybackBySeconds", (RpcTarget)2, seconds, PhotonNetwork.LocalPlayer.ActorNumber);
				return;
			}
			float num = Math.Max(0f, GetTrackedPlaybackSeconds() + seconds);
			AudioSource obj = audioSource;
			if ((Object)(object)((obj != null) ? obj.clip : null) != (Object)null)
			{
				num = Math.Min(num, Math.Max(0f, audioSource.clip.length - 0.05f));
			}
			SetPlaybackReferenceFromSeconds(num);
			data.pendingPlaybackStart = false;
			PublishSharedState();
		}

		public void SelectSongIndex(int index)
		{
			if (index < 0 || index >= data.playbackQueue.Count)
			{
				return;
			}
			if (ShouldRequestMasterMutation())
			{
				BaseListener.RPC(photonView, "RequestSelectSongIndex", (RpcTarget)2, index, PhotonNetwork.LocalPlayer.ActorNumber);
				return;
			}
			data.currentSong = data.playbackQueue[index];
			data.isPlaying = false;
			data.pendingPlaybackStart = true;
			isAwaitingSyncPlayback = PhotonNetwork.IsMasterClient;
			startPlayBackOnDownload = true;
			SetPlaybackReferenceFromSeconds(data.currentSong.PeekStartTime(this));
			PublishSharedState();
			if (PhotonNetwork.IsMasterClient)
			{
				downloadHelper.DismissDownloadQueue();
				downloadHelper.DownloadQueue(index);
			}
		}

		public void SetVolumeLocal(float volume)
		{
			if (ShouldRequestMasterMutation())
			{
				BaseListener.RPC(photonView, "RequestSetVolume", (RpcTarget)2, volume, PhotonNetwork.LocalPlayer.ActorNumber);
			}
			else
			{
				data.absVolume = Mathf.Clamp01(volume);
				float volume2 = data.absVolume * data.personalVolumePercentage;
				audioSource.volume = volume2;
				UpdateAudioRangeBasedOnVolume(volume2);
				PublishSharedState();
			}
		}

		public void SetLoopQueueLocal(bool loop)
		{
			if (ShouldRequestMasterMutation())
			{
				BaseListener.RPC(photonView, "RequestSetLoopQueue", (RpcTarget)2, loop, PhotonNetwork.LocalPlayer.ActorNumber);
			}
			else
			{
				data.loopQueue = loop;
				PublishSharedState();
			}
		}

		public void SetUnderglowEnabledLocal(bool enabled)
		{
			data.underglowEnabled = enabled;
			ApplyVisualStateFromData();
			((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
		}

		public void SetVisualizerEnabledLocal(bool enabled)
		{
			data.visualizerEnabled = enabled;
			ApplyVisualStateFromData();
			((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
		}

		public void DismissQueueLocal()
		{
			if (!PhotonNetwork.IsMasterClient && Instance.MasterClientDismissQueue.Value)
			{
				return;
			}
			if (ShouldRequestMasterMutation())
			{
				BaseListener.RPC(photonView, "RequestDismissQueue", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber);
				return;
			}
			StopLocalPlayback(updateSharedFlag: false);
			data.playbackQueue.Clear();
			data.currentSong = null;
			data.isPlaying = false;
			data.pendingPlaybackStart = false;
			data.playbackTime = 0;
			SetPlaybackReferenceFromSeconds(0f);
			PublishSharedState();
			if (PhotonNetwork.IsMasterClient)
			{
				downloadHelper.DismissDownloadQueue();
				downloadHelper.ForceCancelDownload();
			}
			UpdateUIStatus("Ready to play music! Enter a Video URL");
		}

		public void MoveQueueItemLocal(int index, int newIndex)
		{
			if (index >= 0 && index < data.playbackQueue.Count && newIndex >= 0 && newIndex < data.playbackQueue.Count && index != newIndex)
			{
				if (ShouldRequestMasterMutation())
				{
					BaseListener.RPC(photonView, "RequestMoveQueueItem", (RpcTarget)2, index, newIndex, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				else
				{
					AudioEntry item = data.playbackQueue[index];
					data.playbackQueue.RemoveAt(index);
					data.playbackQueue.Insert(newIndex, item);
					PublishSharedState();
				}
			}
		}

		public void RemoveQueueItemLocal(int index)
		{
			if (index < 0 || index >= data.playbackQueue.Count)
			{
				return;
			}
			if (ShouldRequestMasterMutation())
			{
				BaseListener.RPC(photonView, "RequestRemoveQueueItem", (RpcTarget)2, index, PhotonNetwork.LocalPlayer.ActorNumber);
				return;
			}
			AudioEntry audioEntry = data.playbackQueue[index];
			bool flag = audioEntry == data.currentSong;
			data.playbackQueue.RemoveAt(index);
			if (flag)
			{
				if (data.playbackQueue.Count == 0)
				{
					data.currentSong = null;
					data.isPlaying = false;
					data.pendingPlaybackStart = false;
					SetPlaybackReferenceFromSeconds(0f);
				}
				else
				{
					int index2 = Math.Min(index, data.playbackQueue.Count - 1);
					if (index >= data.playbackQueue.Count && LoopQueue)
					{
						index2 = 0;
					}
					data.currentSong = data.playbackQueue[index2];
					data.isPlaying = false;
					data.pendingPlaybackStart = true;
					isAwaitingSyncPlayback = PhotonNetwork.IsMasterClient;
					startPlayBackOnDownload = true;
					SetPlaybackReferenceFromSeconds(data.currentSong.PeekStartTime(this));
				}
			}
			if (PhotonNetwork.IsMasterClient && data.currentSong?.Url != null)
			{
				downloadHelper.DismissDownloadQueue();
				downloadHelper.DownloadQueue(GetCurrentSongIndex());
			}
			PublishSharedState();
		}

		public void FinalizePendingPlaybackStart(bool startPlaying)
		{
			if (data.currentSong != null)
			{
				isAwaitingSyncPlayback = false;
				data.pendingPlaybackStart = false;
				data.isPlaying = startPlaying;
				SetPlaybackReferenceFromSeconds(data.currentSong.UseStartTime(this));
				startPlayBackOnDownload = true;
				PublishSharedState(force: true);
			}
		}

		[PunRPC]
		public void RequestEnqueueSong(string url, int seconds, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				EnqueueSongLocal(url, seconds);
			}
		}

		[PunRPC]
		public void RequestCommitPlaybackSeek(float playbackSeconds, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient && data.currentSong != null)
			{
				SetPlaybackReferenceFromSeconds(playbackSeconds);
				PublishSharedState();
			}
		}

		[PunRPC]
		public void RequestSetPlaybackState(bool startPlaying, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				SetPlaybackStateLocal(startPlaying);
			}
		}

		[PunRPC]
		public void RequestJumpPlaybackBySeconds(float seconds, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				JumpPlaybackBySeconds(seconds);
			}
		}

		[PunRPC]
		public void RequestSelectSongIndex(int index, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				SelectSongIndex(index);
			}
		}

		[PunRPC]
		public void RequestSetVolume(float volume, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				SetVolumeLocal(volume);
			}
		}

		[PunRPC]
		public void RequestSetLoopQueue(bool loop, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				SetLoopQueueLocal(loop);
			}
		}

		[PunRPC]
		public void RequestDismissQueue(int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				DismissQueueLocal();
			}
		}

		[PunRPC]
		public void RequestMoveQueueItem(int index, int newIndex, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				MoveQueueItemLocal(index, newIndex);
			}
		}

		[PunRPC]
		public void RequestRemoveQueueItem(int index, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				RemoveQueueItemLocal(index);
			}
		}

		public void HandleDownloadedCurrentSong()
		{
			if (!((Object)(object)data.currentSong?.GetAudioClip() == (Object)null))
			{
				ApplySharedPlaybackState(songChanged: false);
			}
		}

		public void SetQuality(int level)
		{
			qualityLevel = Mathf.Clamp(level, 0, 4);
			switch (qualityLevel)
			{
			case 0:
				((Behaviour)lowPassFilter).enabled = true;
				lowPassFilter.cutoffFrequency = 1500f;
				break;
			case 1:
				((Behaviour)lowPassFilter).enabled = true;
				lowPassFilter.cutoffFrequency = 3000f;
				break;
			case 2:
				((Behaviour)lowPassFilter).enabled = true;
				lowPassFilter.cutoffFrequency = 4500f;
				break;
			case 3:
				((Behaviour)lowPassFilter).enabled = true;
				lowPassFilter.cutoffFrequency = 6000f;
				break;
			case 4:
				((Behaviour)lowPassFilter).enabled = false;
				break;
			}
		}

		private void UpdateAudioRangeBasedOnVolume(float volume)
		{
			//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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			float num = Mathf.Lerp(maxDistanceBase, maxDistanceBase + maxDistanceAddition, volume);
			audioSource.minDistance = minDistance;
			audioSource.maxDistance = num;
			AnimationCurve val = new AnimationCurve((Keyframe[])(object)new Keyframe[3]
			{
				new Keyframe(0f, 1f),
				new Keyframe(minDistance, 0.9f),
				new Keyframe(num, 0f)
			});
			audioSource.SetCustomCurve((AudioSourceCurveType)0, val);
		}

		public void UpdateUIStatus(string message)
		{
			if (!((Object)(object)this == (Object)null))
			{
				BoomboxUI component = ((Component)this).GetComponent<BoomboxUI>();
				if ((Object)(object)component != (Object)null && component.IsUIVisible())
				{
					component.UpdateStatus(message);
				}
			}
		}

		public override void OnPlayerEnteredRoom(Player newPlayer)
		{
			((MonoBehaviourPunCallbacks)this).OnPlayerEnteredRoom(newPlayer);
			if (PhotonNetwork.IsMasterClient && !Instance.modDisabled)
			{
				PhotonView obj = ((MonoBehaviourPun)Instance.baseListener).photonView;
				if (obj != null)
				{
					obj.RPC("ModFeedbackCheck", newPlayer, new object[2]
					{
						"1.3.2",
						PhotonNetwork.LocalPlayer.ActorNumber
					});
				}
			}
		}

		private void OnDisable()
		{
			PersistentData.RemoveBoomboxViewInitialized(photonView.ViewID);
			data.playbackTime = (int)Math.Round(audioSource.time);
		}

		private void OnDestroy()
		{
			Instance.data.GetAllBoomboxes().Remove(this);
			Object.Destroy((Object)(object)((Component)this).GetComponent<BoomboxUI>());
			Object.Destroy((Object)(object)visualizer);
			Object.Destroy((Object)(object)lowPassFilter);
			Object.Destroy((Object)(object)audioSource);
			Object.Destroy((Object)(object)downloadHelper);
			Object.Destroy((Object)(object)((Component)this).gameObject.GetComponent<BoomboxController>());
			photonView.RefreshRpcMonoBehaviourCache();
		}

		public void ResetData()
		{
			if (PhotonNetwork.IsMasterClient)
			{
				Logger.LogInfo((object)$"Resetting Boombox {photonView.ViewID}");
				isAwaitingSyncPlayback = false;
				startPlayBackOnDownload = true;
				data.pendingPlaybackStart = false;
				UpdateUIStatus("Ready to play music! Enter a Video URL");
				CleanupCurrentPlayback();
				downloadHelper.DismissDownloadQueue();
				List<BoomboxData> boomboxData = Instance.data.GetBoomboxData();
				int index = boomboxData.Count;
				if (boomboxData.Contains(data))
				{
					index = boomboxData.IndexOf(data);
					boomboxData.Remove(data);
				}
				data = new BoomboxData();
				ApplyVisualStateFromData();
				if (Instance.RestoreBoomboxes.Value)
				{
					boomboxData.Insert(index, data);
				}
				PublishSharedState(force: true);
			}
		}
	}
	public class BoomboxController : MonoBehaviourPun
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		private PhysGrabCart cart;

		private BoomboxUI boomboxUI;

		private Boombox boombox;

		private int currentControllerId = -1;

		private static ManualLogSource Logger => Instance.logger;

		private void Awake()
		{
			cart = ((Component)this).GetComponent<PhysGrabCart>();
			boombox = ((Component)this).GetComponent<Boombox>();
			if ((Object)(object)cart == (Object)null)
			{
				Logger.LogError((object)"BoomboxController: PhysGrabCart component not found!");
			}
			else if ((Object)(object)boombox == (Object)null)
			{
				Logger.LogError((object)"BoomboxController: Boombox component not found!");
			}
		}

		private bool IsLocalPlayerGrabbingCart()
		{
			return PlayerGrabbingTracker.IsLocalPlayerGrabbingCart(((Component)this).gameObject);
		}

		public void RequestBoomboxControl()
		{
			if (IsLocalPlayerGrabbingCart())
			{
				int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
				BaseListener.RPC(((MonoBehaviourPun)this).photonView, "RequestControl", (RpcTarget)2, actorNumber);
			}
		}

		[PunRPC]
		private void RequestControl(int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				bool flag = true;
				if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber)
				{
					flag = PlayerGrabbingTracker.IsLocalPlayerGrabbingCart(((Component)this).gameObject);
				}
				if ((currentControllerId == -1 || !Instance.baseListener.GetAllModUsers().Contains(requesterId)) && flag)
				{
					BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetController", (RpcTarget)0, requesterId);
				}
			}
		}

		[PunRPC]
		private void SetController(int controllerId)
		{
			try
			{
				currentControllerId = controllerId;
				if (controllerId == PhotonNetwork.LocalPlayer.ActorNumber)
				{
					EnsureBoomboxUIExists();
					if ((Object)(object)boomboxUI != (Object)null)
					{
						boomboxUI.ShowUI();
					}
					else
					{
						Logger.LogError((object)"Failed to create BoomboxUI component");
					}
				}
				else if ((Object)(object)boomboxUI != (Object)null && boomboxUI.IsUIVisible())
				{
					boomboxUI.HideUI();
				}
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Error in SetController: " + ex.Message + "\n" + ex.StackTrace));
			}
		}

		private void EnsureBoomboxUIExists()
		{
			if ((Object)(object)boomboxUI == (Object)null)
			{
				boomboxUI = ((Component)this).gameObject.GetComponent<BoomboxUI>();
				if ((Object)(object)boomboxUI == (Object)null)
				{
					boomboxUI = ((Component)this).gameObject.AddComponent<BoomboxUI>();
				}
			}
		}

		public void ReleaseControl()
		{
			if (currentControllerId == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				BaseListener.RPC(((MonoBehaviourPun)this).photonView, "RequestRelease", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber);
			}
		}

		[PunRPC]
		private void RequestRelease(int releaserId)
		{
			if (PhotonNetwork.IsMasterClient && currentControllerId == releaserId)
			{
				BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetController", (RpcTarget)0, -1);
			}
		}

		private void OnPlayerReleasedCart(int playerActorNumber)
		{
			if (playerActorNumber == currentControllerId && PhotonNetwork.IsMasterClient)
			{
				BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetController", (RpcTarget)0, -1);
			}
		}

		public void LocalPlayerReleasedCart()
		{
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			if (currentControllerId == actorNumber)
			{
				ReleaseControl();
			}
		}
	}
	public class BoomboxUI : MonoBehaviourPun
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		public PhotonView photonView;

		public static bool anyUISHown = false;

		public bool showUI = false;

		private string urlInput = "";

		private bool isTimeSliderBeingDragged = false;

		private int songIndexForTime = -2;

		private float songTimePerc = 0f;

		private float lastSentSongTimePerc = -1f;

		private float lastSentVolume = 0.3f;

		private bool isVolumeSliderBeingDragged = false;

		private bool isIndividualVolumeBeingDragged = false;

		private int qualityLevel = 3;

		private string[] qualityLabels = new string[5] { "REALLY Low (You Freak)", "Low", "Medium-Low", "Medium-High", "High" };

		private bool isQualitySliderBeingDragged = false;

		private int lastSentQualityLevel = 3;

		private Rect windowRect;

		private Boombox boombox;

		private BoomboxController controller;

		private VisualEffects visualEffects;

		private Visualizer visualizer;

		private GUIStyle windowStyle;

		private GUIStyle headerStyle;

		private GUIStyle buttonStyle;

		private GUIStyle smallButtonStyle;

		private GUIStyle textFieldStyle;

		private GUIStyle labelStyle;

		private GUIStyle sliderStyle;

		private GUIStyle statusStyle;

		private GUIStyle scrollViewStyle;

		private GUIStyle queueHeaderStyle;

		private GUIStyle queueEntryStyle;

		private GUIStyle currentSongStyle;

		private Texture2D backgroundTexture;

		private Texture2D buttonTexture;

		private Texture2D sliderBackgroundTexture;

		private Texture2D sliderThumbTexture;

		private Texture2D textFieldBackgroundTexture;

		private Vector2 urlScrollPosition = Vector2.zero;

		private float textFieldVisibleWidth = 350f;

		private string errorMessage = "";

		private float errorMessageTime = 0f;

		public string statusMessage = "";

		private CursorLockMode previousLockMode;

		private bool previousCursorVisible;

		private bool stylesInitialized = false;

		private Vector2 scrollPosition = Vector2.zero;

		private Vector2 queueScrollPosition = Vector2.zero;

		private const float refreshHoldTime = 5f;

		private const int maxRefreshSymbols = 5;

		private float? refreshObjectStart = null;

		private bool refreshObjectSent = false;

		private string lastUrl = null;

		private static ManualLogSource Logger => Instance.logger;

		private void Awake()
		{
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				boombox = ((Component)this).GetComponent<Boombox>();
				if ((Object)(object)boombox != (Object)null)
				{
					photonView = boombox.photonView;
				}
				else
				{
					Logger.LogError((object)"BoomboxUI: Failed to find Boombox component");
					photonView = ((Component)this).GetComponent<PhotonView>();
				}
				controller = ((Component)this).GetComponent<BoomboxController>();
				visualEffects = ((Component)this).GetComponent<VisualEffects>();
				if ((Object)(object)visualEffects == (Object)null)
				{
					visualEffects = ((Component)this).gameObject.AddComponent<VisualEffects>();
				}
				visualizer = ((Component)this).GetComponent<Visualizer>();
				boombox?.ApplyVisualStateFromData();
				RefreshVisualComponentRefs();
				if ((Object)(object)photonView == (Object)null)
				{
					Logger.LogError((object)"BoomboxUI: Failed to find PhotonView component");
				}
				windowRect = new Rect((float)(Screen.width / 2 - 400), (float)(Screen.height / 2 - 175), 800f, 550f);
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Error in BoomboxUI.Awake: " + ex.Message + "\n" + ex.StackTrace));
			}
		}

		private void Update()
		{
			if (Time.time > errorMessageTime && !string.IsNullOrEmpty(errorMessage))
			{
				errorMessage = "";
			}
			if (showUI && Keyboard.current != null && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame)
			{
				if ((Object)(object)controller != (Object)null)
				{
					controller.ReleaseControl();
				}
				else
				{
					HideUI();
				}
			}
			if (isTimeSliderBeingDragged && ((Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame) || songIndexForTime != boombox.GetCurrentSongIndex()))
			{
				isTimeSliderBeingDragged = false;
				SendTimeUpdate();
				songIndexForTime = -2;
				songTimePerc = 0f;
				lastSentSongTimePerc = -1f;
			}
			if (isVolumeSliderBeingDragged && Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame)
			{
				isVolumeSliderBeingDragged = false;
				SendVolumeUpdate();
			}
			if (isIndividualVolumeBeingDragged && Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame)
			{
				isIndividualVolumeBeingDragged = false;
			}
			if (isQualitySliderBeingDragged && Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame)
			{
				isQualitySliderBeingDragged = false;
				SendQualityUpdate();
			}
			if (refreshObjectStart.HasValue && Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame)
			{
				refreshObjectStart = null;
				refreshObjectSent = false;
			}
		}

		public void ShowUI()
		{
			//IL_0051: 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)
			if (!showUI)
			{
				if ((Object)(object)boombox == (Object)null || (Object)(object)photonView == (Object)null)
				{
					Logger.LogError((object)"Cannot show UI - boombox or photonView is null");
					return;
				}
				anyUISHown = true;
				showUI = true;
				previousLockMode = Cursor.lockState;
				previousCursorVisible = Cursor.visible;
				Cursor.visible = true;
				Cursor.lockState = (CursorLockMode)0;
				UpdateDataFromBoomBox();
				UpdateStatusFromBoombox();
			}
		}

		public void UpdateDataFromBoomBox()
		{
			if ((Object)(object)boombox != (Object)null)
			{
				lastSentVolume = boombox.data.absVolume;
				qualityLevel = Boombox.qualityLevel;
				lastSentQualityLevel = qualityLevel;
				RefreshVisualComponentRefs();
			}
		}

		private void RefreshVisualComponentRefs()
		{
			visualEffects = ((Component)this).GetComponent<VisualEffects>();
			visualizer = ((Component)this).GetComponent<Visualizer>();
		}

		public void UpdateStatusFromBoombox()
		{
			if ((Object)(object)boombox != (Object)null)
			{
				if (YoutubeDL.IsUpdatingResources)
				{
					statusMessage = YoutubeDL.ResourceUpdateStatus;
				}
				else if (boombox.downloadHelper.IsProcessingQueue() && boombox.data.currentSong != null && (Object)(object)boombox.data.currentSong.GetAudioClip() == (Object)null)
				{
					statusMessage = "Downloading audio from " + boombox.downloadHelper.GetCurrentDownloadUrl() + "...";
				}
				else if (boombox.data.currentSong != null && boombox.data.pendingPlaybackStart)
				{
					statusMessage = "Loading: " + boombox.data.currentSong.Title;
				}
				else if (boombox.data.currentSong != null && boombox.data.isPlaying)
				{
					statusMessage = "Now playing: " + boombox.data.currentSong.Title;
				}
				else if (!string.IsNullOrEmpty(boombox.data.currentSong?.Url))
				{
					statusMessage = "Ready to play: " + boombox.data.currentSong.Title;
				}
				else
				{
					statusMessage = "Ready to play music! Enter a Video URL";
				}
			}
		}

		private void SendTimeUpdate()
		{
			if (songTimePerc == lastSentSongTimePerc)
			{
				return;
			}
			lastSentSongTimePerc = songTimePerc;
			if (boombox?.GetCurrentSongIndex() == songIndexForTime && songIndexForTime != -1)
			{
				Boombox obj = boombox;
				object obj2;
				if (obj == null)
				{
					obj2 = null;
				}
				else
				{
					AudioSource audioSource = obj.audioSource;
					obj2 = ((audioSource != null) ? audioSource.clip : null);
				}
				if ((Object)obj2 != (Object)null && boombox.audioSource.clip.length > 0f)
				{
					float time = Math.Max(0f, Math.Min(songTimePerc * boombox.audioSource.clip.length, boombox.audioSource.clip.length - 0.05f));
					boombox.audioSource.time = time;
					boombox.CommitPlaybackSeek();
					return;
				}
			}
			if (songIndexForTime != boombox.GetCurrentSongIndex())
			{
				UpdateDataFromBoomBox();
			}
		}

		private void SendVolumeUpdate()
		{
			if (boombox.data.absVolume != lastSentVolume)
			{
				lastSentVolume = boombox.data.absVolume;
				if ((Object)(object)boombox.audioSource != (Object)null)
				{
					float volume = boombox.data.absVolume * boombox.data.personalVolumePercentage;
					boombox.audioSource.volume = volume;
				}
				boombox.SetVolumeLocal(boombox.data.absVolume);
			}
		}

		private void SendQualityUpdate()
		{
			if (qualityLevel != lastSentQualityLevel)
			{
				lastSentQualityLevel = qualityLevel;
				if ((Object)(object)boombox != (Object)null)
				{
					boombox.SetQuality(qualityLevel);
				}
			}
		}

		public void HideUI()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (showUI)
			{
				anyUISHown = false;
				showUI = false;
				Cursor.lockState = previousLockMode;
				Cursor.visible = previousCursorVisible;
			}
		}

		public bool IsUIVisible()
		{
			return showUI;
		}

		public void UpdateStatus(string message)
		{
			statusMessage = message;
		}

		private Texture2D CreateColorTexture(Color color)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(1, 1);
			val.SetPixel(0, 0, color);
			val.Apply();
			return val;
		}

		private void InitializeStyles()
		{
			//IL_0026: 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)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Expected O, but got Unknown
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Expected O, but got Unknown
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Expected O, but got Unknown
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Expected O, but got Unknown
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Expected O, but got Unknown
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Expected O, but got Unknown
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Expected O, but got Unknown
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Expected O, but got Unknown
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Expected O, but got Unknown
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a7: Expected O, but got Unknown
			//IL_03b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bc: Expected O, but got Unknown
			//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d2: Expected O, but got Unknown
			//IL_03dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Expected O, but got Unknown
			//IL_0417: Unknown result type (might be due to invalid IL or missing references)
			//IL_0421: Expected O, but got Unknown
			//IL_042c: Unknown result type (might be due to invalid IL or missing references)
			//IL_046a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Expected O, but got Unknown
			//IL_0492: Unknown result type (might be due to invalid IL or missing references)
			//IL_049c: Expected O, but got Unknown
			//IL_04c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cc: Expected O, but got Unknown
			//IL_04d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04de: Expected O, but got Unknown
			//IL_04fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_052e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0552: Unknown result type (might be due to invalid IL or missing references)
			//IL_055c: Expected O, but got Unknown
			//IL_057c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0597: Unknown result type (might be due to invalid IL or missing references)
			if (!stylesInitialized)
			{
				backgroundTexture = CreateColorTexture(new Color(0.1f, 0.1f, 0.1f, 0.9f));
				buttonTexture = CreateColorTexture(new Color(0.2f, 0.2f, 0.3f, 1f));
				sliderBackgroundTexture = CreateColorTexture(new Color(0.15f, 0.15f, 0.2f, 1f));
				sliderThumbTexture = CreateColorTexture(new Color(0.7f, 0.7f, 0.8f, 1f));
				textFieldBackgroundTexture = CreateColorTexture(new Color(0.15f, 0.17f, 0.2f, 1f));
				windowStyle = new GUIStyle(GUI.skin.window);
				windowStyle.normal.background = backgroundTexture;
				windowStyle.onNormal.background = backgroundTexture;
				windowStyle.border = new RectOffset(10, 10, 10, 10);
				windowStyle.padding = new RectOffset(15, 15, 20, 15);
				headerStyle = new GUIStyle(GUI.skin.label);
				headerStyle.fontSize = 18;
				headerStyle.fontStyle = (FontStyle)1;
				headerStyle.normal.textColor = Color.white;
				headerStyle.alignment = (TextAnchor)4;
				headerStyle.margin = new RectOffset(0, 0, 10, 20);
				buttonStyle = new GUIStyle(GUI.skin.button);
				buttonStyle.normal.background = buttonTexture;
				buttonStyle.hover.background = CreateColorTexture(new Color(0.3f, 0.3f, 0.4f, 1f));
				buttonStyle.active.background = CreateColorTexture(new Color(0.4f, 0.4f, 0.5f, 1f));
				buttonStyle.normal.textColor = Color.white;
				buttonStyle.hover.textColor = Color.white;
				buttonStyle.active.textColor = Color.white;
				buttonStyle.fontSize = 14;
				buttonStyle.padding = new RectOffset(15, 15, 8, 8);
				buttonStyle.margin = new RectOffset(5, 5, 5, 5);
				buttonStyle.alignment = (TextAnchor)4;
				smallButtonStyle = new GUIStyle(buttonStyle);
				smallButtonStyle.padding = new RectOffset(8, 8, 4, 4);
				smallButtonStyle.fontSize = 12;
				textFieldStyle = new GUIStyle(GUI.skin.textField);
				textFieldStyle.normal.background = textFieldBackgroundTexture;
				textFieldStyle.normal.textColor = new Color(1f, 1f, 1f);
				textFieldStyle.fontSize = 14;
				textFieldStyle.padding = new RectOffset(10, 10, 8, 8);
				scrollViewStyle = new GUIStyle(GUI.skin.scrollView);
				scrollViewStyle.normal.background = textFieldBackgroundTexture;
				scrollViewStyle.border = new RectOffset(2, 2, 2, 2);
				scrollViewStyle.padding = new RectOffset(0, 0, 0, 0);
				labelStyle = new GUIStyle(GUI.skin.label);
				labelStyle.normal.textColor = Color.white;
				labelStyle.fontSize = 14;
				labelStyle.margin = new RectOffset(0, 0, 10, 5);
				statusStyle = new GUIStyle(GUI.skin.label);
				statusStyle.normal.textColor = Color.cyan;
				statusStyle.fontSize = 14;
				statusStyle.wordWrap = true;
				statusStyle.alignment = (TextAnchor)4;
				sliderStyle = new GUIStyle(GUI.skin.horizontalSlider);
				sliderStyle.normal.background = sliderBackgroundTexture;
				queueHeaderStyle = new GUIStyle(headerStyle);
				queueHeaderStyle.fontSize = 16;
				queueHeaderStyle.alignment = (TextAnchor)3;
				queueHeaderStyle.margin = new RectOffset(5, 0, 10, 5);
				queueEntryStyle = new GUIStyle(textFieldStyle);
				queueEntryStyle.normal.background = CreateColorTexture(new Color(0.1f, 0.1f, 0.1f, 0.7f));
				queueEntryStyle.hover.background = CreateColorTexture(new Color(0.2f, 0.2f, 0.2f, 0.8f));
				queueEntryStyle.alignment = (TextAnchor)3;
				currentSongStyle = new GUIStyle(queueEntryStyle);
				currentSongStyle.normal.background = CreateColorTexture(new Color(0.1f, 0.3f, 0.1f, 0.9f));
				currentSongStyle.normal.textColor = Color.yellow;
				currentSongStyle.hover.background = currentSongStyle.normal.background;
				stylesInitialized = true;
			}
		}

		private void OnGUI()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (showUI)
			{
				if (!stylesInitialized)
				{
					InitializeStyles();
				}
				windowRect = GUILayout.Window(0, windowRect, new WindowFunction(DrawUI), "Boombox Controller", windowStyle, Array.Empty<GUILayoutOption>());
			}
		}

		private void DrawUI(int windowID)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (boombox?.data == null)
			{
				return;
			}
			object obj;
			if (!Instance.baseListener.audioMuted)
			{
				obj = "";
			}
			else
			{
				Key value = Instance.GlobalMuteKey.Value;
				obj = " - MUTED(" + ((object)(Key)(ref value)).ToString() + ")";
			}
			GUILayout.Label("Control The Boombox In The Cart" + (string?)obj, headerStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(400f) });
			DrawMainPanel(boombox);
			GUILayout.EndVertical();
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(420f),
				GUILayout.ExpandHeight(true)
			});
			DrawQueue(boombox);
			GUILayout.EndVertical();
			GUILayout.EndHorizontal();
			GUILayout.FlexibleSpace();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (GUILayout.Button("Close", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(300f),
				GUILayout.Height(36f)
			}))
			{
				if ((Object)(object)controller != (Object)null)
				{
					controller.ReleaseControl();
				}
				else
				{
					HideUI();
				}
			}
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			GUI.DragWindow();
		}

		private void DrawMainPanel(Boombox boombox)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: 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_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_063d: Unknown result type (might be due to invalid IL or missing references)
			//IL_065f: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_080a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0811: Unknown result type (might be due to invalid IL or missing references)
			//IL_0816: Unknown result type (might be due to invalid IL or missing references)
			//IL_081f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0906: Unknown result type (might be due to invalid IL or missing references)
			//IL_090d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0912: Unknown result type (might be due to invalid IL or missing references)
			//IL_091b: Unknown result type (might be due to invalid IL or missing references)
			scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			GUILayout.Space(10f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Enter Video URL:", labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			if (GUILayout.Button("Clear", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }))
			{
				urlInput = "";
				GUI.FocusControl((string)null);
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			urlScrollPosition = GUILayout.BeginScrollView(urlScrollPosition, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(60f) });
			urlInput = GUILayout.TextField(urlInput, textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) });
			urlInput = Regex.Replace(urlInput, "\\s+", "");
			GUILayout.EndScrollView();
			GUILayout.EndHorizontal();
			float num = 0f;
			float num2 = 0f;
			float num3 = 0f;
			string text = "??";
			int num4;
			if ((Object)(object)boombox != (Object)null)
			{
				AudioSource audioSource = boombox.audioSource;
				num4 = (((Object)(object)((audioSource != null) ? audioSource.clip : null) != (Object)null) ? 1 : 0);
			}
			else
			{
				num4 = 0;
			}
			bool flag = (byte)num4 != 0;
			if (flag)
			{
				num = boombox.audioSource.time;
				num2 = boombox.audioSource.clip.length;
				if (num2 > 0f)
				{
					num3 = num / num2;
					text = PrintTime(num2);
				}
				else
				{
					flag = false;
				}
			}
			GUILayout.Label(PrintTime(num) + " / " + text, labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			Rect lastRect;
			if (flag && (int)Event.current.type == 0)
			{
				lastRect = GUILayoutUtility.GetLastRect();
				if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition))
				{
					isTimeSliderBeingDragged = true;
				}
			}
			float num5 = GUILayout.HorizontalSlider(num3, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			int currentSongIndex = boombox.GetCurrentSongIndex();
			if (flag && currentSongIndex != -1 && num5 != num3)
			{
				if (!isTimeSliderBeingDragged)
				{
					isTimeSliderBeingDragged = true;
				}
				if (songIndexForTime == -2)
				{
					songIndexForTime = currentSongIndex;
				}
				if (songIndexForTime == currentSongIndex)
				{
					float time = Math.Max(0f, Math.Min(num5 * num2, boombox.audioSource.clip.length - 0.05f));
					boombox.audioSource.time = time;
					songTimePerc = num5;
				}
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("<<", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(40f),
				GUILayout.Height(40f)
			}) && (Object)(object)boombox != (Object)null && boombox.data.currentSong != null)
			{
				boombox.JumpPlaybackBySeconds(-10f);
			}
			string text2 = ((boombox?.data != null && boombox.data.playbackQueue.Count > 0) ? "+ ENQUEUE" : "▶ PLAY");
			if (GUILayout.Button(text2, buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }))
			{
				var (text3, seconds) = IsValidVideoUrl(urlInput);
				if (string.IsNullOrEmpty(text3))
				{
					ShowErrorMessage("Invalid Video URL!");
				}
				else if (lastUrl != text3)
				{
					lastUrl = text3;
					boombox.EnqueueSongLocal(text3, seconds);
					GUI.FocusControl((string)null);
				}
			}
			string text4 = ((boombox.data.currentSong == null) ? "..." : (boombox.data.isPlaying ? "▌▌ PAUSE" : "▶ RESUME"));
			if (GUILayout.Button(text4, buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }) && boombox.data.currentSong != null)
			{
				boombox.SetPlaybackStateLocal(!boombox.data.isPlaying);
			}
			if (GUILayout.Button(">>", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(40f),
				GUILayout.Height(40f)
			}) && (Object)(object)boombox != (Object)null && boombox.data.currentSong != null)
			{
				boombox.JumpPlaybackBySeconds(10f);
			}
			GUILayout.EndHorizontal();
			if ((Object)(object)boombox != (Object)null && boombox.downloadHelper.IsProcessingQueue() && (Object)(object)boombox.data.currentSong?.GetAudioClip() == (Object)null && boombox.downloadHelper.GetCurrentDownloadUrl() != null)
			{
				GUILayout.Space(10f);
				GUILayout.Label("Download in progress...", statusStyle, Array.Empty<GUILayoutOption>());
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				GUILayout.FlexibleSpace();
				if (GUILayout.Button("Force Cancel Download", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
				{
					GUILayout.Width(200f),
					GUILayout.Height(30f)
				}))
				{
					boombox.downloadHelper.ForceCancelDownload();
				}
				GUILayout.FlexibleSpace();
				GUILayout.EndHorizontal();
			}
			GUILayout.Space(15f);
			if (!string.IsNullOrEmpty(statusMessage))
			{
				GUILayout.Label(statusMessage, statusStyle, Array.Empty<GUILayoutOption>());
				GUILayout.Space(5f);
			}
			if (!string.IsNullOrEmpty(errorMessage))
			{
				GUI.color = Color.red;
				GUILayout.Label(errorMessage, labelStyle, Array.Empty<GUILayoutOption>());
				GUI.color = Color.white;
				GUILayout.Space(5f);
			}
			GUILayout.Space(15f);
			float num6 = boombox.data.absVolume * 100f;
			GUILayout.Label($"Volume: {Mathf.Round(num6)}%", labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if ((int)Event.current.type == 0)
			{
				lastRect = GUILayoutUtility.GetLastRect();
				if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition))
				{
					isVolumeSliderBeingDragged = true;
				}
			}
			float num7 = GUILayout.HorizontalSlider(boombox.data.absVolume, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			if (num7 != boombox.data.absVolume)
			{
				if (!isVolumeSliderBeingDragged)
				{
					isVolumeSliderBeingDragged = true;
				}
				if ((Object)(object)boombox != (Object)null && (Object)(object)boombox.audioSource != (Object)null)
				{
					float volume = boombox.data.absVolume * boombox.data.personalVolumePercentage;
					boombox.audioSource.volume = volume;
				}
				boombox.data.absVolume = num7;
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(15f);
			GUILayout.Label($"Personal Volume Multiplier: {Mathf.Round(boombox.data.personalVolumePercentage * 100.001f)}%", labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if ((int)Event.current.type == 0)
			{
				lastRect = GUILayoutUtility.GetLastRect();
				if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition))
				{
					isIndividualVolumeBeingDragged = true;
				}
			}
			float num8 = GUILayout.HorizontalSlider(boombox.data.personalVolumePercentage, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			if (num8 != boombox.data.personalVolumePercentage)
			{
				isIndividualVolumeBeingDragged = true;
				boombox.data.personalVolumePercentage = num8;
				boombox.audioSource.volume = boombox.data.absVolume * boombox.data.personalVolumePercentage;
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(15f);
			GUILayout.Label("Audio Quality: " + qualityLabels[qualityLevel], labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if ((int)Event.current.type == 0)
			{
				lastRect = GUILayoutUtility.GetLastRect();
				if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition))
				{
					isQualitySliderBeingDragged = true;
				}
			}
			float num9 = GUILayout.HorizontalSlider((float)qualityLevel, 0f, 4f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			int num10 = Mathf.RoundToInt(num9);
			if (num10 != qualityLevel && !isQualitySliderBeingDragged)
			{
				isQualitySliderBeingDragged = true;
			}
			qualityLevel = num10;
			if ((Object)(object)boombox != (Object)null)
			{
				boombox.SetQuality(qualityLevel);
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(10f);
			bool applyQualityToDownloads = Boombox.ApplyQualityToDownloads;
			bool flag2 = GUILayout.Toggle(applyQualityToDownloads, "Apply Quality Setting to Downloads", Array.Empty<GUILayoutOption>());
			if (flag2 != applyQualityToDownloads)
			{
				Boombox.ApplyQualityToDownloads = flag2;
			}
			GUILayout.Space(10f);
			bool monstersCanHearMusic = Boombox.MonstersCanHearMusic;
			bool flag3 = GUILayout.Toggle(monstersCanHearMusic, "Monsters can hear audio", Array.Empty<GUILayoutOption>());
			if (flag3 != monstersCanHearMusic)
			{
				Boombox.MonstersCanHearMusic = flag3;
			}
			GUILayout.Space(10f);
			bool loopQueue = boombox.LoopQueue;
			bool flag4 = GUILayout.Toggle(loopQueue, "Loop queue", Array.Empty<GUILayoutOption>());
			if (flag4 != loopQueue && PhotonNetwork.IsMasterClient)
			{
				boombox.SetLoopQueueLocal(flag4);
			}
			GUILayout.Space(10f);
			bool underglowEnabled = boombox.data.underglowEnabled;
			bool flag5 = GUILayout.Toggle(underglowEnabled, "RGB Underglow enabled", Array.Empty<GUILayoutOption>());
			if (flag5 != underglowEnabled)
			{
				boombox.SetUnderglowEnabledLocal(flag5);
				UpdateDataFromBoomBox();
			}
			GUILayout.Space(10f);
			bool visualizerEnabled = boombox.data.visualizerEnabled;
			bool flag6 = GUILayout.Toggle(visualizerEnabled, "Audio Visualizer enabled", Array.Empty<GUILayoutOption>());
			if (flag6 != visualizerEnabled)
			{
				boombox.SetVisualizerEnabledLocal(flag6);
				UpdateDataFromBoomBox();
			}
			GUILayout.EndScrollView();
		}

		private void DrawQueue(Boombox boombox)
		{
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(" Playback Queue", queueHeaderStyle, Array.Empty<GUILayoutOption>());
			if (PhotonNetwork.IsMasterClient)
			{
				float num = Time.time - (refreshObjectStart ?? Time.time);
				int num2 = 1 + (int)Mathf.Floor(num / 1f);
				string text = ((num > 0f && num <= 5f) ? (new string('!', num2) + new string('.', Math.Max(0, 5 - num2))) : "RESET");
				if (GUILayout.RepeatButton(text, smallButtonStyle, Array.Empty<GUILayoutOption>()))
				{
					if (!refreshObjectStart.HasValue)
					{
						refreshObjectStart = Time.time;
					}
					else if (!refreshObjectSent && num >= 5f)
					{
						boombox.ResetData();
						lastUrl = null;
						refreshObjectSent = true;
					}
				}
			}
			if (GUILayout.Button("Dismiss Queue", smallButtonStyle, Array.Empty<GUILayoutOption>()))
			{
				boombox.DismissQueueLocal();
				lastUrl = null;
			}
			GUILayout.EndHorizontal();
			queueScrollPosition = GUILayout.BeginScrollView(queueScrollPosition, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			List<Boombox.AudioEntry> playbackQueue = boombox.data.playbackQueue;
			int currentSongIndex = boombox.GetCurrentSongIndex();
			if (playbackQueue.Count == 0)
			{
				GUILayout.Label(" Queue is empty and no song is playing.", labelStyle, Array.Empty<GUILayoutOption>());
			}
			for (int i = 0; i < playbackQueue.Count; i++)
			{
				Boombox.AudioEntry audioEntry = playbackQueue[i];
				bool flag = i == currentSongIndex;
				GUIStyle val = (flag ? currentSongStyle : queueEntryStyle);
				string text2 = (flag ? "▶ " : $"{i - ((currentSongIndex != -1) ? currentSongIndex : 0)}. ");
				string text3 = ClipText(text2 + audioEntry.Title, 280f, val);
				GUILayout.BeginHorizontal(val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) });
				if (GUILayout.Button(text3, val, (GUILayoutOption[])(object)new GUILayoutOption[2]
				{
					GUILayout.ExpandWidth(true),
					GUILayout.Height(32f)
				}) && !flag)
				{
					boombox.SelectSongIndex(i);
				}
				if (!flag)
				{
					if (i > 0)
					{
						if (GUILayout.Button("▲", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
						{
							GUILayout.Width(25f),
							GUILayout.Height(28f)
						}))
						{
							boombox.MoveQueueItemLocal(i, i - 1);
						}
					}
					else
					{
						GUILayout.Space(30f);
					}
					if (i + 1 < playbackQueue.Count)
					{
						if (GUILayout.Button("▼", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
						{
							GUILayout.Width(25f),
							GUILayout.Height(28f)
						}))
						{
							boombox.MoveQueueItemLocal(i, i + 1);
						}
					}
					else
					{
						GUILayout.Space(30f);
					}
					if (GUILayout.Button("X", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
					{
						GUILayout.Width(25f),
						GUILayout.Height(28f)
					}))
					{
						boombox.RemoveQueueItemLocal(i);
					}
				}
				GUILayout.EndHorizontal();
			}
			GUILayout.EndScrollView();
		}

		private string ClipText(string text, float maxWidth, GUIStyle style)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_0008: 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)
			//IL_0042: 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_0040: Expected O, but got Unknown
			//IL_003b: 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)
			Vector2 val = style.CalcSize(new GUIContent(text));
			string text2 = text;
			int length = text.Length;
			while (val.x > maxWidth)
			{
				text2 = text2.Substring(0, text2.Length - 4) + "...";
				val = style.CalcSize(new GUIContent(text2));
			}
			return text2;
		}

		private string PrintTime(float time)
		{
			return $"{(int)Math.Floor(time / 60f)}:{(int)(time % 60f)}";
		}

		private (string cleanedUrl, int seconds) IsValidVideoUrl(string url)
		{
			return DownloadHelper.IsValidVideoUrl(url);
		}

		private void ShowErrorMessage(string message)
		{
			errorMessage = message;
			errorMessageTime = Time.time + 3f;
		}

		private void OnDestroy()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (showUI)
			{
				anyUISHown = false;
				showUI = false;
				Cursor.lockState = previousLockMode;
				Cursor.visible = previousCursorVisible;
			}
			if ((Object)(object)backgroundTexture != (Object)null)
			{
				Object.Destroy((Object)(object)backgroundTexture);
			}
			if ((Object)(object)buttonTexture != (Object)null)
			{
				Object.Destroy((Object)(object)buttonTexture);
			}
			if ((Object)(object)sliderBackgroundTexture != (Object)null)
			{
				Object.Destroy((Object)(object)sliderBackgroundTexture);
			}
			if ((Object)(object)sliderThumbTexture != (Object)null)
			{
				Object.Destroy((Object)(object)sliderThumbTexture);
			}
			if ((Object)(object)textFieldBackgroundTexture != (Object)null)
			{
				Object.Destroy((Object)(object)textFieldBackgroundTexture);
			}
		}
	}
	public class DownloadHelper : MonoBehaviourPunCallbacks
	{
		[CompilerGenerated]
		private sealed class <>c__DisplayClass33_0
		{
			public string url;

			internal bool <MasterClientInitiateSync>b__0(Boombox.AudioEntry entry)
			{
				return entry.Url == url;
			}
		}

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

			private object <>2__current;

			public string requestId;

			public string url;

			public DownloadHelper <>4__this;

			private List<int>.Enumerator <>s__1;

			private int <player>5__2;

			private string <timeoutMessage>5__3;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>s__1 = default(List<int>.Enumerator);
				<timeoutMessage>5__3 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(40f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (<>4__this.currentRequestId == requestId && <>4__this.isProcessingQueue)
					{
						Logger.LogWarning((object)("Download timeout for url: " + url));
						Logger.LogInfo((object)"Master client initiating timeout recovery");
						<>4__this.isTimeoutRecovery = true;
						<>s__1 = Instance.baseListener.GetAllModUsers().GetEnumerator();
						try
						{
							while (<>s__1.MoveNext())
							{
								<player>5__2 = <>s__1.Current;
								if (!downloadsReady[url].Contains(<player>5__2))
								{
									if (!downloadErrors.ContainsKey(url))
									{
										downloadErrors[url] = new HashSet<int>();
									}
									downloadErrors[url].Add(<player>5__2);
									Logger.LogWarning((object)$"Player {<player>5__2} timed out during download");
								}
							}
						}
						finally
						{
							((IDisposable)<>s__1).Dispose();
						}
						<>s__1 = default(List<int>.Enumerator);
						if (downloadsReady.ContainsKey(url) && downloadsReady[url].Count > 0)
						{
							<timeoutMessage>5__3 = $"Some players timed out. Continuing playback for {downloadsReady[url].Count} players.";
							BaseListener.RPC(((MonoBehaviourPun)<>4__this).photonView, "NotifyPlayersOfErrors", (RpcTarget)0, <timeoutMessage>5__3);
							if (<>4__this.boomboxParent.isAwaitingSyncPlayback && <>4__this.boomboxParent.data.currentSong?.Url == url)
							{
								<>4__this.boomboxParent.FinalizePendingPlaybackStart(<>4__this.boomboxParent.startPlayBackOnDownload);
							}
							<timeoutMessage>5__3 = null;
						}
						else
						{
							BaseListener.RPC(((MonoBehaviourPun)<>4__this).photonView, "NotifyPlayersOfErrors", (RpcTarget)0, "Download timed out for all players.");
						}
						<>4__this.currentDownloadUrl = null;
						<>4__this.currentRequestId = null;
						<>4__this.isTimeoutRecovery = false;
					}
					<>4__this.timeoutCoroutines.Remove(requestId);
					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 <MasterClientInitiateSync>d__33 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public string url;

			public DownloadHelper <>4__this;

			private <>c__DisplayClass33_0 <>8__1;

			private string <requestId>5__2;

			private Coroutine <coroutine>5__3;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<requestId>5__2 = null;
				<coroutine>5__3 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass33_0();
					<>8__1.url = url;
					if (!<>4__this.boomboxParent.data.playbackQueue.Any((Boombox.AudioEntry entry) => entry.Url == <>8__1.url))
					{
						return false;
					}
					<requestId>5__2 = Guid.NewGuid().ToString();
					<>4__this.currentDownloadUrl = <>8__1.url;
					<>4__this.currentRequestId = <requestId>5__2;
					if (downloadsReady.ContainsKey(<>8__1.url))
					{
						if (downloadsReady[<>8__1.url].Count >= Instance.baseListener.GetAllModUsers().Count)
						{
							if (!<>4__this.boomboxParent.data.isPlaying && !<>4__this.boomboxParent.audioSource.isPlaying && <>4__this.boomboxParent.isAwaitingSyncPlayback && <>8__1.url == <>4__this.boomboxParent.data.currentSong?.Url)
							{
								<>4__this.boomboxParent.FinalizePendingPlaybackStart(<>4__this.boomboxParent.startPlayBackOnDownload);
							}
							return false;
						}
						downloadsReady[<>8__1.url].Clear();
					}
					else
					{
						downloadsReady[<>8__1.url] = new HashSet<int>();
					}
					if (downloadErrors.ContainsKey(<>8__1.url))
					{
						downloadErrors[<>8__1.url].Clear();
					}
					else
					{
						downloadErrors[<>8__1.url] = new HashSet<int>();
					}
					BaseListener.RPC(((MonoBehaviourPun)<>4__this).photonView, "StartDownloadAndSync", (RpcTarget)0, <>8__1.url, PhotonNetwork.LocalPlayer.ActorNumber);
					<>4__this.timeoutCoroutines[<requestId>5__2] = ((MonoBehaviour)<>4__this).StartCoroutine(<>4__this.DownloadTimeoutCoroutine(<requestId>5__2, <>8__1.url));
					<>2__current = <>4__this.WaitForPlayersReadyOrFailed(<>8__1.url);
					<>1__state = 1;
					return true;
				case 1:
				{
					<>1__state = -1;
					Dictionary<string, Coroutine> timeoutCoroutines = <>4__this.timeoutCoroutines;
					if (timeoutCoroutines != null && timeoutCoroutines.TryGetValue(<requestId>5__2, out <coroutine>5__3) && <coroutine>5__3 != null)
					{
						((MonoBehaviour)<>4__this).StopCoroutine(<coroutine>5__3);
						<>4__this.timeoutCoroutines.Remove(<requestId>5__2);
					}
					if (<>4__this.currentDownloadUrl != <>8__1.url)
					{
						return false;
					}
					<>4__this.currentDownloadUrl = null;
					<>4__this.currentRequestId = null;
					Logger.LogInfo((object)("Consensus finished for " + <>8__1.url + ", Starting Playback."));
					Boombox boomboxParent = <>4__this.boomboxParent;
					if (boomboxParent != null && boomboxParent.isAwaitingSyncPlayback && <>8__1.url == <>4__this.boomboxParent.data.currentSong?.Url)
					{
						<>4__this.boomboxParent.FinalizePendingPlaybackStart(<>4__this.boomboxParent.startPlayBackOnDownload);
					}
					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 <ProcessDownloadQueue>d__32 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public DownloadHelper <>4__this;

			private string <url>5__1;

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

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

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

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

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>4__this.isProcessingQueue = true;
					Logger.LogInfo((object)"Master Client Download Queue Processor started.");
					break;
				case 1:
					<>1__state = -1;
					if ((Object)(object)<>4__this == (Object)null)
					{
						return false;
					}
					<url>5__1 = null;
					break;
				}
				if (<>4__this.downloadJobQueue.Count > 0 && <>4__this.isProcessingQueue)
				{
					<url>5__1 = <>4__this.downloadJobQueue.Dequeue();
					<>2__current = <>4__this.MasterClientInitiateSync(<url>5__1);
					<>1__state = 1;
					return true;
				}
				if ((Object)(object)<>4__this == (Object)null)
				{
					return false;
				}
				<>4__this.isProcessingQueue = false;
				<>4__this.currentDownloadUrl = null;
				Logger.LogInfo((object)"Master Client Download Queue Processor finished.");
				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 <WaitForPlayersReadyOrFailed>d__37 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public string url;

			public DownloadHelper <>4__this;

			private int <totalPlayers>5__1;

			private int <readyCount>5__2;

			private int <errorCount>5__3;

			private float <waitTime>5__4;

			private float? <partialConsensusStartTime>5__5;

			private bool <allAccountedFor>5__6;

			private bool <partialConsensus>5__7;

			private bool <timeOutReached>5__8;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0189: Unknown result type (might be due to invalid IL or missing references)
				//IL_0193: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<totalPlayers>5__1 = Instance.baseListener.GetAllModUsers().Count;
					<readyCount>5__2 = 0;
					<errorCount>5__3 = 0;
					<waitTime>5__4 = 0.1f;
					<partialConsensusStartTime>5__5 = null;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if ((Object)(object)<>4__this != (Object)null)
				{
					<readyCount>5__2 = (downloadsReady.ContainsKey(url) ? downloadsReady[url].Count : 0);
					<errorCount>5__3 = (downloadErrors.ContainsKey(url) ? downloadErrors[url].Count : 0);
					<allAccountedFor>5__6 = <readyCount>5__2 + <errorCount>5__3 >= <totalPlayers>5__1;
					<partialConsensus>5__7 = <readyCount>5__2 > 0 && <errorCount>5__3 > 0;
					<timeOutReached>5__8 = false;
					if (!<partialConsensus>5__7)
					{
						<partialConsensusStartTime>5__5 = null;
					}
					else if (!<partialConsensusStartTime>5__5.HasValue)
					{
						<partialConsensusStartTime>5__5 = Time.time;
					}
					else
					{
						<timeOutReached>5__8 = Time.time - <partialConsensusStartTime>5__5.Value >= 10f;
					}
					if (!(<allAccountedFor>5__6 | <timeOutReached>5__8))
					{
						<>2__current = (object)new WaitForSeconds(<waitTime>5__4);
						<>1__state = 1;
						return true;
					}
				}
				if ((Object)(object)<>4__this != (Object)null)
				{
					Logger.LogInfo((object)$"Ready to proceed with playback. Ready: {<readyCount>5__2}, Errors: {<errorCount>5__3}, Total: {<totalPlayers>5__1} for url: {url}");
				}
				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 static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		private Boombox boomboxParent = null;

		public static Dictionary<string, AudioClip> downloadedClips = new Dictionary<string, AudioClip>();

		public static Dictionary<string, string> songTitles = new Dictionary<string, string>();

		public static Dictionary<string, HashSet<int>> downloadsReady = new Dictionary<string, HashSet<int>>();

		public static Dictionary<string, HashSet<int>> downloadErrors = new Dictionary<string, HashSet<int>>();

		private const float DOWNLOAD_TIMEOUT = 40f;

		private const float TIMEOUT_THRESHOLD = 10f;

		private Dictionary<string, Coroutine> timeoutCoroutines = new Dictionary<string, Coroutine>();

		private Queue<string> downloadJobQueue = new Queue<string>();

		private bool isProcessingQueue = false;

		private string currentRequestId = null;

		private string currentDownloadUrl = null;

		private bool isTimeoutRecovery = false;

		private Coroutine processingCoroutine;

		private static readonly Regex[] supportedVideoUrlRegexes = new Regex[5]
		{
			new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?(((?:www|m)\\.)?((?:youtube(?:-nocookie)?\\.com|youtu\\.be))|music\\.youtube\\.com)(\\/(?:[\\w\\-]+\\?v=|embed\\/|live\\/|v\\/)?)([\\w\\-]+))(?<TrailingParams>(&(\\S+&)*?(t=(?<TimeStamp>(?<Seconds>\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled),
			new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?((?:www)?\\.?)(rutube\\.ru)(\\/video\\/)([\\w\\-]+))(?<TrailingParams>(\\?(?:\\S+&)*?(t=(?<TimeStamp>(?<Seconds>\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled),
			new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?((?:www)?\\.?)(music\\.yandex\\.ru)(\\/album\\/\\d+\\/track\\/)([\\w\\-]+))(?<TrailingParams>(?:\\?(\\S+&)*?(t=(?<TimeStamp>(?<Seconds>\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled),
			new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?((?:www|m)\\.)?(bilibili\\.com)(\\/video\\/)([\\w\\-]+))(?<TrailingParams>(\\?(?:\\S+&)*?(t=(?<TimeStamp>(?<Seconds>\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled),
			new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?((?:www|m)\\.)?(soundcloud\\.com|snd\\.sc)\\/([\\w\\-]+\\/[\\w\\-]+))(?<TrailingParams>(?:\\?(?:\\S+&*?#)*?t=(?<TimeStamp>(?<Minutes>\\d+)(?:\\/|(?:%3A))(?<Seconds>\\d{1,2})))?\\S*)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled)
		};

		private static ManualLogSource Logger => Instance.logger;

		private void Awake()
		{
			boomboxParent = ((Component)this).gameObject.GetComponent<Boombox>();
		}

		private void OnDestroy()
		{
			downloadJobQueue.Clear();
			foreach (Coroutine value in timeoutCoroutines.Values)
			{
				if (value != null)
				{
					((MonoBehaviour)this).StopCoroutine(value);
				}
			}
			timeoutCoroutines.Clear();
		}

		public bool IsProcessingQueue()
		{
			return isProcessingQueue;
		}

		public string GetCurrentDownloadUrl()
		{
			return currentDownloadUrl;
		}

		public static (string cleanedUrl, int seconds) IsValidVideoUrl(string url)
		{
			if (!string.IsNullOrWhiteSpace(url))
			{
				Regex[] array = supportedVideoUrlRegexes;
				foreach (Regex regex in array)
				{
					Match match = regex.Match(url);
					if (!match.Success)
					{
						continue;
					}
					Group group = match.Groups["CleanedUrl"];
					if (!group.Success)
					{
						continue;
					}
					Group group2 = match.Groups["TimeStamp"];
					int num = 0;
					if (group2.Success)
					{
						Group group3 = match.Groups["Seconds"];
						Group group4 = match.Groups["Minutes"];
						if (group3.Success && int.TryParse(group3.Value, out var result))
						{
							num = result;
						}
						if (group4.Success && int.TryParse(group4.Value, out var result2))
						{
							num += result2 * 60;
						}
					}
					return (group.Value, num);
				}
			}
			return (null, 0);
		}

		public static int CheckDownloadCount(string url, bool includeErrors = false)
		{
			if (string.IsNullOrEmpty(url))
			{
				return 0;
			}
			int num = 0;
			if (downloadsReady.ContainsKey(url))
			{
				num += downloadsReady[url].Count;
			}
			if (downloadErrors.ContainsKey(url))
			{
				num += downloadErrors[url].Count;
			}
			return num;
		}

		public void EnqueueDownload(string Url)
		{
			downloadJobQueue.Enqueue(Url);
		}

		public void DismissDownloadQueue()
		{
			downloadJobQueue.Clear();
		}

		public void StartDownloadJob()
		{
			if (!isProcessingQueue)
			{
				processingCoroutine = ((MonoBehaviour)this).StartCoroutine(ProcessDownloadQueue());
			}
		}

		public static float EstimateDownloadTimeSeconds(float songLength)
		{
			return 1.5f * songLength / 60f / 1.25f;
		}

		public void DownloadQueue(int startIndex)
		{
			if (startIndex < 0 || startIndex >= boomboxParent.data.playbackQueue.Count)
			{
				startIndex = 0;
			}
			for (int i = startIndex; i < boomboxParent.data.playbackQueue.Count; i++)
			{
				downloadJobQueue.Enqueue(boomboxParent.data.playbackQueue.ElementAt(i).Url);
			}
			for (int j = 0; j < startIndex; j++)
			{
				downloadJobQueue.Enqueue(boomboxParent.data.playbackQueue.ElementAt(j).Url);
			}
			StartDownloadJob();
		}

		[PunRPC]
		public void NotifyPlayersOfErrors(string message)
		{
			Logger.LogWarning((object)message);
			boomboxParent.UpdateUIStatus(message);
		}

		[PunRPC]
		public void ReportDownloadError(int actorNumber, string url, string errorMessage)
		{
			if (actorNumber == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				boomboxParent.UpdateUIStatus("Error: " + errorMessage);
			}
			if (PhotonNetwork.IsMasterClient)
			{
				Logger.LogError((object)$"Player {actorNumber} reported download error for {url}: {errorMessage}");
				if (!downloadErrors.ContainsKey(url))
				{
					downloadErrors[url] = new HashSet<int>();
				}
				downloadErrors[url].Add(actorNumber);
			}
		}

		[PunRPC]
		public void SetSongTitle(string url, string title)
		{
			if (string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(title))
			{
				return;
			}
			songTitles[url] = title;
			bool flag = false;
			if (boomboxParent?.data?.playbackQueue != null)
			{
				foreach (Boombox.AudioEntry item in boomboxParent.data.playbackQueue.FindAll((Boombox.AudioEntry entry) => entry.Url == url))
				{
					if (item.Title != title)
					{
						item.Title = title;
						flag = true;
					}
				}
			}
			if (boomboxParent?.data?.currentSong != null && boomboxParent.data.currentSong.Url == url)
			{
				boomboxParent.data.currentSong.Title = title;
				if (boomboxParent.data.pendingPlaybackStart)
				{
					boomboxParent.UpdateUIStatus("Loading: " + title);
				}
				else if ((Object)(object)boomboxParent.data.currentSong.GetAudioClip() != (Object)null && boomboxParent.data.isPlaying)
				{
					boomboxParent.UpdateUIStatus("Now playing: " + title);
				}
				else if ((Object)(object)boomboxParent.data.currentSong.GetAudioClip() != (Object)null)
				{
					boomboxParent.UpdateUIStatus("Ready to play: " + title);
				}
				else
				{
					boomboxParent.UpdateUIStatus("Loading: " + title);
				}
			}
			if (flag)
			{
				Boombox boombox = boomboxParent;
				if (boombox != null)
				{
					((Component)boombox).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
				}
				if (PhotonNetwork.IsMasterClient)
				{
					boomboxParent.PublishSharedState();
				}
			}
		}

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

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

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

		[PunRPC]
		public async void StartDownloadAndSync(string url, int requesterId)
		{
			if (url == null)
			{
				return;
			}
			if (currentDownloadUrl != url)
			{
				currentDownloadUrl = url;
				if (!downloadsReady.ContainsKey(url))
				{
					downloadsReady[url] = new HashSet<int>();
				}
				if (!downloadErrors.ContainsKey(url))
				{
					downloadErrors[url] = new HashSet<int>();
				}
			}
			if (downloadedClips.ContainsKey(url))
			{
				boomboxParent?.HandleDownloadedCurrentSong();
				BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber);
			}
			else if (await StartAudioDownload(url) && (Object)(object)this != (Object)null)
			{
				boomboxParent?.HandleDownloadedCurrentSong();
				BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber);
			}
		}

		[PunRPC]
		public void ReportDownloadComplete(string url, int actorNumber)
		{
			if (PhotonNetwork.IsMasterClient && url != null)
			{
				if (!downloadsReady.ContainsKey(url))
				{
					downloadsReady[url] = new HashSet<int>();
				}
				if (downloadErrors.ContainsKey(url) && downloadErrors[url].Contains(actorNumber))
				{
					downloadErrors[url].Remove(actorNumber);
				}
				if (!downloadsReady[url].Contains(actorNumber))
				{
					downloadsReady[url].Add(actorNumber);
				}
			}
		}

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

		public static async Task<AudioClip> GetAudioClipAsync(string filePath)
		{
			await Task.Yield();
			if (!File.Exists(filePath))
			{
				throw new Exception("Audio file not found at path: " + filePath);
			}
			Uri fileUri = new Uri(filePath);
			string uri = fileUri.AbsoluteUri;
			UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(uri, (AudioType)13);
			try
			{
				www.timeout = 40;
				((DownloadHandlerAudioClip)www.downloadHandler).streamAudio = true;
				TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
				UnityWebRequestAsyncOperation operation = www.SendWebRequest();
				((AsyncOperation)operation).completed += delegate
				{
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0012: Invalid comparison between Unknown and I4
					if ((int)www.result != 1)
					{
						Logger.LogError((object)("Web request failed: " + www.error + ", URI: " + uri));
						tcs.SetException(new Exception("Failed to load audio file: " + www.error));
					}
					else
					{
						tcs.SetResult(result: true);
					}
				};
				await tcs.Task;
				AudioClip clip = DownloadHandlerAudioClip.GetContent(www);
				if ((Object)(object)clip == (Object)null)
				{
					throw new Exception("Failed to get AudioClip content.");
				}
				return clip;
			}
			finally
			{
				if (www != null)
				{
					((IDisposable)www).Dispose();
				}
			}
		}

		public async Task<bool> StartAudioDownload(string url)
		{
			if (!downloadedClips.ContainsKey(url))
			{
				try
				{
					string title = await YoutubeDL.DownloadAudioTitleAsync(url);
					songTitles[url] = title;
					BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetSongTitle", (RpcTarget)0, url, title);
					string filePath = await YoutubeDL.DownloadAudioAsync(url, title);
					if (boomboxParent?.data?.currentSong?.Url == url)
					{
						boomboxParent?.UpdateUIStatus("Processing audio: " + title);
					}
					AudioClip clip = await GetAudioClipAsync(filePath);
					downloadedClips[url] = clip;
					Logger.LogInfo((object)("Downloaded and cached clip for video: " + title));
				}
				catch (Exception ex2)
				{
					Exception ex = ex2;
					if (boomboxParent?.data?.currentSong?.Url == url)
					{
						boomboxParent?.UpdateUIStatus("Error: " + ex.Message);
					}
					BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadError", (RpcTarget)0, PhotonNetwork.LocalPlayer.ActorNumber, url, ex.Message);
					return false;
				}
			}
			if (boomboxParent?.data?.playbackQueue != null)
			{
				foreach (Boombox.AudioEntry song in boomboxParent.data.playbackQueue.FindAll((Boombox.AudioEntry entry) => entry.Url == url))
				{