Decompiled source of BoomboxCartUpgrade v1.3.1

BoomBoxCartUpgradeMod.dll

Decompiled a day 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.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.1.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
	{
		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 int playbackTime = 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 UseStartTime(Boombox boombox)
			{
				int result = StartTime;
				if (boombox.data != null && boombox.data.playbackTime != 0)
				{
					result = boombox.data.playbackTime;
					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 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 = false;
			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 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;
			}
			if (syncFinished && data.isPlaying && !audioSource.isPlaying && PhotonNetwork.IsMasterClient)
			{
				int currentSongIndex = GetCurrentSongIndex();
				CleanupCurrentPlayback();
				if (currentSongIndex == -1)
				{
					BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, -1, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
				}
				else if (currentSongIndex + 1 >= data.playbackQueue.Count)
				{
					BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, (!LoopQueue) ? (-1) : 0, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
				}
				else
				{
					BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, currentSongIndex + 1, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
				}
			}
		}

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

		public async void SyncInitializeWithOthers()
		{
			if (PhotonNetwork.IsMasterClient)
			{
				Logger.LogInfo((object)$"Syncing with others - in queue: {data.playbackQueue.Count}");
				BaseListener.RPC(photonView, "SetData", (RpcTarget)1, data.key, Instance.RestoreBoomboxes.Value, data.playbackQueue.Count, PhotonNetwork.LocalPlayer.ActorNumber);
				if (data.playbackQueue.Count > 0 && data.currentSong?.Url != null)
				{
					isAwaitingSyncPlayback = true;
					startPlayBackOnDownload = data.isPlaying && Instance.AutoResume.Value;
					data.isPlaying = false;
					downloadHelper.DownloadQueue(GetCurrentSongIndex());
				}
				else
				{
					data.playbackQueue.Clear();
					data.currentSong = null;
				}
				syncFinished = true;
			}
		}

		[PunRPC]
		public void SetData(string key, bool save, int queueSize, int requesterId)
		{
			if (requesterId != PhotonNetwork.MasterClient.ActorNumber)
			{
				return;
			}
			isAwaitingSyncPlayback = false;
			syncFinished = false;
			UpdateUIStatus("Ready to play music! Enter a Video URL");
			BoomboxData boomboxData = Instance.data.GetBoomboxData().FirstOrDefault((BoomboxData data) => data.key == key);
			if (boomboxData != null)
			{
				if (!save)
				{
					Instance.data.GetBoomboxData().Remove(boomboxData);
					data = new BoomboxData();
					data.key = key;
				}
				else
				{
					data = boomboxData;
					data.isPlaying = false;
				}
			}
			else
			{
				data = new BoomboxData();
				data.key = key;
				if (save)
				{
					Instance.data.GetBoomboxData().Add(data);
				}
			}
			if (data.playbackQueue.Count != queueSize)
			{
				BaseListener.RPC(photonView, "RequestFullSync", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber);
			}
			((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
			CleanupCurrentPlayback();
		}

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

		public long GetRelativePlaybackMilliseconds()
		{
			return GetCurrentTimeMilliseconds() - (long)Math.Round(audioSource.time * 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 void CleanupCurrentPlayback()
		{
			if (audioSource.isPlaying)
			{
				audioSource.Stop();
			}
			TogglePlaying(value: false);
			audioSource.clip = null;
		}

		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;
		}

		[PunRPC]
		public async void RequestSong(string url, int seconds, int requesterId)
		{
			if (url == null)
			{
				return;
			}
			_ = requesterId == PhotonNetwork.LocalPlayer.ActorNumber;
			AudioEntry song = new AudioEntry(DownloadHelper.songTitles.ContainsKey(url) ? DownloadHelper.songTitles[url] : "Unknown Title", url)
			{
				StartTime = seconds
			};
			data.playbackQueue.Add(song);
			if (data.currentSong == null)
			{
				data.currentSong = song;
				if (PhotonNetwork.IsMasterClient)
				{
					isAwaitingSyncPlayback = true;
				}
			}
			if (PhotonNetwork.IsMasterClient)
			{
				downloadHelper.EnqueueDownload(url);
				downloadHelper.StartDownloadJob();
			}
		}

		[PunRPC]
		public void SyncQueue(int currentIndex, Dictionary<string, object>[] queueObject, int requesterId)
		{
			if (requesterId != PhotonNetwork.MasterClient.ActorNumber)
			{
				return;
			}
			List<AudioEntry> list = DeserializeAudioEntryArray(queueObject);
			Logger.LogInfo((object)$"SyncQueue RPC received: currentIndex={currentIndex}, queueSize={list.Count}");
			data.playbackQueue = list;
			foreach (AudioEntry item in data.playbackQueue)
			{
				if (!DownloadHelper.songTitles.ContainsKey(item.Url))
				{
					DownloadHelper.songTitles[item.Url] = item.Title;
				}
			}
			if (currentIndex == -1)
			{
				isAwaitingSyncPlayback = false;
				data.currentSong = null;
				CleanupCurrentPlayback();
			}
			else if (currentIndex >= data.playbackQueue.Count)
			{
				Logger.LogError((object)$"SyncQueue RPC received with wrong index: newSongIndex={currentIndex}, songCount={data.playbackQueue.Count}, requesterId={requesterId}");
			}
			else
			{
				data.currentSong = list[currentIndex];
			}
		}

		[PunRPC]
		public void DismissQueue(int requesterId)
		{
			if (requesterId != PhotonNetwork.MasterClient.ActorNumber)
			{
				if (PhotonNetwork.IsMasterClient && !Instance.MasterClientDismissQueue.Value)
				{
					BaseListener.RPC(photonView, "DismissQueue", (RpcTarget)0, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				return;
			}
			if (audioSource.isPlaying)
			{
				CleanupCurrentPlayback();
				UpdateUIStatus("Ready to play music! Enter a Video URL");
			}
			data.playbackQueue.Clear();
			data.currentSong = null;
			if (PhotonNetwork.IsMasterClient)
			{
				downloadHelper.DismissDownloadQueue();
				downloadHelper.ForceCancelDownload();
			}
		}

		[PunRPC]
		public void MoveQueueItem(int index, int newIndex, int requesterId)
		{
			if (index < 0 || index >= data.playbackQueue.Count || newIndex < 0 || newIndex >= data.playbackQueue.Count || index == newIndex)
			{
				Logger.LogError((object)$"MoveQueueItem RPC received with invalid indices: {index}, {newIndex}, queue count: {data.playbackQueue.Count}");
				if (PhotonNetwork.IsMasterClient)
				{
					Player val = ((IEnumerable<Player>)PhotonNetwork.PlayerList).FirstOrDefault((Func<Player, bool>)((Player player) => player.ActorNumber == requesterId));
					if (val != null && !val.IsLocal && Instance.baseListener.GetAllModUsers().Contains(requesterId))
					{
						HandleLateJoin(val);
					}
				}
				else
				{
					BaseListener.RPC(photonView, "RequestFullSync", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber);
				}
			}
			else
			{
				AudioEntry item = data.playbackQueue[index];
				data.playbackQueue.RemoveAt(index);
				data.playbackQueue.Insert(newIndex, item);
				if (PhotonNetwork.IsMasterClient && data.currentSong?.Url != null && DownloadHelper.CheckDownloadCount(data.currentSong.Url) < Instance.baseListener.GetAllModUsers().Count)
				{
					downloadHelper.DismissDownloadQueue();
					downloadHelper.DownloadQueue(GetCurrentSongIndex());
				}
			}
		}

		[PunRPC]
		public void RemoveQueueItem(int index, int requesterId)
		{
			if (index < 0 || index > data.playbackQueue.Count)
			{
				Logger.LogError((object)$"RemoveQueueItem RPC received with wrong index: index={index}, songCount={data.playbackQueue.Count}, requesterId={requesterId}");
				if (PhotonNetwork.IsMasterClient)
				{
					Player val = ((IEnumerable<Player>)PhotonNetwork.PlayerList).FirstOrDefault((Func<Player, bool>)((Player player) => player.ActorNumber == requesterId));
					if (val != null && !val.IsLocal && Instance.baseListener.GetAllModUsers().Contains(requesterId))
					{
						HandleLateJoin(val);
					}
				}
				else
				{
					BaseListener.RPC(photonView, "RequestFullSync", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				return;
			}
			int currentSongIndex = GetCurrentSongIndex();
			data.playbackQueue.RemoveAt(index);
			if (index != currentSongIndex)
			{
				return;
			}
			CleanupCurrentPlayback();
			data.currentSong = null;
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			if (data.playbackQueue.Count > 0)
			{
				index %= data.playbackQueue.Count;
				if (currentSongIndex < data.playbackQueue.Count || LoopQueue)
				{
					BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, index, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
					return;
				}
			}
			BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, -1, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
		}

		[PunRPC]
		public async void SyncPlayback(int newSongIndex, long startTime, int requesterId)
		{
			if (newSongIndex == -1)
			{
				isAwaitingSyncPlayback = false;
				data.currentSong = null;
				CleanupCurrentPlayback();
				return;
			}
			if (newSongIndex >= data.playbackQueue.Count)
			{
				if (!PhotonNetwork.IsMasterClient)
				{
					BaseListener.RPC(photonView, "RequestFullSync", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				Logger.LogError((object)$"SyncPlayback RPC received with wrong index: newSongIndex={newSongIndex}, songCount={data.playbackQueue.Count}, startTime={startTime}, requesterId={requesterId}");
				return;
			}
			if (GetCurrentSongIndex() == newSongIndex)
			{
				if (!data.isPlaying || !audioSource.isPlaying)
				{
					StartPlayBack();
				}
				SetPlaybackTime(startTime);
				return;
			}
			if (data.isPlaying || audioSource.isPlaying)
			{
				CleanupCurrentPlayback();
			}
			_ = data.currentSong;
			data.currentSong = data.playbackQueue.ElementAt(newSongIndex);
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			if (data.currentSong?.Url == null)
			{
				BaseListener.RPC(photonView, "RemoveQueueItem", (RpcTarget)0, newSongIndex, PhotonNetwork.LocalPlayer.ActorNumber);
			}
			else if (DownloadHelper.CheckDownloadCount(data.currentSong.Url) < Instance.baseListener.GetAllModUsers().Count)
			{
				if (data.currentSong.Url != downloadHelper.GetCurrentDownloadUrl())
				{
					downloadHelper.DismissDownloadQueue();
				}
				isAwaitingSyncPlayback = true;
				if (data.currentSong.Url != downloadHelper.GetCurrentDownloadUrl())
				{
					downloadHelper.DownloadQueue(newSongIndex);
				}
			}
			else
			{
				BaseListener.RPC(photonView, "PlayPausePlayback", (RpcTarget)0, true, GetCurrentTimeMilliseconds() - data.currentSong.UseStartTime(this) * 1000, PhotonNetwork.LocalPlayer.ActorNumber);
			}
		}

		[PunRPC]
		public void PlayPausePlayback(bool startPlaying, long startTime, int requesterId)
		{
			if (data.playbackQueue.Count == 0 || (Object)(object)data.currentSong?.GetAudioClip() == (Object)null)
			{
				return;
			}
			if (startPlaying)
			{
				if ((Object)(object)data.currentSong?.GetAudioClip() == (Object)null)
				{
					if (PhotonNetwork.IsMasterClient)
					{
						BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, -1, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
					}
					return;
				}
				StartPlayBack();
				string text = "Started";
			}
			else
			{
				if ((Object)(object)audioSource.clip != (Object)(object)data.currentSong?.GetAudioClip() && (Object)(object)data.currentSong?.GetAudioClip() != (Object)null)
				{
					StartPlayBack();
				}
				PausePlayBack();
				string text = "Paused";
			}
			SetPlaybackTime(startTime);
			if (data.isPlaying)
			{
				UpdateUIStatus("Now playing: " + ((data.currentSong?.Url == null) ? "Unkown" : data.currentSong?.Title));
			}
		}

		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;
			}
		}

		[PunRPC]
		public void UpdateQuality(int level, int requesterId)
		{
			BoomboxController component = ((Component)this).GetComponent<BoomboxController>();
			SetQuality(level);
		}

		[PunRPC]
		public void UpdateVolume(float volume, int requesterId)
		{
			BoomboxController component = ((Component)this).GetComponent<BoomboxController>();
			data.absVolume = volume;
			float volume2 = volume * data.personalVolumePercentage;
			audioSource.volume = volume2;
			UpdateAudioRangeBasedOnVolume(volume2);
		}

		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);
		}

		[PunRPC]
		public void UpdateLooping(bool loop, int requesterId)
		{
			if (PhotonNetwork.MasterClient.ActorNumber == requesterId)
			{
				LoopQueue = loop;
			}
		}

		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);
				}
			}
		}

		[PunRPC]
		public void RequestFullSync(int requesterId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				Player val = ((IEnumerable<Player>)PhotonNetwork.PlayerList).FirstOrDefault((Func<Player, bool>)((Player player) => player.ActorNumber == requesterId));
				if (val != null && Instance.baseListener.GetAllModUsers().Contains(requesterId))
				{
					HandleLateJoin(val);
				}
			}
		}

		public override void OnPlayerEnteredRoom(Player newPlayer)
		{
			((MonoBehaviourPunCallbacks)this).OnPlayerEnteredRoom(newPlayer);
			if (!PhotonNetwork.IsMasterClient || Instance.modDisabled)
			{
				return;
			}
			Logger.LogInfo((object)$"New player {newPlayer.ActorNumber} joined - syncing current playback state");
			Task.Run(async delegate
			{
				PhotonView obj = ((MonoBehaviourPun)Instance.baseListener).photonView;
				if (obj != null)
				{
					obj.RPC("ModFeedbackCheck", newPlayer, new object[2]
					{
						"1.3.1",
						PhotonNetwork.LocalPlayer.ActorNumber
					});
				}
				float startTime = Time.time;
				while ((Object)(object)photonView != (Object)null && !PersistentData.GetBoomboxViewStatus(newPlayer, photonView.ViewID))
				{
					if (Time.time - startTime > 5f)
					{
						return;
					}
					await Task.Delay(200);
				}
				if ((Object)(object)photonView != (Object)null && Instance.baseListener.GetAllModUsers().Contains(newPlayer.ActorNumber))
				{
					HandleLateJoin(newPlayer);
				}
			});
		}

		private async void HandleLateJoin(Player player)
		{
			BaseListener.RPC(photonView, "SetData", player, data.key, Instance.RestoreBoomboxes.Value, 0, PhotonNetwork.LocalPlayer.ActorNumber);
			BaseListener.RPC(photonView, "SyncQueue", player, GetCurrentSongIndex(), SerializeAudioEntryList(data.playbackQueue).ToArray(), PhotonNetwork.LocalPlayer.ActorNumber);
			BaseListener.RPC(photonView, "UpdateLooping", player, LoopQueue, PhotonNetwork.LocalPlayer.ActorNumber);
			BaseListener.RPC(photonView, "UpdateVolume", player, data.absVolume, PhotonNetwork.LocalPlayer.ActorNumber);
			AudioSource obj = audioSource;
			if ((Object)(object)((obj != null) ? obj.clip : null) != (Object)null)
			{
				int downloadIndex = GetCurrentSongIndex() + ((audioSource.isPlaying && audioSource.time + 10f > audioSource.clip.length) ? 1 : 0);
				downloadHelper.DismissDownloadQueue();
				downloadHelper.DownloadQueue(downloadIndex);
			}
		}

		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;
				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();
				if (Instance.RestoreBoomboxes.Value)
				{
					boomboxData.Insert(index, data);
				}
				BaseListener.RPC(photonView, "SetData", (RpcTarget)1, data.key, Instance.RestoreBoomboxes.Value, data.playbackQueue.Count, PhotonNetwork.LocalPlayer.ActorNumber);
			}
		}

		public List<Dictionary<string, object>> SerializeAudioEntryList(List<AudioEntry> list)
		{
			List<Dictionary<string, object>> list2 = new List<Dictionary<string, object>>();
			foreach (AudioEntry item in list)
			{
				Dictionary<string, object> dictionary = new Dictionary<string, object>();
				dictionary.Add("Title", item.Title);
				dictionary.Add("Url", item.Url);
				dictionary.Add("StartTime", item.StartTime);
				list2.Add(dictionary);
			}
			return list2;
		}

		public List<AudioEntry> DeserializeAudioEntryArray(Dictionary<string, object>[] array)
		{
			List<AudioEntry> list = new List<AudioEntry>();
			foreach (Dictionary<string, object> dictionary in array)
			{
				dictionary.TryGetValue("Title", out var value);
				string title = (value as string) ?? "Unknown";
				dictionary.TryGetValue("Url", out var value2);
				string url = value2 as string;
				dictionary.TryGetValue("StartTime", out var value3);
				int valueOrDefault = (value3 as int?).GetValueOrDefault();
				AudioEntry audioEntry = new AudioEntry(title, url);
				audioEntry.StartTime = valueOrDefault;
				list.Add(audioEntry);
			}
			return list;
		}
	}
	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_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: 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>();
				if ((Object)(object)visualizer == (Object)null)
				{
					visualizer = ((Component)this).gameObject.AddComponent<Visualizer>();
					visualizer.audioSource = boombox.audioSource;
				}
				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;
			}
		}

		public void UpdateStatusFromBoombox()
		{
			if ((Object)(object)boombox != (Object)null)
			{
				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.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;
					BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, boombox.GetCurrentSongIndex(), boombox.GetRelativePlaybackMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
					return;
				}
			}
			if (songIndexForTime != boombox.GetCurrentSongIndex())
			{
				BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, boombox.GetCurrentSongIndex(), boombox.GetRelativePlaybackMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
			}
		}

		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;
				}
				BaseListener.RPC(photonView, "UpdateVolume", (RpcTarget)0, boombox.data.absVolume, PhotonNetwork.LocalPlayer.ActorNumber);
			}
		}

		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_07ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_075e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0780: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0801: Unknown result type (might be due to invalid IL or missing references)
			//IL_092b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0932: Unknown result type (might be due to invalid IL or missing references)
			//IL_0937: Unknown result type (might be due to invalid IL or missing references)
			//IL_0940: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a27: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a2e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a33: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a3c: 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)
			{
				AudioSource audioSource2 = boombox.audioSource;
				if ((Object)(object)((audioSource2 != null) ? audioSource2.clip : null) != (Object)null)
				{
					BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, boombox.GetCurrentSongIndex(), boombox.GetRelativePlaybackMilliseconds() + 10000, PhotonNetwork.LocalPlayer.ActorNumber);
				}
			}
			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, num6) = IsValidVideoUrl(urlInput);
				if (string.IsNullOrEmpty(text3))
				{
					ShowErrorMessage("Invalid Video URL!");
				}
				else if (lastUrl != text3)
				{
					lastUrl = text3;
					BaseListener.RPC(photonView, "RequestSong", (RpcTarget)0, text3, num6, PhotonNetwork.LocalPlayer.ActorNumber);
					GUI.FocusControl((string)null);
				}
			}
			string text4 = (((Object)(object)boombox.data.currentSong?.GetAudioClip() == (Object)null) ? "..." : (boombox.audioSource.isPlaying ? "▌▌ PAUSE" : "▶ RESUME"));
			if (GUILayout.Button(text4, buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }) && (Object)(object)boombox.data.currentSong?.GetAudioClip() != (Object)null)
			{
				BaseListener.RPC(photonView, "PlayPausePlayback", (RpcTarget)0, !boombox.audioSource.isPlaying, boombox.GetRelativePlaybackMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
			}
			if (GUILayout.Button(">>", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(40f),
				GUILayout.Height(40f)
			}) && (Object)(object)boombox != (Object)null)
			{
				AudioSource audioSource3 = boombox.audioSource;
				if ((Object)(object)((audioSource3 != null) ? audioSource3.clip : null) != (Object)null)
				{
					BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, boombox.GetCurrentSongIndex(), boombox.GetRelativePlaybackMilliseconds() - 10000, PhotonNetwork.LocalPlayer.ActorNumber);
				}
			}
			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 num7 = boombox.data.absVolume * 100f;
			GUILayout.Label($"Volume: {Mathf.Round(num7)}%", 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 num8 = GUILayout.HorizontalSlider(boombox.data.absVolume, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			if (num8 != 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 = num8;
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(15f);
			GUILayout.Label($"Personal Volume %: {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 num9 = GUILayout.HorizontalSlider(boombox.data.personalVolumePercentage, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			if (num9 != boombox.data.personalVolumePercentage)
			{
				isIndividualVolumeBeingDragged = true;
				boombox.data.personalVolumePercentage = num9;
				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 num10 = GUILayout.HorizontalSlider((float)qualityLevel, 0f, 4f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			int num11 = Mathf.RoundToInt(num10);
			if (num11 != qualityLevel && !isQualitySliderBeingDragged)
			{
				isQualitySliderBeingDragged = true;
			}
			qualityLevel = num11;
			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)
			{
				BaseListener.RPC(photonView, "UpdateLooping", (RpcTarget)0, flag4, PhotonNetwork.LocalPlayer.ActorNumber);
			}
			GUILayout.Space(10f);
			bool flag5 = (Object)(object)visualEffects != (Object)null && visualEffects.AreLightsOn();
			bool flag6 = GUILayout.Toggle(flag5, "RGB Lights enabled", Array.Empty<GUILayoutOption>());
			if (flag6 != flag5 && (Object)(object)visualEffects != (Object)null)
			{
				visualEffects.SetLights(flag6);
			}
			GUILayout.Space(10f);
			bool flag7 = (Object)(object)visualizer != (Object)null;
			bool flag8 = GUILayout.Toggle(flag7, "Audio Visualizer enabled", Array.Empty<GUILayoutOption>());
			if (flag8 != flag7)
			{
				if (flag8)
				{
					visualizer = ((Component)this).gameObject.AddComponent<Visualizer>();
					visualizer.audioSource = boombox.audioSource;
				}
				else
				{
					Object.Destroy((Object)(object)visualizer);
					visualizer = null;
				}
			}
			GUILayout.EndScrollView();
		}

		private void DrawQueue(Boombox boombox)
		{
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: 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>()))
			{
				BaseListener.RPC(photonView, "DismissQueue", (RpcTarget)((!PhotonNetwork.IsMasterClient) ? 2 : 0), PhotonNetwork.LocalPlayer.ActorNumber);
				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)
				{
					BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, i, Boombox.GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
				}
				if (!flag)
				{
					if (i > 0)
					{
						if (GUILayout.Button("▲", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
						{
							GUILayout.Width(25f),
							GUILayout.Height(28f)
						}))
						{
							BaseListener.RPC(photonView, "MoveQueueItem", (RpcTarget)0, i, i - 1, PhotonNetwork.LocalPlayer.ActorNumber);
						}
					}
					else
					{
						GUILayout.Space(30f);
					}
					if (i + 1 < playbackQueue.Count)
					{
						if (GUILayout.Button("▼", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
						{
							GUILayout.Width(25f),
							GUILayout.Height(28f)
						}))
						{
							BaseListener.RPC(photonView, "MoveQueueItem", (RpcTarget)0, i, i + 1, PhotonNetwork.LocalPlayer.ActorNumber);
						}
					}
					else
					{
						GUILayout.Space(30f);
					}
					if (GUILayout.Button("X", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
					{
						GUILayout.Width(25f),
						GUILayout.Height(28f)
					}))
					{
						BaseListener.RPC(photonView, "RemoveQueueItem", (RpcTarget)0, i, PhotonNetwork.LocalPlayer.ActorNumber);
					}
				}
				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
	{
		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)
		{
			songTitles[url] = title;
			if (boomboxParent.data.currentSong != null && boomboxParent.data.currentSong.Url == url)
			{
				boomboxParent.data.currentSong.Title = title;
				if ((Object)(object)boomboxParent.data.currentSong.GetAudioClip() != (Object)null)
				{
					boomboxParent.UpdateUIStatus("Now playing: " + title);
				}
				else
				{
					boomboxParent.UpdateUIStatus("Loading: " + title);
				}
			}
		}

		private IEnumerator ProcessDownloadQueue()
		{
			isProcessingQueue = true;
			Logger.LogInfo((object)"Master Client Download Queue Processor started.");
			while (downloadJobQueue.Count > 0 && isProcessingQueue)
			{
				string url = downloadJobQueue.Dequeue();
				yield return MasterClientInitiateSync(url);
				if ((Object)(object)this == (Object)null)
				{
					yield break;
				}
			}
			if (!((Object)(object)this == (Object)null))
			{
				isProcessingQueue = false;
				currentDownloadUrl = null;
				Logger.LogInfo((object)"Master Client Download Queue Processor finished.");
			}
		}

		private IEnumerator MasterClientInitiateSync(string url)
		{
			if (!boomboxParent.data.playbackQueue.Any((Boombox.AudioEntry entry) => entry.Url == url))
			{
				yield break;
			}
			string requestId = Guid.NewGuid().ToString();
			currentDownloadUrl = url;
			currentRequestId = requestId;
			if (downloadsReady.ContainsKey(url))
			{
				if (downloadsReady[url].Count >= Instance.baseListener.GetAllModUsers().Count)
				{
					if (!boomboxParent.data.isPlaying && !boomboxParent.audioSource.isPlaying && boomboxParent.isAwaitingSyncPlayback && url == boomboxParent.data.currentSong?.Url)
					{
						boomboxParent.isAwaitingSyncPlayback = false;
						BaseListener.RPC(((MonoBehaviourPun)this).photonView, "PlayPausePlayback", (RpcTarget)0, boomboxParent.startPlayBackOnDownload, Boombox.GetCurrentTimeMilliseconds() - boomboxParent.data.currentSong.UseStartTime(boomboxParent) * 1000, PhotonNetwork.LocalPlayer.ActorNumber);
						boomboxParent.startPlayBackOnDownload = true;
					}
					yield break;
				}
				downloadsReady[url].Clear();
			}
			else
			{
				downloadsReady[url] = new HashSet<int>();
			}
			if (downloadErrors.ContainsKey(url))
			{
				downloadErrors[url].Clear();
			}
			else
			{
				downloadErrors[url] = new HashSet<int>();
			}
			BaseListener.RPC(((MonoBehaviourPun)this).photonView, "StartDownloadAndSync", (RpcTarget)0, url, PhotonNetwork.LocalPlayer.ActorNumber);
			timeoutCoroutines[requestId] = ((MonoBehaviour)this).StartCoroutine(DownloadTimeoutCoroutine(requestId, url));
			yield return WaitForPlayersReadyOrFailed(url);
			Coroutine coroutine = default(Coroutine);
			if ((timeoutCoroutines?.TryGetValue(requestId, out coroutine) ?? false) && coroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(coroutine);
				timeoutCoroutines.Remove(requestId);
			}
			if (!(currentDownloadUrl != url))
			{
				currentDownloadUrl = null;
				currentRequestId = null;
				Logger.LogInfo((object)("Consensus finished for " + url + ", Starting Playback."));
				if ((boomboxParent?.isAwaitingSyncPlayback ?? false) && url == boomboxParent.data.currentSong?.Url)
				{
					boomboxParent.isAwaitingSyncPlayback = false;
					BaseListener.RPC(((MonoBehaviourPun)this).photonView, "PlayPausePlayback", (RpcTarget)0, boomboxParent.startPlayBackOnDownload, Boombox.GetCurrentTimeMilliseconds() - boomboxParent.data.currentSong.UseStartTime(boomboxParent) * 1000, PhotonNetwork.LocalPlayer.ActorNumber);
					boomboxParent.startPlayBackOnDownload = true;
				}
			}
		}

		private IEnumerator DownloadTimeoutCoroutine(string requestId, string url)
		{
			yield return (object)new WaitForSeconds(40f);
			if (currentRequestId == requestId && isProcessingQueue)
			{
				Logger.LogWarning((object)("Download timeout for url: " + url));
				Logger.LogInfo((object)"Master client initiating timeout recovery");
				isTimeoutRecovery = true;
				foreach (int player in Instance.baseListener.GetAllModUsers())
				{
					if (!downloadsReady[url].Contains(player))
					{
						if (!downloadErrors.ContainsKey(url))
						{
							downloadErrors[url] = new HashSet<int>();
						}
						downloadErrors[url].Add(player);
						Logger.LogWarning((object)$"Player {player} timed out during download");
					}
				}
				if (downloadsReady.ContainsKey(url) && downloadsReady[url].Count > 0)
				{
					string timeoutMessage = $"Some players timed out. Continuing playback for {downloadsReady[url].Count} players.";
					BaseListener.RPC(((MonoBehaviourPun)this).photonView, "NotifyPlayersOfErrors", (RpcTarget)0, timeoutMessage);
					if (boomboxParent.isAwaitingSyncPlayback && boomboxParent.data.currentSong?.Url == url)
					{
						boomboxParent.isAwaitingSyncPlayback = false;
						BaseListener.RPC(((MonoBehaviourPun)this).photonView, "PlayPausePlayback", (RpcTarget)0, boomboxParent.startPlayBackOnDownload, Boombox.GetCurrentTimeMilliseconds() - boomboxParent.data.currentSong.UseStartTime(boomboxParent) * 1000, PhotonNetwork.LocalPlayer.ActorNumber);
						boomboxParent.startPlayBackOnDownload = true;
					}
				}
				else
				{
					BaseListener.RPC(((MonoBehaviourPun)this).photonView, "NotifyPlayersOfErrors", (RpcTarget)0, "Download timed out for all players.");
				}
				currentDownloadUrl = null;
				currentRequestId = null;
				isTimeoutRecovery = false;
			}
			timeoutCoroutines.Remove(requestId);
		}

		[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))
			{
				BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber);
			}
			else if (await StartAudioDownload(url) && (Object)(object)this != (Object)null)
			{
				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)
			{
				return;
			}
			if (!downloadsReady.ContainsKey(url))
			{
				downloadsReady[url] = new HashSet<int>();
			}
			if (downloadErrors.ContainsKey(url) && downloadErrors[url].Contains(actorNumber))
			{
				downloadErrors[url].Remove(actorNumber);
			}
			if (!boomboxParent.isAwaitingSyncPlayback && boomboxParent.data.currentSong?.Url == url)
			{
				PhotonNetwork.CurrentRoom.Players.TryGetValue(actorNumber, out var value);
				if (!downloadsReady[url].Contains(actorNumber) && value != null)
				{
					Logger.LogInfo((object)$"Player {actorNumber} reported ready late. Sending start playback.");
					BaseListener.RPC(((MonoBehaviourPun)this).photonView, "PlayPausePlayback", value, boomboxParent.audioSource.isPlaying && boomboxParent.data.isPlaying, boomboxParent.GetRelativePlaybackMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber);
				}
			}
			if (!downloadsReady[url].Contains(actorNumber))
			{
				downloadsReady[url].Add(actorNumber);
			}
		}

		private IEnumerator WaitForPlayersReadyOrFailed(string url)
		{
			int totalPlayers = Instance.baseListener.GetAllModUsers().Count;
			int readyCount = 0;
			int errorCount = 0;
			float waitTime = 0.1f;
			float? partialConsensusStartTime = null;
			while ((Object)(object)this != (Object)null)
			{
				readyCount = (downloadsReady.ContainsKey(url) ? downloadsReady[url].Count : 0);
				errorCount = (downloadErrors.ContainsKey(url) ? downloadErrors[url].Count : 0);
				bool allAccountedFor = readyCount + errorCount >= totalPlayers;
				bool partialConsensus = readyCount > 0 && errorCount > 0;
				bool timeOutReached = false;
				if (!partialConsensus)
				{
					partialConsensusStartTime = null;
				}
				else if (!partialConsensusStartTime.HasValue)
				{
					partialConsensusStartTime = Time.time;
				}
				else
				{
					timeOutReached = Time.time - partialConsensusStartTime.Value >= 10f;
				}
				if (allAccountedFor || timeOutReached)
				{
					break;
				}
				yield return (object)new WaitForSeconds(waitTime);
			}
			if ((Object)(object)this != (Object)null)
			{
				Logger.LogInfo((object)$"Ready to proceed with playback. Ready: {readyCount}, Errors: {errorCount}, Total: {totalPlayers} for 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))
				{
					if (songTitles.ContainsKey(url))
					{
						song.Title = songTitles[url];
					}
				}
			}
			return true;
		}

		public void RemoveFromCache(string url)
		{
			if (downloadedClips.ContainsKey(url))
			{
				AudioClip val = downloadedClips[url];
				downloadedClips.Remove(url);
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
			if (songTitles.ContainsKey(url))
			{
				songTitles.Remove(url);
			}
			if (downloadsReady.ContainsKey(url))
			{
				downloadsReady.Remove(url);
			}
			if (downloadErrors.ContainsKey(url))
			{
				downloadErrors.Remove(url);
			}
		}

		public override void OnPlayerLeftRoom(Player otherPlayer)
		{
			if (isProcessingQueue && !string.IsNullOrEmpty(currentDownloadUrl) && downloadsReady.ContainsKey(currentDownloadUrl) && !downloadsReady[currentDownloadUrl].Contains(otherPlayer.ActorNumber))
			{
				if (!downloadErrors.ContainsKey(currentDownloadUrl))
				{
					downloadErrors[currentDownloadUrl] = new HashSet<int>();
				}
				downloadErrors[currentDownloadUrl].Add(otherPlayer.ActorNumber);
				Logger.LogInfo((object)$"Player {otherPlayer.ActorNumber} left during download - marking as error");
			}
			((MonoBehaviourPunCallbacks)this).OnPlayerLeftRoom(otherPlayer);
		}

		public void ForceCancelDownload()
		{
			if (!isProcessingQueue)
			{
				return;
			}
			string text = currentDownloadUrl;
			if (text == null)
			{
				text = "Unknown";
			}
			currentDownloadUrl = null;
			foreach (Coroutine value in timeoutCoroutines.Values)
			{
				if (value != null)
				{
					((MonoBehaviour)this).StopCoroutine(value);
				}
			}
			timeoutCoroutines.Clear();
			BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadError", (RpcTarget)0, PhotonNetwork.LocalPlayer.ActorNumber, text, "Download cancelled.");
			Logger.LogInfo((object)"Download was force cancelled by user.");
		}
	}
	public class VisualEffects : MonoBehaviour
	{
		private Light frontLight;

		private Light backLight;

		private float rgbSpeed = 0.5f;

		private bool lightsOn = false;

		private void Start()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("BoomboxFrontLight");
			val.transform.SetParent(((Component)this).transform);
			val.transform.localPosition = new Vector3(0f, 0f, 1f);
			frontLight = val.AddComponent<Light>();
			frontLight.type = (LightType)2;
			frontLight.range = 6f;
			frontLight.intensity = 2f;
			((Behaviour)frontLight).enabled = false;
			GameObject val2 = new GameObject("BoomboxBackLight");
			val2.transform.SetParent(((Component)this).transform);
			val2.transform.localPosition = new Vector3(0f, 0f, -1f);
			backLight = val2.AddComponent<Light>();
			backLight.type = (LightType)2;
			backLight.range = 6f;
			backLight.intensity = 2f;
			((Behaviour)backLight).enabled = false;
		}

		private void Update()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (lightsOn)
			{
				float num = Time.time * rgbSpeed;
				Color color = Color.HSVToRGB(num % 1f, 1f, 1f);
				if ((Object)(object)frontLight != (Object)null)
				{
					frontLight.color = color;
				}
				if ((Object)(object)backLight != (Object)null)
				{
					backLight.color = color;
				}
			}
		}

		public void SetLights(bool on)
		{
			lightsOn = on;
			if ((Object)(object)frontLight != (Object)null)
			{
				((Behaviour)frontLight).enabled = on;
			}
			if ((Object)(object)backLight != (Object)null)
			{
				((Behaviour)backLight).enabled = on;
			}
		}

		public bool AreLightsOn()
		{
			return lightsOn;
		}
	}
	public class Visualizer : MonoBehaviour
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		public AudioSource audioSource;

		private bool visible = true;

		public int numBars = 16;

		public int spectrumSize = 128;

		public float radius = 0.7f;

		public float barWidth = 0.1f;

		public float barMaxHeight = 1.5f;

		public float barMinHeight = 0.15f;

		public float heightMultiplier = 24f;

		private float[] spectrum;

		private Transform[] bars;

		private bool lastPlaying = false;

		public bool IsVisible
		{
			get
			{
				return visible;
			}
			set
			{
				visible = value;
				for (int i = 0; i < numBars; i++)
				{
					((Component)bars[i]).GetComponent<Renderer>().enabled = value || Instance.VisualizerBehaviourPaused.Value != BoomBoxCartMod.VisualizerPaused.Hide;
				}
			}
		}

		private void Start()
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)audioSource == (Object)null)
			{
				audioSource = ((Component)this).GetComponent<AudioSource>();
			}
			spectrum = new float[spectrumSize];
			bars = (Transform[])(object)new Transform[numBars];
			Vector3 localPosition = default(Vector3);
			for (int i = 0; i < numBars; i++)
			{
				GameObject val = GameObject.CreatePrimitive((PrimitiveType)3);
				val.transform.SetParent(((Component)this).transform);
				float num = Mathf.Lerp(-(float)Math.PI / 2f, (float)Math.PI / 2f, (float)i / (float)(numBars - 1));
				((Vector3)(ref localPosition))..ctor(Mathf.Sin(num) * radius, 0.5f, Mathf.Cos(num) * radius + 0.7f);
				val.transform.localPosition = localPosition;
				val.transform.localScale = new Vector3(barWidth, barMinHeight, barWidth);
				val.GetComponent<Renderer>().material.color = Color.HSVToRGB((float)i / (float)numBars, 1f, 1f);
				Object.Destroy((Object)(object)val.GetComponent<Collider>());
				bars[i] = val.transform;
			}
			IsVisible = audioSource.isPlaying || Instance.VisualizerBehaviourPaused.Value != BoomBoxCartMod.VisualizerPaused.Hide;
			lastPlaying = audioSource.isPlaying;
		}

		private void Update()
		{
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			AudioSource obj = audioSource;
			bool flag = obj != null && obj.isPlaying;
			if (lastPlaying != flag)
			{
				IsVisible = flag || Instance.VisualizerBehaviourPaused.Value != BoomBoxCartMod.VisualizerPaused.Hide;
				lastPlaying = flag;
			}
			if (!visible)
			{
				return;
			}
			if (flag)
			{
				audioSource.GetSpectrumData(spectrum, 0, (FFTWindow)4);
				for (int i = 0; i < numBars; i++)
				{
					int num = (int)Mathf.Pow((float)spectrumSize, (float)i / (float)numBars);
					int num2 = (int)Mathf.Pow((float)spectrumSize, (float)(i + 1) / (float)numBars);
					num2 = Mathf.Clamp(num2, num + 1, spectrumSize);
					float num3 = 0f;
					for (int j = num; j < num2; j++)
					{
						num3 += spectrum[j];
					}
					num3 /= (float)(num2 - num);
					float y = Mathf.Clamp(Mathf.Pow(num3 * heightMultiplier, 0.5f), barMinHeight, barMaxHeight);
					Vector3 localScale = bars[i].localScale;
					localScale.y = y;
					bars[i].localScale = localScale;
				}
			}
			else if (Instance.VisualizerBehaviourPaused.Value == BoomBoxCartMod.VisualizerPaused.Show)
			{
				for (int k = 0; k < numBars; k++)
				{
					Vector3 localScale2 = bars[k].localScale;
					localScale2.y = barMinHeight;
					bars[k].localScale = localScale2;
				}
			}
		}

		private void OnDestroy()
		{
			if (bars == null)
			{
				return;
			}
			Transform[] array = bars;
			foreach (Transform val in array)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
		}
	}
	[BepInPlugin("Doppelclick.BoomboxCartUpgrade", "BoomboxCartUpgrade", "1.3.1")]
	public class BoomBoxCartMod : BaseUnityPlugin
	{
		public enum VisualizerPaused
		{
			Show,
			PausePosition,
			Hide
		}

		public const string modGUID = "Doppelclick.BoomboxCartUpgrade";

		public const string modName = "BoomboxCartUpgrade";

		public const string modVersion = "1.3.1";

		private readonly Harmony harmony = new Harmony("Doppelclick.BoomboxCartUpgrade");

		public BaseListener baseListener = null;

		internal static BoomBoxCartMod instance;

		internal ManualLogSource logger;

		private bool modDisabledValue = false;

		public PersistentData data;

		public bool modDisabled
		{
			get
			{
				return modDisabledValue;
			}
			set
			{
				if (modDisabledValue != value)
				{
					logger.LogInfo((object)("Mod " + (value ? "Disabled" : "Enabled")));
				}
				modDisabledValue = value;
				if (!value || data == null)
				{
					return;
				}
				foreach (Boombox allBoombox in data.GetAllBoomboxes())
				{
					PhotonView photonView = allBoombox.photonView;
					Object.Destroy((Object)(object)allBoombox);
					if (photonView != null)
					{
						photonView.RefreshRpcMonoBehaviourCache();
					}
				}
				data.GetAllBoomboxes().Clear();
				data.GetBoomboxData().Clear();
			}
		}

		public ConfigEntry<Key> OpenUIKey { get; private set; }

		public ConfigEntry<Key> GlobalMuteKey { get; private set; }

		public ConfigEntry<bool> MasterClientDismissQueue { get; private set; }

		public ConfigEntry<bool> UseTimeStampOnce { get; private set; }

		public ConfigEntry<bool> RestoreBoomboxes { get; private set; }

		public ConfigEntry<bool> AutoResume { get; private set; }

		public ConfigEntry<VisualizerPaused> VisualizerBehaviourPaused { get; private set; }

		private void Awake()
		{
			if ((Object)(object)instance == (Object)null)
			{
				instance = this;
			}
			data = new PersistentData();
			logger = Logger.CreateLogSource("Doppelclick.BoomboxCartUpgrade");
			logger.LogInfo((object)"BoomBoxCartMod loaded!");
			harmony.PatchAll();
			Task.Run(delegate
			{
				YoutubeDL.InitializeAsync().Wait();
			});
			OpenUIKey = ((BaseUnityPlugin)this).Config.Bind<Key>("General", "OpenUIKey", (Key)39, "Key to open the Boombox UI when grabbing a cart.");
			GlobalMuteKey = ((BaseUnityPlugin)this).Config.Bind<Key>("General", "GlobalMuteKey", (Key)27, "Key to mute all playback.");
			MasterClientDismissQueue = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "MasterClientDismissQueue", true, "Allow only the master client to dismiss the queue.");
			UseTimeStampOnce = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UseTimeStampOnce", false, "Only use the timestamp provided with a Url the first time the song is played.");
			RestoreBoomboxes = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RestoreBoomboxes", true, "Restore BoomBoxes and their Queues when you load back into a level.");
			AutoResume = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AutoResume", false, "Automatically resume playback when entering a new lobby.");
			VisualizerBehaviourPaused = ((BaseUnityPlugin)this).Config.Bind<VisualizerPaused>("General", "VisualizerPaused", VisualizerPaused.Hide, "Visualizer behaviour when music is paused.");
			logger.LogInfo((object)"BoomBoxCartMod initialization finished!");
		}

		private void OnDestroy()
		{
			YoutubeDL.CleanUp();
		}
	}
}
namespace BoomBoxCartMod.Util
{
	public class BaseListener : MonoBehaviourPunCallbacks
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		public int lastUserAmount = 0;

		private List<int> modList = new List<int>();

		public bool audioMuted = false;

		private bool mutePressed = false;

		public static bool downloaderAvailable = true;

		public List<int> GetAllModUsers()
		{
			return modList;
		}

		public void AddModUser(int id)
		{
			if (!modList.Contains(id))
			{
				modList.Add(id);
				SendModListUpdate();
			}
		}

		public void RemoveModUser(int id)
		{
			modList.Remove(id);
			SendModListUpdate();
		}

		private void SendModListUpdate()
		{
			if (PhotonNetwork.IsMasterClient && PhotonNetwork.IsMasterClient)
			{
				RPC(((MonoBehaviourPun)this).photonView, "UpdateModUsers", (RpcTarget)1, modList.ToArray(), PhotonNetwork.LocalPlayer.ActorNumber);
			}
		}

		private void Update()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (!mutePressed && Keyboard.current != null && ((ButtonControl)Keyboard.current[Instance.GlobalMuteKey.Value]).wasPressedThisFrame)
			{
				mutePressed = true;
				audioMuted = !audioMuted;
			}
			else if (mutePressed && (Keyboard.current == null || ((ButtonControl)Keyboard.current[Instance.GlobalMuteKey.Value]).wasReleasedThisFrame))
			{
				mutePressed = false;
			}
		}

		public static void RPC(PhotonView view, string methodName, RpcTarget target, params object[] parameters)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)view == (Object)null)
			{
				return;
			}
			if ((int)target == 0 || (int)target == 1)
			{
				List<int> allModUsers = Instance.baseListener.GetAllModUsers();
				Dictionary<int, Player> players = PhotonNetwork.CurrentRoom.Players;
				{
					foreach (KeyValuePair<int, Player> item in players)
					{
						if (allModUsers.Contains(item.Key) && ((int)target == 0 || item.Key != PhotonNetwork.LocalPlayer.ActorNumber))
						{
							view.RPC(methodName, item.Value, parameters);
						}
					}
					return;
				}
			}
			view.RPC(methodName, target, parameters);
		}

		public static void RPC(PhotonView view, string methodName, Player target, params object[] parameters)
		{
			if ((Object)(object)view != (Object)null && Instance.baseListener.GetAllModUsers().Contains(target.ActorNumber))
			{
				view.RPC(methodName, PhotonNetwork.MasterClient, parameters);
			}
		}

		public static void ReportDownloaderStatus(bool available)
		{
			if (available == downloaderAvailable)
			{
				return;
			}
			Instance.logger.LogInfo((object)("Downloader status: " + (available ? "Available" : "Unavailable")));
			downloaderAvailable = available;
			if (!available)
			{
				Instance.modDisabled = true;
			}
			if (PhotonNetwork.IsMasterClient)
			{
				if (available)
				{
					Instance.modDisabled = false;
					Instance.baseListener.AddModUser(PhotonNetwork.LocalPlayer.ActorNumber);
				}
				else
				{