Decompiled source of BoomboxCartUpgrade v1.3.4

BoomBoxCartUpgradeMod.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BoomBoxCartMod.Patches;
using BoomBoxCartMod.Util;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
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.4.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.4.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 AudioPlayer : MonoBehaviourPunCallbacks
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		public float minDistance = 3f;

		public float maxDistanceBase = 15f;

		public float maxDistanceAddition = 30f;

		private static int qualityLevel = 4;

		private AudioLowPassFilter lowPassFilter;

		public AudioSource audioSource;

		private static ManualLogSource Logger => Instance.logger;

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

		private void Update()
		{
			if (Instance.baseListener.audioMuted != audioSource.mute)
			{
				audioSource.mute = Instance.baseListener.audioMuted;
			}
		}

		public AudioClip GetClip()
		{
			return ((Object)(object)audioSource == (Object)null) ? null : audioSource.clip;
		}

		public void SetVolume(float volume)
		{
			audioSource.volume = volume;
			UpdateAudioRangeBasedOnVolume();
		}

		public void UpdateAudioRangeBasedOnVolume()
		{
			UpdateAudioRangeBasedOnVolume(audioSource.volume);
		}

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

		public void SetClip(AudioClip clip)
		{
			audioSource.Stop();
			audioSource.clip = clip;
			audioSource.time = 0f;
			SetQuality(qualityLevel);
			UpdateAudioRangeBasedOnVolume();
		}

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

		public static int GetQuality()
		{
			return qualityLevel;
		}

		public float GetTime()
		{
			return audioSource.time;
		}

		public void SetTime(float time)
		{
			audioSource.time = time;
		}

		public bool IsPlaying()
		{
			return audioSource.isPlaying;
		}

		public void Play()
		{
			audioSource.Play();
		}

		public void Pause()
		{
			audioSource.Pause();
		}

		public void Stop()
		{
			audioSource.Stop();
			audioSource.clip = null;
		}

		private void OnDestroy()
		{
			Object.Destroy((Object)(object)lowPassFilter);
			Object.Destroy((Object)(object)audioSource);
		}
	}
	public class Boombox : MonoBehaviourPunCallbacks
	{
		private class SharedAudioEntry
		{
			public string title;

			public string url;

			public double duration;

			public int startTime;
		}

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

			public AudioEntry currentSong = null;

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

			public bool isPlaying = false;

			public float absVolume = 0.6f;

			public float personalVolumePercentage = 0.35f;

			public bool loopQueue = false;

			public bool underglowEnabled = false;

			public bool visualizerEnabled = true;

			public bool pendingPlaybackStart = false;

			public int stateVersion = 0;

			public int playbackTime = 0;

			public int playbackStartTimestamp = 0;
		}

		public class AudioEntry
		{
			public string Title;

			public string Url;

			public double Duration = -1.0;

			public int StartTime = 0;

			public AudioEntry(string url, SongInfo info)
			{
				Url = url;
				Title = info.title;
				Duration = info.duration;
			}

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

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

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

		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		public PhotonView photonView;

		public AudioPlayer audioPlayer;

		public Visualizer visualizer;

		public VisualEffects visualEffects;

		public DownloadHelper downloadHelper = null;

		private bool syncFinished = false;

		public bool startPlayBackOnDownload = true;

		public BoomboxData data = new BoomboxData();

		private static bool mutePressed = false;

		private bool isApplyingSharedState = false;

		private string pendingCurrentSongDownloadUrl = null;

		private int lastAppliedSharedStateVersion = -1;

		private Hashtable lastSharedState = null;

		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()
		{
			audioPlayer = ((Component)this).gameObject.AddComponent<AudioPlayer>();
			downloadHelper = ((Component)this).gameObject.AddComponent<DownloadHelper>();
			photonView = ((Component)this).GetComponent<PhotonView>();
			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 = audioPlayer.audioSource;
			}
			if ((Object)(object)((Component)this).GetComponent<VisualEffects>() == (Object)null)
			{
				visualEffects = ((Component)this).gameObject.AddComponent<VisualEffects>();
			}
			PersistentData.SetBoomboxViewInitialized(photonView.ViewID);
			Logger.LogInfo((object)$"Boombox initialized on this cart. AudioPlayer: {audioPlayer}, PhotonView: {photonView}");
		}

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

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

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

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

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

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

		private Hashtable CreateSharedState(bool includeTime, bool updateQueue)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			//IL_0109: Expected O, but got Unknown
			Hashtable val = new Hashtable();
			((Dictionary<object, object>)val).Add((object)"propVersion", (object)data.stateVersion);
			((Dictionary<object, object>)val).Add((object)"IDKey", (object)data.key);
			((Dictionary<object, object>)val).Add((object)"isPlaying", (object)(data.currentSong != null && data.isPlaying));
			((Dictionary<object, object>)val).Add((object)"pendingPlaybackStart", (object)data.pendingPlaybackStart);
			((Dictionary<object, object>)val).Add((object)"absVolume", (object)data.absVolume);
			((Dictionary<object, object>)val).Add((object)"loopQueue", (object)data.loopQueue);
			((Dictionary<object, object>)val).Add((object)"underglow", (object)data.underglowEnabled);
			((Dictionary<object, object>)val).Add((object)"visualizer", (object)data.visualizerEnabled);
			((Dictionary<object, object>)val).Add((object)"currentSongIndex", (object)GetCurrentSongIndex());
			Hashtable val2 = val;
			if (includeTime)
			{
				((Dictionary<object, object>)(object)val2).Add((object)"playbackStartTimestamp", (object)data.playbackStartTimestamp);
			}
			if (updateQueue)
			{
				((Dictionary<object, object>)(object)val2).Add((object)"queue", (object)JsonConvert.SerializeObject((object)data.playbackQueue.Select((AudioEntry entry) => new SharedAudioEntry
				{
					title = entry.Title,
					url = entry.Url,
					duration = entry.Duration,
					startTime = entry.StartTime
				}).ToArray()));
				Logger.LogDebug((object)string.Format("Created table with queue size: {0}", JsonConvert.DeserializeObject<SharedAudioEntry[]>((string)val2[(object)"queue"]).Count()));
			}
			return val2;
		}

		private void LoadSharedStateFromRoom()
		{
			if (PhotonNetwork.IsConnected && PhotonNetwork.CurrentRoom != null && !((Object)(object)photonView == (Object)null))
			{
				Hashtable customProperties = ((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties;
				ApplyChangedValues(customProperties);
				if (PhotonNetwork.IsMasterClient && ((Dictionary<object, object>)(object)customProperties).Count > 0)
				{
					PublishSharedState(updateTime: true, updateQueue: true, force: true);
				}
			}
		}

		public override void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
		{
			((MonoBehaviourPunCallbacks)this).OnRoomPropertiesUpdate(propertiesThatChanged);
			if (!((Object)(object)photonView == (Object)null) && propertiesThatChanged != null)
			{
				ApplyChangedValues(propertiesThatChanged);
			}
		}

		private void ApplyChangedValues(Hashtable propertiesThatChanged)
		{
			isApplyingSharedState = true;
			if (!((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"propVersion"))
			{
				isApplyingSharedState = false;
				return;
			}
			int num = (int)propertiesThatChanged[(object)"propVersion"];
			if (num < data.stateVersion)
			{
				isApplyingSharedState = false;
				return;
			}
			int currentSongIndex = GetCurrentSongIndex();
			bool flag = ((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"queue");
			try
			{
				if (flag)
				{
					string text = (string)propertiesThatChanged[(object)"queue"];
					SharedAudioEntry[] array = JsonConvert.DeserializeObject<SharedAudioEntry[]>(text);
					if (array != null)
					{
						data.playbackQueue = array.Select(delegate(SharedAudioEntry entry)
						{
							string title = (string.IsNullOrWhiteSpace(entry.title) ? "Unknown Title" : entry.title);
							_ = entry.duration;
							double duration = entry.duration;
							string url = entry.url;
							SongInfo info = new SongInfo(title, duration);
							return new AudioEntry(url, info)
							{
								StartTime = entry.startTime
							};
						}).ToList();
						Logger.LogDebug((object)$"Updated queue to size {data.playbackQueue.Count}.");
					}
					else
					{
						data.playbackQueue = new List<AudioEntry>();
					}
				}
				if (((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"currentSongIndex"))
				{
					int num2 = (int)propertiesThatChanged[(object)"currentSongIndex"];
					if (num2 >= 0 && num2 < data.playbackQueue.Count)
					{
						data.currentSong = data.playbackQueue[num2];
					}
					else
					{
						data.currentSong = null;
						data.pendingPlaybackStart = false;
						audioPlayer.Stop();
						UpdateUIStatus("Ready to play music! Enter a Video URL");
						Logger.LogDebug((object)$"Invalid currentsong index: {num2}.");
					}
				}
				else if (flag)
				{
					data.currentSong = data.playbackQueue[currentSongIndex];
				}
				if (((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"pendingPlaybackStart"))
				{
					bool pendingPlaybackStart = (bool)propertiesThatChanged[(object)"pendingPlaybackStart"];
					data.pendingPlaybackStart = pendingPlaybackStart;
				}
				if (((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"isPlaying"))
				{
					bool flag2 = (bool)propertiesThatChanged[(object)"isPlaying"];
					data.isPlaying = data.currentSong != null && flag2;
				}
				if (((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"absVolume"))
				{
					float value = (float)propertiesThatChanged[(object)"absVolume"];
					float num3 = Mathf.Clamp01(Convert.ToSingle(value));
					if (data.absVolume != num3)
					{
						data.absVolume = num3;
						float volume = num3 * data.personalVolumePercentage;
						audioPlayer.SetVolume(volume);
					}
				}
				if (((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"loopQueue"))
				{
					bool loopQueue = (bool)propertiesThatChanged[(object)"loopQueue"];
					data.loopQueue = loopQueue;
				}
				if (((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"underglow") && Instance.SyncVisuals.Value)
				{
					bool underglowEnabled = (bool)propertiesThatChanged[(object)"underglow"];
					data.underglowEnabled = underglowEnabled;
					ApplyVisualStateFromData();
					((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
				}
				if (((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"visualizer") && Instance.SyncVisuals.Value)
				{
					bool visualizerEnabled = (bool)propertiesThatChanged[(object)"visualizer"];
					data.visualizerEnabled = visualizerEnabled;
					ApplyVisualStateFromData();
					((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
				}
				if (((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"playbackStartTimestamp"))
				{
					int num4 = (int)propertiesThatChanged[(object)"playbackStartTimestamp"];
					data.playbackStartTimestamp = num4;
					SetPlaybackTime(num4);
				}
				if (((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)"IDKey"))
				{
					string text2 = (string)propertiesThatChanged[(object)"IDKey"];
					if (!string.IsNullOrWhiteSpace(text2))
					{
						data.key = text2;
					}
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Error occured while reading properties: " + ex.Message));
			}
			ApplySharedPlaybackState();
			((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
			syncFinished = true;
			isApplyingSharedState = false;
		}

		private void ApplySharedPlaybackState()
		{
			if (data.currentSong?.Url == null)
			{
				Logger.LogDebug((object)("ApplySharedPlaybackState: No current song" + ((data.currentSong == null) ? "" : "URL") + "!"));
				data.pendingPlaybackStart = false;
				audioPlayer.Stop();
				UpdateUIStatus("Ready to play music! Enter a Video URL");
				return;
			}
			AudioClip val = data.currentSong?.GetAudioClip();
			if ((Object)(object)val == (Object)null)
			{
				audioPlayer.Stop();
				startPlayBackOnDownload = data.pendingPlaybackStart;
				if (data.pendingPlaybackStart)
				{
					UpdateUIStatus("Loading: " + data.currentSong.Title);
				}
				if (!data.pendingPlaybackStart)
				{
					EnsureCurrentSongDownloaded();
				}
				return;
			}
			if ((Object)(object)audioPlayer.GetClip() != (Object)(object)val)
			{
				audioPlayer.Stop();
				audioPlayer.audioSource.clip = val;
				audioPlayer.SetQuality(AudioPlayer.GetQuality());
				audioPlayer.UpdateAudioRangeBasedOnVolume();
			}
			ApplyVisualStateFromData();
			if (data.pendingPlaybackStart)
			{
				if (audioPlayer.IsPlaying())
				{
					audioPlayer.Pause();
				}
				UpdateUIStatus("Loading: " + data.currentSong.Title);
			}
			else if (data.isPlaying)
			{
				if (!audioPlayer.IsPlaying())
				{
					Logger.LogDebug((object)$"ApplySharedPlaybackState: Starting playback - timestamp={data.playbackStartTimestamp}");
					audioPlayer.Play();
					SetPlaybackTime(data.playbackStartTimestamp);
					Logger.LogDebug((object)$"ApplySharedPlaybackState: After Play(), audioPlayer.GetTime()={audioPlayer.GetTime()}");
				}
				UpdateUIStatus("Now playing: " + data.currentSong.Title);
			}
			else
			{
				if (audioPlayer.IsPlaying())
				{
					audioPlayer.Pause();
				}
				UpdateUIStatus("Ready to play: " + data.currentSong.Title);
			}
		}

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

		private async void EnsureCurrentSongDownloaded()
		{
			string url = data.currentSong?.Url;
			if (string.IsNullOrWhiteSpace(url) || (Object)(object)downloadHelper == (Object)null || DownloadHelper.downloadedClips.ContainsKey(url) || pendingCurrentSongDownloadUrl == url)
			{
				return;
			}
			pendingCurrentSongDownloadUrl = url;
			try
			{
				if (await downloadHelper.StartAudioDownload(url))
				{
					BaseListener.RPC(photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber);
				}
			}
			finally
			{
				if (pendingCurrentSongDownloadUrl == url)
				{
					pendingCurrentSongDownloadUrl = null;
				}
			}
			if (!((Object)(object)this == (Object)null) && !(data.currentSong?.Url != url))
			{
				ApplySharedPlaybackState();
			}
		}

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

		public bool CanPublish()
		{
			return !isApplyingSharedState && PhotonNetwork.IsConnected && PhotonNetwork.CurrentRoom != null && (Object)(object)photonView != (Object)null;
		}

		public void PublishSharedState(bool updateTime, bool updateQueue = false, bool force = false)
		{
			if (CanPublish())
			{
				if (PhotonNetwork.IsMasterClient)
				{
					data.stateVersion++;
				}
				Hashtable val = CreateSharedState(updateTime, updateQueue);
				PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
				lastSharedState = val;
				syncFinished = true;
			}
		}

		private void StopLocalPlayback(bool updateSharedFlag)
		{
			audioPlayer.Stop();
			if (updateSharedFlag)
			{
				TogglePlaying(value: false);
			}
		}

		private void SetPlaybackReferenceFromSeconds(float totalSeconds)
		{
			float num = Math.Max(0f, totalSeconds);
			data.playbackStartTimestamp = (int)((double)GetCurrentServerTimeMilliseconds() - Math.Round(num * 1000f));
			Logger.LogDebug((object)$"SetPlaybackReferenceFromSeconds: Set to {num}s, timestamp={data.playbackStartTimestamp}");
		}

		private float GetTrackedPlaybackSeconds()
		{
			if ((Object)(object)audioPlayer?.GetClip() != (Object)null)
			{
				if (!audioPlayer.IsPlaying() && data.playbackTime > 0 && audioPlayer.GetTime() <= 0f)
				{
					Logger.LogDebug((object)$"GetTrackedPlaybackSeconds: Returning cached playbackTime={data.playbackTime} (not playing, time <= 0)");
					return data.playbackTime;
				}
				float time = audioPlayer.GetTime();
				Logger.LogDebug((object)$"GetTrackedPlaybackSeconds: Returning audioPlayer.GetTime()={time}, isPlaying={audioPlayer.IsPlaying()}");
				return time;
			}
			if (data.playbackTime > 0)
			{
				Logger.LogDebug((object)$"GetTrackedPlaybackSeconds: Returning cached playbackTime={data.playbackTime} (no clip)");
				return data.playbackTime;
			}
			float num = Math.Max(0f, (float)(GetCurrentServerTimeMilliseconds() - data.playbackStartTimestamp) / 1000f);
			Logger.LogDebug((object)$"GetTrackedPlaybackSeconds: Calculating from timestamp: {num}s");
			return num;
		}

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

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

		public void SetPlaybackTime(long relativeStartTimeMillis)
		{
			float num = Math.Max(0f, (float)(GetCurrentServerTimeMilliseconds() - relativeStartTimeMillis) / 1000f);
			Logger.LogDebug((object)$"SetPlaybackTime: Setting audioPlayer time to {num}s (from timestamp {relativeStartTimeMillis})");
			audioPlayer.SetTime(num);
		}

		public void EnqueueSongLocal(string url, int seconds)
		{
			if (string.IsNullOrWhiteSpace(url))
			{
				return;
			}
			if (ShouldRequestMasterMutation())
			{
				BaseListener.RPC(photonView, "RequestEnqueueSong", (RpcTarget)2, url, seconds, PhotonNetwork.LocalPlayer.ActorNumber);
				return;
			}
			SongInfo info = (DownloadHelper.songInfo.ContainsKey(url) ? DownloadHelper.songInfo[url] : new SongInfo());
			AudioEntry audioEntry = new AudioEntry(url, info)
			{
				StartTime = seconds
			};
			data.playbackQueue.Add(audioEntry);
			bool flag = data.currentSong == null;
			if (flag)
			{
				data.currentSong = audioEntry;
				data.playbackTime = 0;
				Logger.LogDebug((object)$"Set currentsong local {data.currentSong}");
				StopLocalPlayback(updateSharedFlag: true);
				data.pendingPlaybackStart = true;
				startPlayBackOnDownload = true;
				SetPlaybackReferenceFromSeconds(audioEntry.UseStartTime(this));
			}
			PublishSharedState(flag, updateQueue: true);
			if (PhotonNetwork.IsMasterClient)
			{
				downloadHelper.EnqueueDownload(url);
				downloadHelper.StartDownloadJob();
			}
		}

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

		public void SetPlaybackStateLocal(bool startPlaying)
		{
			if (data.currentSong != null)
			{
				if (ShouldRequestMasterMutation())
				{
					BaseListener.RPC(photonView, "RequestSetPlaybackState", (RpcTarget)2, startPlaying, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				else if ((Object)(object)data.currentSong.GetAudioClip() == (Object)null)
				{
					startPlayBackOnDownload = startPlaying;
					data.isPlaying = false;
					data.pendingPlaybackStart = startPlaying;
					PublishSharedState(updateTime: true);
				}
				else
				{
					float trackedPlaybackSeconds = GetTrackedPlaybackSeconds();
					Logger.LogDebug((object)$"SetPlaybackStateLocal: trackedPlaybackSeconds={trackedPlaybackSeconds}, isPlaying={startPlaying}");
					SetPlaybackReferenceFromSeconds(trackedPlaybackSeconds);
					Logger.LogDebug((object)$"SetPlaybackStateLocal: Final state - isPlaying={startPlaying}, timestamp={data.playbackStartTimestamp}");
					data.isPlaying = startPlaying;
					data.pendingPlaybackStart = false;
					PublishSharedState(updateTime: true, updateQueue: false, force: true);
				}
			}
		}

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

		public void SelectSongIndex(int index)
		{
			if (index < 0 || index >= data.playbackQueue.Count)
			{
				return;
			}
			if (ShouldRequestMasterMutation())
			{
				BaseListener.RPC(photonView, "RequestSelectSongIndex", (RpcTarget)2, index, PhotonNetwork.LocalPlayer.ActorNumber);
				return;
			}
			data.currentSong = data.playbackQueue[index];
			StopLocalPlayback(updateSharedFlag: true);
			data.playbackTime = 0;
			data.pendingPlaybackStart = true;
			startPlayBackOnDownload = true;
			PublishSharedState(updateTime: true);
			if (PhotonNetwork.IsMasterClient)
			{
				string text = data.currentSong?.Url;
				if (text != null && DownloadHelper.downloadsReady.ContainsKey(text) && DownloadHelper.downloadsReady[text].Count >= Instance.baseListener.GetAllModUsers().Count && data.pendingPlaybackStart)
				{
					Logger.LogDebug((object)"Skipping download queue, as all users are ready to play.");
					FinalizePendingPlaybackStart(startPlayBackOnDownload);
				}
				else
				{
					downloadHelper.DismissDownloadQueue();
					downloadHelper.DownloadQueue(index);
				}
			}
		}

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

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

		public void SetUnderglowEnabledLocal(bool enabled)
		{
			if (Instance.SyncVisuals.Value)
			{
				if (ShouldRequestMasterMutation())
				{
					BaseListener.RPC(photonView, "RequestSetUnderglowEnabled", (RpcTarget)2, enabled, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				else
				{
					data.underglowEnabled = enabled;
					ApplyVisualStateFromData();
					((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
					PublishSharedState(updateTime: false);
				}
			}
			else
			{
				data.underglowEnabled = enabled;
				ApplyVisualStateFromData();
				((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
			}
		}

		public void SetVisualizerEnabledLocal(bool enabled)
		{
			if (Instance.SyncVisuals.Value)
			{
				if (ShouldRequestMasterMutation())
				{
					BaseListener.RPC(photonView, "RequestSetVisualizerEnabled", (RpcTarget)2, enabled, PhotonNetwork.LocalPlayer.ActorNumber);
				}
				else
				{
					data.visualizerEnabled = enabled;
					ApplyVisualStateFromData();
					((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
					PublishSharedState(updateTime: false);
				}
			}
			else
			{
				data.visualizerEnabled = enabled;
				ApplyVisualStateFromData();
				((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
			}
		}

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

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

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

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

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

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

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

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

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

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

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

		[PunRPC]
		public void RequestSetUnderglowEnabled(bool enabled, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient && Instance.SyncVisuals.Value)
			{
				SetUnderglowEnabledLocal(enabled);
			}
		}

		[PunRPC]
		public void RequestSetVisualizerEnabled(bool enabled, int requesterId)
		{
			if (PhotonNetwork.IsMasterClient && Instance.SyncVisuals.Value)
			{
				SetVisualizerEnabledLocal(enabled);
			}
		}

		[PunRPC]
		public void RequestDismissQueue(int requesterId)
		{
			if (PhotonNetwork.IsMasterClient && (!Instance.MasterClientDismissQueue.Value || PhotonNetwork.MasterClient.ActorNumber == requesterId))
			{
				DismissQueueLocal();
			}
		}

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

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

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

		public double EstimateDownloadTimeSeconds(double durationSeconds, int downloadSpeedMbps)
		{
			int bitrateKbps = DownloadHelper.GetBitrateKbps(ApplyQualityToDownloads ? 4 : AudioPlayer.GetQuality());
			double num = (double)bitrateKbps * 1000.0 / 8.0 * durationSeconds;
			double num2 = (double)Math.Max(1, downloadSpeedMbps) / 8.0 * 1024.0 * 1024.0;
			double num3 = num / num2 * 2.5;
			double num4 = durationSeconds * 0.005;
			return (5.0 + num3 + num4) * 1.3;
		}

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

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

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

		private void OnDestroy()
		{
			Instance.data.GetAllBoomboxes().Remove(this);
			Object.Destroy((Object)(object)((Component)this).GetComponent<BoomboxUI>());
			if ((Object)(object)visualizer != (Object)null)
			{
				Object.Destroy((Object)(object)visualizer);
			}
			Object.Destroy((Object)(object)((Component)this).gameObject.GetComponent<VisualEffects>());
			Object.Destroy((Object)(object)audioPlayer);
			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}");
				startPlayBackOnDownload = true;
				data.pendingPlaybackStart = false;
				UpdateUIStatus("Ready to play music! Enter a Video URL");
				CleanupCurrentPlayback();
				downloadHelper.DismissDownloadQueue();
				List<BoomboxData> boomboxData = Instance.data.GetBoomboxData();
				int index = boomboxData.Count;
				if (boomboxData.Contains(data))
				{
					index = boomboxData.IndexOf(data);
					boomboxData.Remove(data);
				}
				int stateVersion = data.stateVersion;
				data = new BoomboxData();
				data.stateVersion = stateVersion + 1;
				ApplyVisualStateFromData();
				if (Instance.RestoreBoomboxes.Value)
				{
					boomboxData.Insert(index, data);
				}
				PublishSharedState(updateTime: true, updateQueue: true, force: true);
			}
		}
	}
	public class BoomboxController : MonoBehaviourPun
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		private PhysGrabCart cart;

		private BoomboxUI boomboxUI;

		private Boombox boombox;

		private int currentControllerId = -1;

		private static ManualLogSource Logger => Instance.logger;

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

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

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

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

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

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

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

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

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

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

		public PhotonView photonView;

		public static bool anyUISHown = false;

		public bool showUI = false;

		private string urlInput = "";

		private bool isTimeSliderBeingDragged = false;

		private int songIndexForTime = -2;

		private float songTimePerc = 0f;

		private float lastSentSongTimePerc = -1f;

		private float lastSentVolume = 0.3f;

		private bool isVolumeSliderBeingDragged = false;

		private bool isIndividualVolumeBeingDragged = false;

		private int qualityLevel = 3;

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

		private bool isQualitySliderBeingDragged = false;

		private int lastSentQualityLevel = 3;

		private Rect windowRect;

		private Boombox boombox;

		private BoomboxController controller;

		private VisualEffects visualEffects;

		private Visualizer visualizer;

		private GUIStyle windowStyle;

		private GUIStyle headerStyle;

		private GUIStyle buttonStyle;

		private GUIStyle smallButtonStyle;

		private GUIStyle textFieldStyle;

		private GUIStyle labelStyle;

		private GUIStyle sliderStyle;

		private GUIStyle statusStyle;

		private GUIStyle scrollViewStyle;

		private GUIStyle queueHeaderStyle;

		private GUIStyle queueEntryStyle;

		private GUIStyle currentSongStyle;

		private Texture2D backgroundTexture;

		private Texture2D buttonTexture;

		private Texture2D sliderBackgroundTexture;

		private Texture2D sliderThumbTexture;

		private Texture2D textFieldBackgroundTexture;

		private Vector2 urlScrollPosition = Vector2.zero;

		private float textFieldVisibleWidth = 350f;

		private string errorMessage = "";

		private float errorMessageTime = 0f;

		public string statusMessage = "";

		private CursorLockMode previousLockMode;

		private bool previousCursorVisible;

		private bool stylesInitialized = false;

		private Vector2 scrollPosition = Vector2.zero;

		private Vector2 queueScrollPosition = Vector2.zero;

		private const float refreshHoldTime = 5f;

		private const int maxRefreshSymbols = 5;

		private float? refreshObjectStart = null;

		private bool refreshObjectSent = false;

		private string lastUrl = null;

		private static ManualLogSource Logger => Instance.logger;

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

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

		public void ShowUI()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (!showUI)
			{
				if ((Object)(object)boombox == (Object)null || (Object)(object)photonView == (Object)null)
				{
					Logger.LogError((object)"Cannot show UI - boombox or photonView is null");
					return;
				}
				anyUISHown = true;
				showUI = true;
				previousLockMode = Cursor.lockState;
				previousCursorVisible = Cursor.visible;
				Cursor.visible = true;
				Cursor.lockState = (CursorLockMode)0;
				UpdateDataFromBoomBox();
				UpdateStatusFromBoombox();
			}
		}

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

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

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

		private void SendTimeUpdate()
		{
			if (songTimePerc != lastSentSongTimePerc)
			{
				lastSentSongTimePerc = songTimePerc;
				if (boombox?.GetCurrentSongIndex() == songIndexForTime && songIndexForTime != -1 && (Object)(object)boombox?.audioPlayer?.GetClip() != (Object)null && boombox.audioPlayer.GetClip().length > 0f)
				{
					float time = Math.Max(0f, Math.Min(songTimePerc * boombox.audioPlayer.GetClip().length, boombox.audioPlayer.GetClip().length - 0.05f));
					boombox.audioPlayer.SetTime(time);
					boombox.CommitPlaybackSeek();
				}
				else if (songIndexForTime != boombox.GetCurrentSongIndex())
				{
					UpdateDataFromBoomBox();
				}
			}
		}

		private void SendVolumeUpdate()
		{
			if (boombox.data.absVolume != lastSentVolume)
			{
				lastSentVolume = boombox.data.absVolume;
				boombox.SetVolumeLocal(boombox.data.absVolume);
			}
		}

		private void SendQualityUpdate()
		{
			if (qualityLevel != lastSentQualityLevel)
			{
				lastSentQualityLevel = qualityLevel;
				if ((Object)(object)boombox != (Object)null)
				{
					boombox.audioPlayer?.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_01e2: 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_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0647: Unknown result type (might be due to invalid IL or missing references)
			//IL_0669: Unknown result type (might be due to invalid IL or missing references)
			//IL_06dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0814: Unknown result type (might be due to invalid IL or missing references)
			//IL_081b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0820: Unknown result type (might be due to invalid IL or missing references)
			//IL_0829: Unknown result type (might be due to invalid IL or missing references)
			//IL_0930: 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_093c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0945: 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 = "??";
			bool flag = (Object)(object)boombox != (Object)null && (Object)(object)boombox.audioPlayer?.GetClip() != (Object)null;
			if (flag)
			{
				num = boombox.audioPlayer.audioSource.time;
				num2 = boombox.audioPlayer.GetClip().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 num4 = GUILayout.HorizontalSlider(num3, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			int currentSongIndex = boombox.GetCurrentSongIndex();
			if (flag && currentSongIndex != -1 && num4 != num3)
			{
				if (!isTimeSliderBeingDragged)
				{
					isTimeSliderBeingDragged = true;
				}
				if (songIndexForTime == -2)
				{
					songIndexForTime = currentSongIndex;
				}
				if (songIndexForTime == currentSongIndex)
				{
					float time = Math.Max(0f, Math.Min(num4 * num2, boombox.audioPlayer.GetClip().length - 0.05f));
					boombox.audioPlayer.audioSource.time = time;
					songTimePerc = num4;
				}
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("<<", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(40f),
				GUILayout.Height(40f)
			}) && (Object)(object)boombox != (Object)null && boombox.data.currentSong != null)
			{
				boombox.JumpPlaybackBySeconds(-10f);
			}
			string text2 = ((boombox?.data != null && boombox.data.playbackQueue.Count > 0) ? "+ ENQUEUE" : "▶ PLAY");
			if (GUILayout.Button(text2, buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }))
			{
				var (text3, seconds) = IsValidVideoUrl(urlInput);
				if (string.IsNullOrEmpty(text3))
				{
					ShowErrorMessage("Invalid Video URL!");
				}
				else if (lastUrl != text3)
				{
					lastUrl = text3;
					boombox.EnqueueSongLocal(text3, seconds);
					GUI.FocusControl((string)null);
				}
			}
			string text4 = ((boombox.data.currentSong == null) ? "..." : (boombox.data.isPlaying ? "▌▌ PAUSE" : "▶ RESUME"));
			if (GUILayout.Button(text4, buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }) && boombox.data.currentSong != null)
			{
				boombox.SetPlaybackStateLocal(!boombox.data.isPlaying);
			}
			if (GUILayout.Button(">>", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(40f),
				GUILayout.Height(40f)
			}) && (Object)(object)boombox != (Object)null && boombox.data.currentSong != null)
			{
				boombox.JumpPlaybackBySeconds(10f);
			}
			GUILayout.EndHorizontal();
			if ((Object)(object)boombox != (Object)null && boombox.downloadHelper.IsProcessingQueue() && (Object)(object)boombox.data.currentSong?.GetAudioClip() == (Object)null && boombox.downloadHelper.GetCurrentDownloadUrl() != null)
			{
				GUILayout.Space(10f);
				GUILayout.Label("Download in progress...", statusStyle, Array.Empty<GUILayoutOption>());
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				GUILayout.FlexibleSpace();
				if (GUILayout.Button("Force Cancel Download", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2]
				{
					GUILayout.Width(200f),
					GUILayout.Height(30f)
				}))
				{
					boombox.downloadHelper.ForceCancelDownload();
				}
				GUILayout.FlexibleSpace();
				GUILayout.EndHorizontal();
			}
			GUILayout.Space(15f);
			if (!string.IsNullOrEmpty(statusMessage))
			{
				GUILayout.Label(statusMessage, statusStyle, Array.Empty<GUILayoutOption>());
				GUILayout.Space(5f);
			}
			if (!string.IsNullOrEmpty(errorMessage))
			{
				GUI.color = Color.red;
				GUILayout.Label(errorMessage, labelStyle, Array.Empty<GUILayoutOption>());
				GUI.color = Color.white;
				GUILayout.Space(5f);
			}
			GUILayout.Space(15f);
			float num5 = boombox.data.absVolume * 100f;
			GUILayout.Label($"Volume: {Mathf.Round(num5)}%", 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 num6 = GUILayout.HorizontalSlider(boombox.data.absVolume, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			if (num6 != boombox.data.absVolume)
			{
				if (!isVolumeSliderBeingDragged)
				{
					isVolumeSliderBeingDragged = true;
				}
				if ((Object)(object)boombox.audioPlayer?.audioSource != (Object)null)
				{
					float volume = boombox.data.absVolume * boombox.data.personalVolumePercentage;
					boombox.audioPlayer.SetVolume(volume);
				}
				boombox.data.absVolume = num6;
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(15f);
			GUILayout.Label($"Personal Volume Multiplier: {Mathf.Round(boombox.data.personalVolumePercentage * 100.001f)}%", labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if ((int)Event.current.type == 0)
			{
				lastRect = GUILayoutUtility.GetLastRect();
				if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition))
				{
					isIndividualVolumeBeingDragged = true;
				}
			}
			float num7 = GUILayout.HorizontalSlider(boombox.data.personalVolumePercentage, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			if (num7 != boombox.data.personalVolumePercentage)
			{
				isIndividualVolumeBeingDragged = true;
				boombox.data.personalVolumePercentage = num7;
				if ((Object)(object)boombox.audioPlayer?.audioSource != (Object)null)
				{
					boombox.audioPlayer.SetVolume(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 num8 = GUILayout.HorizontalSlider((float)qualityLevel, 0f, 4f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>());
			int num9 = Mathf.RoundToInt(num8);
			if (num9 != qualityLevel && !isQualitySliderBeingDragged)
			{
				isQualitySliderBeingDragged = true;
			}
			qualityLevel = num9;
			if ((Object)(object)boombox?.audioPlayer != (Object)null)
			{
				boombox.audioPlayer.SetQuality(qualityLevel);
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(10f);
			bool applyQualityToDownloads = Boombox.ApplyQualityToDownloads;
			bool flag2 = GUILayout.Toggle(applyQualityToDownloads, "Apply Quality Setting to Downloads", Array.Empty<GUILayoutOption>());
			if (flag2 != applyQualityToDownloads)
			{
				Boombox.ApplyQualityToDownloads = flag2;
			}
			GUILayout.Space(10f);
			bool monstersCanHearMusic = Boombox.MonstersCanHearMusic;
			bool flag3 = GUILayout.Toggle(monstersCanHearMusic, "Monsters can hear audio", Array.Empty<GUILayoutOption>());
			if (flag3 != monstersCanHearMusic)
			{
				Boombox.MonstersCanHearMusic = flag3;
			}
			GUILayout.Space(10f);
			bool loopQueue = boombox.LoopQueue;
			bool flag4 = GUILayout.Toggle(loopQueue, "Loop queue", Array.Empty<GUILayoutOption>());
			if (flag4 != loopQueue && PhotonNetwork.IsMasterClient)
			{
				boombox.SetLoopQueueLocal(flag4);
			}
			GUILayout.Space(10f);
			bool underglowEnabled = boombox.data.underglowEnabled;
			bool flag5 = GUILayout.Toggle(underglowEnabled, "RGB Underglow enabled", Array.Empty<GUILayoutOption>());
			if (flag5 != underglowEnabled)
			{
				boombox.SetUnderglowEnabledLocal(flag5);
				UpdateDataFromBoomBox();
			}
			GUILayout.Space(10f);
			bool visualizerEnabled = boombox.data.visualizerEnabled;
			bool flag6 = GUILayout.Toggle(visualizerEnabled, "Audio Visualizer enabled", Array.Empty<GUILayoutOption>());
			if (flag6 != visualizerEnabled)
			{
				boombox.SetVisualizerEnabledLocal(flag6);
				UpdateDataFromBoomBox();
			}
			GUILayout.EndScrollView();
		}

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

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

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

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

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

		private void OnDestroy()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (showUI)
			{
				anyUISHown = false;
				showUI = false;
				Cursor.lockState = previousLockMode;
				Cursor.visible = previousCursorVisible;
			}
			if ((Object)(object)backgroundTexture != (Object)null)
			{
				Object.Destroy((Object)(object)backgroundTexture);
			}
			if ((Object)(object)buttonTexture != (Object)null)
			{
				Object.Destroy((Object)(object)buttonTexture);
			}
			if ((Object)(object)sliderBackgroundTexture != (Object)null)
			{
				Object.Destroy((Object)(object)sliderBackgroundTexture);
			}
			if ((Object)(object)sliderThumbTexture != (Object)null)
			{
				Object.Destroy((Object)(object)sliderThumbTexture);
			}
			if ((Object)(object)textFieldBackgroundTexture != (Object)null)
			{
				Object.Destroy((Object)(object)textFieldBackgroundTexture);
			}
		}
	}
	public class DownloadHelper : MonoBehaviourPunCallbacks
	{
		private static BoomBoxCartMod Instance = BoomBoxCartMod.instance;

		private Boombox boomboxParent = null;

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

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

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

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

		public const int INITIAL_DOWNLOAD_TIMEOUT = 11;

		private const int TIMEOUT_THRESHOLD = 10;

		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 int GetBitrateKbps(string quality)
		{
			if (1 == 0)
			{
			}
			int result = quality switch
			{
				"32K" => 32, 
				"64K" => 64, 
				"96K" => 96, 
				"128K" => 128, 
				_ => 192, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		public static int GetBitrateKbps(int qualityLevel)
		{
			if (1 == 0)
			{
			}
			int result = qualityLevel switch
			{
				0 => 32, 
				1 => 64, 
				2 => 96, 
				3 => 128, 
				_ => 192, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		public static double EstimateSizeBytes(int bitrateKbps, double durationSeconds)
		{
			return (double)bitrateKbps * 1000.0 / 8.0 * durationSeconds;
		}

		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)
			{
				if (!downloadErrors.ContainsKey(url))
				{
					downloadErrors[url] = new HashSet<int>();
				}
				if (!downloadErrors[url].Contains(actorNumber))
				{
					downloadErrors[url].Add(actorNumber);
					Logger.LogError((object)$"Player {actorNumber} reported download error for {url}: {errorMessage}");
				}
			}
		}

		[PunRPC]
		public void SetSongInfo(string url, string title, double duration, int actorNumber)
		{
			if (!PhotonNetwork.IsMasterClient || string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(title))
			{
				return;
			}
			if (PhotonNetwork.IsMasterClient && DownloadHelper.songInfo.ContainsKey(url) && actorNumber != PhotonNetwork.LocalPlayer.ActorNumber)
			{
				SongInfo songInfo = DownloadHelper.songInfo[url];
				if (!songInfo.IsInvalid())
				{
					return;
				}
			}
			DownloadHelper.songInfo[url] = new SongInfo(title, duration);
			bool flag = false;
			if (boomboxParent?.data?.playbackQueue != null)
			{
				foreach (Boombox.AudioEntry item in boomboxParent.data.playbackQueue.FindAll((Boombox.AudioEntry entry) => entry.Url == url))
				{
					if (item.Title != title)
					{
						item.Title = title;
						flag = true;
					}
					if (item.Duration != duration)
					{
						item.Duration = duration;
					}
					flag = true;
				}
			}
			if (boomboxParent?.data?.currentSong != null && boomboxParent.data.currentSong.Url == url)
			{
				boomboxParent.data.currentSong.Title = title;
				if (boomboxParent.data.pendingPlaybackStart)
				{
					boomboxParent.UpdateUIStatus("Loading: " + title);
				}
				else if ((Object)(object)boomboxParent.data.currentSong.GetAudioClip() != (Object)null && boomboxParent.data.isPlaying)
				{
					boomboxParent.UpdateUIStatus("Now playing: " + title);
				}
				else if ((Object)(object)boomboxParent.data.currentSong.GetAudioClip() != (Object)null)
				{
					boomboxParent.UpdateUIStatus("Ready to play: " + title);
				}
				else
				{
					boomboxParent.UpdateUIStatus("Loading: " + title);
				}
			}
			if (flag)
			{
				Boombox boombox = boomboxParent;
				if (boombox != null)
				{
					((Component)boombox).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox();
				}
				if (PhotonNetwork.IsMasterClient)
				{
					boomboxParent.PublishSharedState(updateTime: false, updateQueue: true);
				}
			}
		}

		private IEnumerator ProcessDownloadQueue()
		{
			isProcessingQueue = true;
			Logger.LogDebug((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.LogDebug((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.pendingPlaybackStart && url == boomboxParent.data.currentSong?.Url)
					{
						boomboxParent.FinalizePendingPlaybackStart(boomboxParent.startPlayBackOnDownload);
					}
					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.LogDebug((object)("Consensus finished for " + url + ", Starting Playback."));
				Boombox boombox = boomboxParent;
				if (boombox != null && (boombox.data?.pendingPlaybackStart).GetValueOrDefault() && url == boomboxParent.data.currentSong?.Url)
				{
					boomboxParent.FinalizePendingPlaybackStart(boomboxParent.startPlayBackOnDownload);
				}
			}
		}

		private int SongTimeout(string url)
		{
			double num = songInfo[url].duration;
			if (num < 0.0)
			{
				num = 180.0;
			}
			double a = boomboxParent.EstimateDownloadTimeSeconds(num, Instance.DownloadSpeed.Value);
			return (int)Math.Round(a);
		}

		private IEnumerator DownloadTimeoutCoroutine(string requestId, string url)
		{
			int num;
			if (songInfo.ContainsKey(url))
			{
				_ = songInfo[url].duration;
				num = 0;
			}
			else
			{
				num = 1;
			}
			if (num != 0)
			{
				yield return (object)new WaitForSeconds(11f);
			}
			while (YoutubeDL.isInitializing)
			{
				yield return (object)new WaitForSeconds(1f);
			}
			int num2;
			if (songInfo.ContainsKey(url))
			{
				_ = songInfo[url].duration;
				num2 = 1;
			}
			else
			{
				num2 = 0;
			}
			if (num2 != 0)
			{
				int timeout = SongTimeout(url);
				Logger.LogDebug((object)$"Using download timeout: {timeout}");
				yield return (object)new WaitForSeconds((float)timeout);
			}
			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.data.pendingPlaybackStart && boomboxParent.data.currentSong?.Url == url)
					{
						boomboxParent.FinalizePendingPlaybackStart(boomboxParent.startPlayBackOnDownload);
					}
				}
				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 || requesterId != PhotonNetwork.MasterClient.ActorNumber)
			{
				return;
			}
			if (currentDownloadUrl != url)
			{
				currentDownloadUrl = url;
				if (!downloadsReady.ContainsKey(url))
				{
					downloadsReady[url] = new HashSet<int>();
				}
				if (!downloadErrors.ContainsKey(url))
				{
					downloadErrors[url] = new HashSet<int>();
				}
			}
			if (downloadedClips.ContainsKey(url))
			{
				boomboxParent?.HandleDownloadedCurrentSong();
				BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber);
			}
			else if (await StartAudioDownload(url) && (Object)(object)this != (Object)null)
			{
				boomboxParent?.HandleDownloadedCurrentSong();
				BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber);
			}
		}

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

		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, SongInfo info)
		{
			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 = 11;
				((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))
			{
				return true;
			}
			try
			{
				SongInfo info = (songInfo.ContainsKey(url) ? songInfo[url] : null);
				if (info?.IsInvalid() ?? true)
				{
					SongInfo tempInfo = await YoutubeDL.DownloadAudioInfoAsync(url);
					if (!songInfo.ContainsKey(url) || songInfo[url].IsInvalid())
					{
						songInfo[url] = tempInfo;
						info = tempInfo;
						if (!tempInfo.IsInvalid())
						{
							BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetSongInfo", (RpcTarget)0, url, info.title, info.duration, PhotonNetwork.LocalPlayer.ActorNumber);
						}
					}
				}
				string filePath = await YoutubeDL.DownloadAudioAsync(url, info);
				if (boomboxParent?.data?.currentSong?.Url == url)
				{
					boomboxParent?.UpdateUIStatus("Processing audio: " + info.title);
				}
				AudioClip clip = await GetAudioClipAsync(filePath, info);
				downloadedClips[url] = clip;
				Logger.LogDebug((object)("Downloaded and cached clip for video: " + info.title));
				if ((Object)(object)clip != (Object)null && info.IsInvalid())
				{
					SongInfo newInfo = await YoutubeDL.DownloadAudioInfoAsync(url);
					if (!newInfo.IsInvalid() && !songInfo.ContainsKey(url))
					{
						songInfo[url] = newInfo;
						BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetSongInfo", (RpcTarget)0, url, newInfo.title, newInfo.duration, PhotonNetwork.LocalPlayer.ActorNumber);
					}
				}
			}
			catch (Exception ex)
			{