Decompiled source of BoomboxCartUpgrade v1.3.1
BoomBoxCartUpgradeMod.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BoomBoxCartMod.Patches; using BoomBoxCartMod.Util; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("BoomboxCartUpgrade")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BoomboxCartUpgrade")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("37e852e0-b511-4315-8182-68c0a54e1ba9")] [assembly: AssemblyFileVersion("1.3.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BoomBoxCartMod { public class Boombox : MonoBehaviourPunCallbacks { public class BoomboxData { public string key = Guid.NewGuid().ToString(); public AudioEntry currentSong = null; public List<AudioEntry> playbackQueue = new List<AudioEntry>(); public bool isPlaying = false; public float absVolume = 0.6f; public float personalVolumePercentage = 0.35f; public bool loopQueue = false; public int playbackTime = 0; } public class AudioEntry { public string Title; public string Url; public int StartTime = 0; public AudioEntry(string title, string url) { Title = title; Url = url; } public int UseStartTime(Boombox boombox) { int result = StartTime; if (boombox.data != null && boombox.data.playbackTime != 0) { result = boombox.data.playbackTime; boombox.data.playbackTime = 0; } else if (Instance.UseTimeStampOnce.Value) { StartTime = 0; } return result; } public AudioClip GetAudioClip() { return DownloadHelper.downloadedClips.ContainsKey(Url) ? DownloadHelper.downloadedClips[Url] : null; } } private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; public PhotonView photonView; public AudioSource audioSource; public Visualizer visualizer; public DownloadHelper downloadHelper = null; private bool syncFinished = false; public float minDistance = 3f; public float maxDistanceBase = 15f; public float maxDistanceAddition = 30f; public bool isAwaitingSyncPlayback = false; public bool startPlayBackOnDownload = true; public BoomboxData data = new BoomboxData(); private AudioLowPassFilter lowPassFilter; public static int qualityLevel = 4; private static bool mutePressed = false; private float monsterAttractTimer = 0f; private float monsterAttractInterval = 1f; private static bool applyQualityToDownloads = false; private static bool monstersCanHearMusic = false; private static ManualLogSource Logger => Instance.logger; public static bool ApplyQualityToDownloads { get { return applyQualityToDownloads; } set { applyQualityToDownloads = value; } } public static bool MonstersCanHearMusic { get { return monstersCanHearMusic; } set { monstersCanHearMusic = value; } } public bool LoopQueue { get { return data.loopQueue; } set { data.loopQueue = value; } } private void Awake() { audioSource = ((Component)this).gameObject.AddComponent<AudioSource>(); audioSource.volume = 0.15f; audioSource.spatialBlend = 1f; audioSource.playOnAwake = false; audioSource.rolloffMode = (AudioRolloffMode)2; audioSource.spread = 90f; audioSource.dopplerLevel = 0f; audioSource.reverbZoneMix = 1f; audioSource.spatialize = true; audioSource.loop = false; audioSource.mute = Instance.baseListener.audioMuted; lowPassFilter = ((Component)this).gameObject.AddComponent<AudioLowPassFilter>(); ((Behaviour)lowPassFilter).enabled = false; downloadHelper = ((Component)this).gameObject.AddComponent<DownloadHelper>(); UpdateAudioRangeBasedOnVolume(audioSource.volume); photonView = ((Component)this).GetComponent<PhotonView>(); isAwaitingSyncPlayback = false; if ((Object)(object)photonView == (Object)null) { Logger.LogError((object)"PhotonView not found on Boombox object."); return; } if ((Object)(object)((Component)this).GetComponent<BoomboxController>() == (Object)null) { ((Component)this).gameObject.AddComponent<BoomboxController>(); } if ((Object)(object)((Component)this).GetComponent<Visualizer>() == (Object)null) { visualizer = ((Component)this).gameObject.AddComponent<Visualizer>(); visualizer.audioSource = audioSource; } PersistentData.SetBoomboxViewInitialized(photonView.ViewID); Logger.LogInfo((object)$"Boombox initialized on this cart. AudioSource: {audioSource}, PhotonView: {photonView}"); } private void Update() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (PhotonNetwork.IsMasterClient && data.isPlaying && MonstersCanHearMusic && (Object)(object)EnemyDirector.instance != (Object)null) { monsterAttractTimer += Time.deltaTime; if (monsterAttractTimer >= monsterAttractInterval) { EnemyDirector.instance.SetInvestigate(((Component)this).transform.position, 5f, false); monsterAttractTimer = 0f; } } else { monsterAttractTimer = 0f; } if (Instance.baseListener.audioMuted != audioSource.mute) { audioSource.mute = Instance.baseListener.audioMuted; } if (syncFinished && data.isPlaying && !audioSource.isPlaying && PhotonNetwork.IsMasterClient) { int currentSongIndex = GetCurrentSongIndex(); CleanupCurrentPlayback(); if (currentSongIndex == -1) { BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, -1, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); } else if (currentSongIndex + 1 >= data.playbackQueue.Count) { BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, (!LoopQueue) ? (-1) : 0, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); } else { BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, currentSongIndex + 1, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); } } } public void TogglePlaying(bool value) { data.isPlaying = value; } public async void SyncInitializeWithOthers() { if (PhotonNetwork.IsMasterClient) { Logger.LogInfo((object)$"Syncing with others - in queue: {data.playbackQueue.Count}"); BaseListener.RPC(photonView, "SetData", (RpcTarget)1, data.key, Instance.RestoreBoomboxes.Value, data.playbackQueue.Count, PhotonNetwork.LocalPlayer.ActorNumber); if (data.playbackQueue.Count > 0 && data.currentSong?.Url != null) { isAwaitingSyncPlayback = true; startPlayBackOnDownload = data.isPlaying && Instance.AutoResume.Value; data.isPlaying = false; downloadHelper.DownloadQueue(GetCurrentSongIndex()); } else { data.playbackQueue.Clear(); data.currentSong = null; } syncFinished = true; } } [PunRPC] public void SetData(string key, bool save, int queueSize, int requesterId) { if (requesterId != PhotonNetwork.MasterClient.ActorNumber) { return; } isAwaitingSyncPlayback = false; syncFinished = false; UpdateUIStatus("Ready to play music! Enter a Video URL"); BoomboxData boomboxData = Instance.data.GetBoomboxData().FirstOrDefault((BoomboxData data) => data.key == key); if (boomboxData != null) { if (!save) { Instance.data.GetBoomboxData().Remove(boomboxData); data = new BoomboxData(); data.key = key; } else { data = boomboxData; data.isPlaying = false; } } else { data = new BoomboxData(); data.key = key; if (save) { Instance.data.GetBoomboxData().Add(data); } } if (data.playbackQueue.Count != queueSize) { BaseListener.RPC(photonView, "RequestFullSync", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber); } ((Component)this).GetComponent<BoomboxUI>()?.UpdateDataFromBoomBox(); CleanupCurrentPlayback(); } public static long GetCurrentTimeMilliseconds() { return PhotonNetwork.ServerTimestamp; } public long GetRelativePlaybackMilliseconds() { return GetCurrentTimeMilliseconds() - (long)Math.Round(audioSource.time * 1000f); } public static string GetSongTitle(string url) { if (DownloadHelper.songTitles.ContainsKey(url)) { return DownloadHelper.songTitles[url]; } return null; } public int GetCurrentSongIndex() { if (data.currentSong == null) { return -1; } return data.playbackQueue.IndexOf(data.currentSong); } private void CleanupCurrentPlayback() { if (audioSource.isPlaying) { audioSource.Stop(); } TogglePlaying(value: false); audioSource.clip = null; } public void StartPlayBack() { AudioClip val = data.currentSong?.GetAudioClip(); if ((Object)(object)val == (Object)null) { Logger.LogError((object)"Clip not found for current song"); CleanupCurrentPlayback(); return; } if ((Object)(object)audioSource.clip != (Object)(object)val) { CleanupCurrentPlayback(); audioSource.clip = val; SetQuality(qualityLevel); UpdateAudioRangeBasedOnVolume(audioSource.volume); audioSource.Play(); } audioSource.Play(); TogglePlaying(value: true); } public void PausePlayBack() { audioSource.Pause(); TogglePlaying(value: false); } public void SetPlaybackTime(long startTimeMillis) { audioSource.time = Math.Max(0f, GetCurrentTimeMilliseconds() - startTimeMillis) / 1000f; } [PunRPC] public async void RequestSong(string url, int seconds, int requesterId) { if (url == null) { return; } _ = requesterId == PhotonNetwork.LocalPlayer.ActorNumber; AudioEntry song = new AudioEntry(DownloadHelper.songTitles.ContainsKey(url) ? DownloadHelper.songTitles[url] : "Unknown Title", url) { StartTime = seconds }; data.playbackQueue.Add(song); if (data.currentSong == null) { data.currentSong = song; if (PhotonNetwork.IsMasterClient) { isAwaitingSyncPlayback = true; } } if (PhotonNetwork.IsMasterClient) { downloadHelper.EnqueueDownload(url); downloadHelper.StartDownloadJob(); } } [PunRPC] public void SyncQueue(int currentIndex, Dictionary<string, object>[] queueObject, int requesterId) { if (requesterId != PhotonNetwork.MasterClient.ActorNumber) { return; } List<AudioEntry> list = DeserializeAudioEntryArray(queueObject); Logger.LogInfo((object)$"SyncQueue RPC received: currentIndex={currentIndex}, queueSize={list.Count}"); data.playbackQueue = list; foreach (AudioEntry item in data.playbackQueue) { if (!DownloadHelper.songTitles.ContainsKey(item.Url)) { DownloadHelper.songTitles[item.Url] = item.Title; } } if (currentIndex == -1) { isAwaitingSyncPlayback = false; data.currentSong = null; CleanupCurrentPlayback(); } else if (currentIndex >= data.playbackQueue.Count) { Logger.LogError((object)$"SyncQueue RPC received with wrong index: newSongIndex={currentIndex}, songCount={data.playbackQueue.Count}, requesterId={requesterId}"); } else { data.currentSong = list[currentIndex]; } } [PunRPC] public void DismissQueue(int requesterId) { if (requesterId != PhotonNetwork.MasterClient.ActorNumber) { if (PhotonNetwork.IsMasterClient && !Instance.MasterClientDismissQueue.Value) { BaseListener.RPC(photonView, "DismissQueue", (RpcTarget)0, PhotonNetwork.LocalPlayer.ActorNumber); } return; } if (audioSource.isPlaying) { CleanupCurrentPlayback(); UpdateUIStatus("Ready to play music! Enter a Video URL"); } data.playbackQueue.Clear(); data.currentSong = null; if (PhotonNetwork.IsMasterClient) { downloadHelper.DismissDownloadQueue(); downloadHelper.ForceCancelDownload(); } } [PunRPC] public void MoveQueueItem(int index, int newIndex, int requesterId) { if (index < 0 || index >= data.playbackQueue.Count || newIndex < 0 || newIndex >= data.playbackQueue.Count || index == newIndex) { Logger.LogError((object)$"MoveQueueItem RPC received with invalid indices: {index}, {newIndex}, queue count: {data.playbackQueue.Count}"); if (PhotonNetwork.IsMasterClient) { Player val = ((IEnumerable<Player>)PhotonNetwork.PlayerList).FirstOrDefault((Func<Player, bool>)((Player player) => player.ActorNumber == requesterId)); if (val != null && !val.IsLocal && Instance.baseListener.GetAllModUsers().Contains(requesterId)) { HandleLateJoin(val); } } else { BaseListener.RPC(photonView, "RequestFullSync", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber); } } else { AudioEntry item = data.playbackQueue[index]; data.playbackQueue.RemoveAt(index); data.playbackQueue.Insert(newIndex, item); if (PhotonNetwork.IsMasterClient && data.currentSong?.Url != null && DownloadHelper.CheckDownloadCount(data.currentSong.Url) < Instance.baseListener.GetAllModUsers().Count) { downloadHelper.DismissDownloadQueue(); downloadHelper.DownloadQueue(GetCurrentSongIndex()); } } } [PunRPC] public void RemoveQueueItem(int index, int requesterId) { if (index < 0 || index > data.playbackQueue.Count) { Logger.LogError((object)$"RemoveQueueItem RPC received with wrong index: index={index}, songCount={data.playbackQueue.Count}, requesterId={requesterId}"); if (PhotonNetwork.IsMasterClient) { Player val = ((IEnumerable<Player>)PhotonNetwork.PlayerList).FirstOrDefault((Func<Player, bool>)((Player player) => player.ActorNumber == requesterId)); if (val != null && !val.IsLocal && Instance.baseListener.GetAllModUsers().Contains(requesterId)) { HandleLateJoin(val); } } else { BaseListener.RPC(photonView, "RequestFullSync", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber); } return; } int currentSongIndex = GetCurrentSongIndex(); data.playbackQueue.RemoveAt(index); if (index != currentSongIndex) { return; } CleanupCurrentPlayback(); data.currentSong = null; if (!PhotonNetwork.IsMasterClient) { return; } if (data.playbackQueue.Count > 0) { index %= data.playbackQueue.Count; if (currentSongIndex < data.playbackQueue.Count || LoopQueue) { BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, index, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); return; } } BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, -1, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); } [PunRPC] public async void SyncPlayback(int newSongIndex, long startTime, int requesterId) { if (newSongIndex == -1) { isAwaitingSyncPlayback = false; data.currentSong = null; CleanupCurrentPlayback(); return; } if (newSongIndex >= data.playbackQueue.Count) { if (!PhotonNetwork.IsMasterClient) { BaseListener.RPC(photonView, "RequestFullSync", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber); } Logger.LogError((object)$"SyncPlayback RPC received with wrong index: newSongIndex={newSongIndex}, songCount={data.playbackQueue.Count}, startTime={startTime}, requesterId={requesterId}"); return; } if (GetCurrentSongIndex() == newSongIndex) { if (!data.isPlaying || !audioSource.isPlaying) { StartPlayBack(); } SetPlaybackTime(startTime); return; } if (data.isPlaying || audioSource.isPlaying) { CleanupCurrentPlayback(); } _ = data.currentSong; data.currentSong = data.playbackQueue.ElementAt(newSongIndex); if (!PhotonNetwork.IsMasterClient) { return; } if (data.currentSong?.Url == null) { BaseListener.RPC(photonView, "RemoveQueueItem", (RpcTarget)0, newSongIndex, PhotonNetwork.LocalPlayer.ActorNumber); } else if (DownloadHelper.CheckDownloadCount(data.currentSong.Url) < Instance.baseListener.GetAllModUsers().Count) { if (data.currentSong.Url != downloadHelper.GetCurrentDownloadUrl()) { downloadHelper.DismissDownloadQueue(); } isAwaitingSyncPlayback = true; if (data.currentSong.Url != downloadHelper.GetCurrentDownloadUrl()) { downloadHelper.DownloadQueue(newSongIndex); } } else { BaseListener.RPC(photonView, "PlayPausePlayback", (RpcTarget)0, true, GetCurrentTimeMilliseconds() - data.currentSong.UseStartTime(this) * 1000, PhotonNetwork.LocalPlayer.ActorNumber); } } [PunRPC] public void PlayPausePlayback(bool startPlaying, long startTime, int requesterId) { if (data.playbackQueue.Count == 0 || (Object)(object)data.currentSong?.GetAudioClip() == (Object)null) { return; } if (startPlaying) { if ((Object)(object)data.currentSong?.GetAudioClip() == (Object)null) { if (PhotonNetwork.IsMasterClient) { BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, -1, GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); } return; } StartPlayBack(); string text = "Started"; } else { if ((Object)(object)audioSource.clip != (Object)(object)data.currentSong?.GetAudioClip() && (Object)(object)data.currentSong?.GetAudioClip() != (Object)null) { StartPlayBack(); } PausePlayBack(); string text = "Paused"; } SetPlaybackTime(startTime); if (data.isPlaying) { UpdateUIStatus("Now playing: " + ((data.currentSong?.Url == null) ? "Unkown" : data.currentSong?.Title)); } } public void SetQuality(int level) { qualityLevel = Mathf.Clamp(level, 0, 4); switch (qualityLevel) { case 0: ((Behaviour)lowPassFilter).enabled = true; lowPassFilter.cutoffFrequency = 1500f; break; case 1: ((Behaviour)lowPassFilter).enabled = true; lowPassFilter.cutoffFrequency = 3000f; break; case 2: ((Behaviour)lowPassFilter).enabled = true; lowPassFilter.cutoffFrequency = 4500f; break; case 3: ((Behaviour)lowPassFilter).enabled = true; lowPassFilter.cutoffFrequency = 6000f; break; case 4: ((Behaviour)lowPassFilter).enabled = false; break; } } [PunRPC] public void UpdateQuality(int level, int requesterId) { BoomboxController component = ((Component)this).GetComponent<BoomboxController>(); SetQuality(level); } [PunRPC] public void UpdateVolume(float volume, int requesterId) { BoomboxController component = ((Component)this).GetComponent<BoomboxController>(); data.absVolume = volume; float volume2 = volume * data.personalVolumePercentage; audioSource.volume = volume2; UpdateAudioRangeBasedOnVolume(volume2); } private void UpdateAudioRangeBasedOnVolume(float volume) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown float num = Mathf.Lerp(maxDistanceBase, maxDistanceBase + maxDistanceAddition, volume); audioSource.minDistance = minDistance; audioSource.maxDistance = num; AnimationCurve val = new AnimationCurve((Keyframe[])(object)new Keyframe[3] { new Keyframe(0f, 1f), new Keyframe(minDistance, 0.9f), new Keyframe(num, 0f) }); audioSource.SetCustomCurve((AudioSourceCurveType)0, val); } [PunRPC] public void UpdateLooping(bool loop, int requesterId) { if (PhotonNetwork.MasterClient.ActorNumber == requesterId) { LoopQueue = loop; } } public void UpdateUIStatus(string message) { if (!((Object)(object)this == (Object)null)) { BoomboxUI component = ((Component)this).GetComponent<BoomboxUI>(); if ((Object)(object)component != (Object)null && component.IsUIVisible()) { component.UpdateStatus(message); } } } [PunRPC] public void RequestFullSync(int requesterId) { if (PhotonNetwork.IsMasterClient) { Player val = ((IEnumerable<Player>)PhotonNetwork.PlayerList).FirstOrDefault((Func<Player, bool>)((Player player) => player.ActorNumber == requesterId)); if (val != null && Instance.baseListener.GetAllModUsers().Contains(requesterId)) { HandleLateJoin(val); } } } public override void OnPlayerEnteredRoom(Player newPlayer) { ((MonoBehaviourPunCallbacks)this).OnPlayerEnteredRoom(newPlayer); if (!PhotonNetwork.IsMasterClient || Instance.modDisabled) { return; } Logger.LogInfo((object)$"New player {newPlayer.ActorNumber} joined - syncing current playback state"); Task.Run(async delegate { PhotonView obj = ((MonoBehaviourPun)Instance.baseListener).photonView; if (obj != null) { obj.RPC("ModFeedbackCheck", newPlayer, new object[2] { "1.3.1", PhotonNetwork.LocalPlayer.ActorNumber }); } float startTime = Time.time; while ((Object)(object)photonView != (Object)null && !PersistentData.GetBoomboxViewStatus(newPlayer, photonView.ViewID)) { if (Time.time - startTime > 5f) { return; } await Task.Delay(200); } if ((Object)(object)photonView != (Object)null && Instance.baseListener.GetAllModUsers().Contains(newPlayer.ActorNumber)) { HandleLateJoin(newPlayer); } }); } private async void HandleLateJoin(Player player) { BaseListener.RPC(photonView, "SetData", player, data.key, Instance.RestoreBoomboxes.Value, 0, PhotonNetwork.LocalPlayer.ActorNumber); BaseListener.RPC(photonView, "SyncQueue", player, GetCurrentSongIndex(), SerializeAudioEntryList(data.playbackQueue).ToArray(), PhotonNetwork.LocalPlayer.ActorNumber); BaseListener.RPC(photonView, "UpdateLooping", player, LoopQueue, PhotonNetwork.LocalPlayer.ActorNumber); BaseListener.RPC(photonView, "UpdateVolume", player, data.absVolume, PhotonNetwork.LocalPlayer.ActorNumber); AudioSource obj = audioSource; if ((Object)(object)((obj != null) ? obj.clip : null) != (Object)null) { int downloadIndex = GetCurrentSongIndex() + ((audioSource.isPlaying && audioSource.time + 10f > audioSource.clip.length) ? 1 : 0); downloadHelper.DismissDownloadQueue(); downloadHelper.DownloadQueue(downloadIndex); } } private void OnDisable() { PersistentData.RemoveBoomboxViewInitialized(photonView.ViewID); data.playbackTime = (int)Math.Round(audioSource.time); } private void OnDestroy() { Instance.data.GetAllBoomboxes().Remove(this); Object.Destroy((Object)(object)((Component)this).GetComponent<BoomboxUI>()); Object.Destroy((Object)(object)visualizer); Object.Destroy((Object)(object)lowPassFilter); Object.Destroy((Object)(object)audioSource); Object.Destroy((Object)(object)downloadHelper); Object.Destroy((Object)(object)((Component)this).gameObject.GetComponent<BoomboxController>()); photonView.RefreshRpcMonoBehaviourCache(); } public void ResetData() { if (PhotonNetwork.IsMasterClient) { Logger.LogInfo((object)$"Resetting Boombox {photonView.ViewID}"); isAwaitingSyncPlayback = false; startPlayBackOnDownload = true; UpdateUIStatus("Ready to play music! Enter a Video URL"); CleanupCurrentPlayback(); downloadHelper.DismissDownloadQueue(); List<BoomboxData> boomboxData = Instance.data.GetBoomboxData(); int index = boomboxData.Count; if (boomboxData.Contains(data)) { index = boomboxData.IndexOf(data); boomboxData.Remove(data); } data = new BoomboxData(); if (Instance.RestoreBoomboxes.Value) { boomboxData.Insert(index, data); } BaseListener.RPC(photonView, "SetData", (RpcTarget)1, data.key, Instance.RestoreBoomboxes.Value, data.playbackQueue.Count, PhotonNetwork.LocalPlayer.ActorNumber); } } public List<Dictionary<string, object>> SerializeAudioEntryList(List<AudioEntry> list) { List<Dictionary<string, object>> list2 = new List<Dictionary<string, object>>(); foreach (AudioEntry item in list) { Dictionary<string, object> dictionary = new Dictionary<string, object>(); dictionary.Add("Title", item.Title); dictionary.Add("Url", item.Url); dictionary.Add("StartTime", item.StartTime); list2.Add(dictionary); } return list2; } public List<AudioEntry> DeserializeAudioEntryArray(Dictionary<string, object>[] array) { List<AudioEntry> list = new List<AudioEntry>(); foreach (Dictionary<string, object> dictionary in array) { dictionary.TryGetValue("Title", out var value); string title = (value as string) ?? "Unknown"; dictionary.TryGetValue("Url", out var value2); string url = value2 as string; dictionary.TryGetValue("StartTime", out var value3); int valueOrDefault = (value3 as int?).GetValueOrDefault(); AudioEntry audioEntry = new AudioEntry(title, url); audioEntry.StartTime = valueOrDefault; list.Add(audioEntry); } return list; } } public class BoomboxController : MonoBehaviourPun { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; private PhysGrabCart cart; private BoomboxUI boomboxUI; private Boombox boombox; private int currentControllerId = -1; private static ManualLogSource Logger => Instance.logger; private void Awake() { cart = ((Component)this).GetComponent<PhysGrabCart>(); boombox = ((Component)this).GetComponent<Boombox>(); if ((Object)(object)cart == (Object)null) { Logger.LogError((object)"BoomboxController: PhysGrabCart component not found!"); } else if ((Object)(object)boombox == (Object)null) { Logger.LogError((object)"BoomboxController: Boombox component not found!"); } } private bool IsLocalPlayerGrabbingCart() { return PlayerGrabbingTracker.IsLocalPlayerGrabbingCart(((Component)this).gameObject); } public void RequestBoomboxControl() { if (IsLocalPlayerGrabbingCart()) { int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber; BaseListener.RPC(((MonoBehaviourPun)this).photonView, "RequestControl", (RpcTarget)2, actorNumber); } } [PunRPC] private void RequestControl(int requesterId) { if (PhotonNetwork.IsMasterClient) { bool flag = true; if (requesterId == PhotonNetwork.LocalPlayer.ActorNumber) { flag = PlayerGrabbingTracker.IsLocalPlayerGrabbingCart(((Component)this).gameObject); } if ((currentControllerId == -1 || !Instance.baseListener.GetAllModUsers().Contains(requesterId)) && flag) { BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetController", (RpcTarget)0, requesterId); } } } [PunRPC] private void SetController(int controllerId) { try { currentControllerId = controllerId; if (controllerId == PhotonNetwork.LocalPlayer.ActorNumber) { EnsureBoomboxUIExists(); if ((Object)(object)boomboxUI != (Object)null) { boomboxUI.ShowUI(); } else { Logger.LogError((object)"Failed to create BoomboxUI component"); } } else if ((Object)(object)boomboxUI != (Object)null && boomboxUI.IsUIVisible()) { boomboxUI.HideUI(); } } catch (Exception ex) { Logger.LogError((object)("Error in SetController: " + ex.Message + "\n" + ex.StackTrace)); } } private void EnsureBoomboxUIExists() { if ((Object)(object)boomboxUI == (Object)null) { boomboxUI = ((Component)this).gameObject.GetComponent<BoomboxUI>(); if ((Object)(object)boomboxUI == (Object)null) { boomboxUI = ((Component)this).gameObject.AddComponent<BoomboxUI>(); } } } public void ReleaseControl() { if (currentControllerId == PhotonNetwork.LocalPlayer.ActorNumber) { BaseListener.RPC(((MonoBehaviourPun)this).photonView, "RequestRelease", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber); } } [PunRPC] private void RequestRelease(int releaserId) { if (PhotonNetwork.IsMasterClient && currentControllerId == releaserId) { BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetController", (RpcTarget)0, -1); } } private void OnPlayerReleasedCart(int playerActorNumber) { if (playerActorNumber == currentControllerId && PhotonNetwork.IsMasterClient) { BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetController", (RpcTarget)0, -1); } } public void LocalPlayerReleasedCart() { int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber; if (currentControllerId == actorNumber) { ReleaseControl(); } } } public class BoomboxUI : MonoBehaviourPun { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; public PhotonView photonView; public static bool anyUISHown = false; public bool showUI = false; private string urlInput = ""; private bool isTimeSliderBeingDragged = false; private int songIndexForTime = -2; private float songTimePerc = 0f; private float lastSentSongTimePerc = -1f; private float lastSentVolume = 0.3f; private bool isVolumeSliderBeingDragged = false; private bool isIndividualVolumeBeingDragged = false; private int qualityLevel = 3; private string[] qualityLabels = new string[5] { "REALLY Low (You Freak)", "Low", "Medium-Low", "Medium-High", "High" }; private bool isQualitySliderBeingDragged = false; private int lastSentQualityLevel = 3; private Rect windowRect; private Boombox boombox; private BoomboxController controller; private VisualEffects visualEffects; private Visualizer visualizer; private GUIStyle windowStyle; private GUIStyle headerStyle; private GUIStyle buttonStyle; private GUIStyle smallButtonStyle; private GUIStyle textFieldStyle; private GUIStyle labelStyle; private GUIStyle sliderStyle; private GUIStyle statusStyle; private GUIStyle scrollViewStyle; private GUIStyle queueHeaderStyle; private GUIStyle queueEntryStyle; private GUIStyle currentSongStyle; private Texture2D backgroundTexture; private Texture2D buttonTexture; private Texture2D sliderBackgroundTexture; private Texture2D sliderThumbTexture; private Texture2D textFieldBackgroundTexture; private Vector2 urlScrollPosition = Vector2.zero; private float textFieldVisibleWidth = 350f; private string errorMessage = ""; private float errorMessageTime = 0f; public string statusMessage = ""; private CursorLockMode previousLockMode; private bool previousCursorVisible; private bool stylesInitialized = false; private Vector2 scrollPosition = Vector2.zero; private Vector2 queueScrollPosition = Vector2.zero; private const float refreshHoldTime = 5f; private const int maxRefreshSymbols = 5; private float? refreshObjectStart = null; private bool refreshObjectSent = false; private string lastUrl = null; private static ManualLogSource Logger => Instance.logger; private void Awake() { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) try { boombox = ((Component)this).GetComponent<Boombox>(); if ((Object)(object)boombox != (Object)null) { photonView = boombox.photonView; } else { Logger.LogError((object)"BoomboxUI: Failed to find Boombox component"); photonView = ((Component)this).GetComponent<PhotonView>(); } controller = ((Component)this).GetComponent<BoomboxController>(); visualEffects = ((Component)this).GetComponent<VisualEffects>(); if ((Object)(object)visualEffects == (Object)null) { visualEffects = ((Component)this).gameObject.AddComponent<VisualEffects>(); } visualizer = ((Component)this).GetComponent<Visualizer>(); if ((Object)(object)visualizer == (Object)null) { visualizer = ((Component)this).gameObject.AddComponent<Visualizer>(); visualizer.audioSource = boombox.audioSource; } if ((Object)(object)photonView == (Object)null) { Logger.LogError((object)"BoomboxUI: Failed to find PhotonView component"); } windowRect = new Rect((float)(Screen.width / 2 - 400), (float)(Screen.height / 2 - 175), 800f, 550f); } catch (Exception ex) { Logger.LogError((object)("Error in BoomboxUI.Awake: " + ex.Message + "\n" + ex.StackTrace)); } } private void Update() { if (Time.time > errorMessageTime && !string.IsNullOrEmpty(errorMessage)) { errorMessage = ""; } if (showUI && Keyboard.current != null && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame) { if ((Object)(object)controller != (Object)null) { controller.ReleaseControl(); } else { HideUI(); } } if (isTimeSliderBeingDragged && ((Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame) || songIndexForTime != boombox.GetCurrentSongIndex())) { isTimeSliderBeingDragged = false; SendTimeUpdate(); songIndexForTime = -2; songTimePerc = 0f; lastSentSongTimePerc = -1f; } if (isVolumeSliderBeingDragged && Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame) { isVolumeSliderBeingDragged = false; SendVolumeUpdate(); } if (isIndividualVolumeBeingDragged && Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame) { isIndividualVolumeBeingDragged = false; } if (isQualitySliderBeingDragged && Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame) { isQualitySliderBeingDragged = false; SendQualityUpdate(); } if (refreshObjectStart.HasValue && Mouse.current != null && Mouse.current.leftButton.wasReleasedThisFrame) { refreshObjectStart = null; refreshObjectSent = false; } } public void ShowUI() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (!showUI) { if ((Object)(object)boombox == (Object)null || (Object)(object)photonView == (Object)null) { Logger.LogError((object)"Cannot show UI - boombox or photonView is null"); return; } anyUISHown = true; showUI = true; previousLockMode = Cursor.lockState; previousCursorVisible = Cursor.visible; Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; UpdateDataFromBoomBox(); UpdateStatusFromBoombox(); } } public void UpdateDataFromBoomBox() { if ((Object)(object)boombox != (Object)null) { lastSentVolume = boombox.data.absVolume; qualityLevel = Boombox.qualityLevel; lastSentQualityLevel = qualityLevel; } } public void UpdateStatusFromBoombox() { if ((Object)(object)boombox != (Object)null) { if (boombox.downloadHelper.IsProcessingQueue() && boombox.data.currentSong != null && (Object)(object)boombox.data.currentSong.GetAudioClip() == (Object)null) { statusMessage = "Downloading audio from " + boombox.downloadHelper.GetCurrentDownloadUrl() + "..."; } else if (boombox.data.currentSong != null && boombox.data.isPlaying) { statusMessage = "Now playing: " + boombox.data.currentSong.Title; } else if (!string.IsNullOrEmpty(boombox.data.currentSong?.Url)) { statusMessage = "Ready to play: " + boombox.data.currentSong.Title; } else { statusMessage = "Ready to play music! Enter a Video URL"; } } } private void SendTimeUpdate() { if (songTimePerc == lastSentSongTimePerc) { return; } lastSentSongTimePerc = songTimePerc; if (boombox?.GetCurrentSongIndex() == songIndexForTime && songIndexForTime != -1) { Boombox obj = boombox; object obj2; if (obj == null) { obj2 = null; } else { AudioSource audioSource = obj.audioSource; obj2 = ((audioSource != null) ? audioSource.clip : null); } if ((Object)obj2 != (Object)null && boombox.audioSource.clip.length > 0f) { float time = Math.Max(0f, Math.Min(songTimePerc * boombox.audioSource.clip.length, boombox.audioSource.clip.length - 0.05f)); boombox.audioSource.time = time; BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, boombox.GetCurrentSongIndex(), boombox.GetRelativePlaybackMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); return; } } if (songIndexForTime != boombox.GetCurrentSongIndex()) { BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, boombox.GetCurrentSongIndex(), boombox.GetRelativePlaybackMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); } } private void SendVolumeUpdate() { if (boombox.data.absVolume != lastSentVolume) { lastSentVolume = boombox.data.absVolume; if ((Object)(object)boombox.audioSource != (Object)null) { float volume = boombox.data.absVolume * boombox.data.personalVolumePercentage; boombox.audioSource.volume = volume; } BaseListener.RPC(photonView, "UpdateVolume", (RpcTarget)0, boombox.data.absVolume, PhotonNetwork.LocalPlayer.ActorNumber); } } private void SendQualityUpdate() { if (qualityLevel != lastSentQualityLevel) { lastSentQualityLevel = qualityLevel; if ((Object)(object)boombox != (Object)null) { boombox.SetQuality(qualityLevel); } } } public void HideUI() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (showUI) { anyUISHown = false; showUI = false; Cursor.lockState = previousLockMode; Cursor.visible = previousCursorVisible; } } public bool IsUIVisible() { return showUI; } public void UpdateStatus(string message) { statusMessage = message; } private Texture2D CreateColorTexture(Color color) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, color); val.Apply(); return val; } private void InitializeStyles() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Expected O, but got Unknown //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Expected O, but got Unknown //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Expected O, but got Unknown //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Expected O, but got Unknown //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Expected O, but got Unknown //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Expected O, but got Unknown //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Expected O, but got Unknown //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Expected O, but got Unknown //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03a7: Expected O, but got Unknown //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Expected O, but got Unknown //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Expected O, but got Unknown //IL_03dd: Unknown result type (might be due to invalid IL or missing references) //IL_0401: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Expected O, but got Unknown //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Expected O, but got Unknown //IL_042c: Unknown result type (might be due to invalid IL or missing references) //IL_046a: Unknown result type (might be due to invalid IL or missing references) //IL_0474: Expected O, but got Unknown //IL_0492: Unknown result type (might be due to invalid IL or missing references) //IL_049c: Expected O, but got Unknown //IL_04c2: Unknown result type (might be due to invalid IL or missing references) //IL_04cc: Expected O, but got Unknown //IL_04d4: Unknown result type (might be due to invalid IL or missing references) //IL_04de: Expected O, but got Unknown //IL_04fe: Unknown result type (might be due to invalid IL or missing references) //IL_052e: Unknown result type (might be due to invalid IL or missing references) //IL_0552: Unknown result type (might be due to invalid IL or missing references) //IL_055c: Expected O, but got Unknown //IL_057c: Unknown result type (might be due to invalid IL or missing references) //IL_0597: Unknown result type (might be due to invalid IL or missing references) if (!stylesInitialized) { backgroundTexture = CreateColorTexture(new Color(0.1f, 0.1f, 0.1f, 0.9f)); buttonTexture = CreateColorTexture(new Color(0.2f, 0.2f, 0.3f, 1f)); sliderBackgroundTexture = CreateColorTexture(new Color(0.15f, 0.15f, 0.2f, 1f)); sliderThumbTexture = CreateColorTexture(new Color(0.7f, 0.7f, 0.8f, 1f)); textFieldBackgroundTexture = CreateColorTexture(new Color(0.15f, 0.17f, 0.2f, 1f)); windowStyle = new GUIStyle(GUI.skin.window); windowStyle.normal.background = backgroundTexture; windowStyle.onNormal.background = backgroundTexture; windowStyle.border = new RectOffset(10, 10, 10, 10); windowStyle.padding = new RectOffset(15, 15, 20, 15); headerStyle = new GUIStyle(GUI.skin.label); headerStyle.fontSize = 18; headerStyle.fontStyle = (FontStyle)1; headerStyle.normal.textColor = Color.white; headerStyle.alignment = (TextAnchor)4; headerStyle.margin = new RectOffset(0, 0, 10, 20); buttonStyle = new GUIStyle(GUI.skin.button); buttonStyle.normal.background = buttonTexture; buttonStyle.hover.background = CreateColorTexture(new Color(0.3f, 0.3f, 0.4f, 1f)); buttonStyle.active.background = CreateColorTexture(new Color(0.4f, 0.4f, 0.5f, 1f)); buttonStyle.normal.textColor = Color.white; buttonStyle.hover.textColor = Color.white; buttonStyle.active.textColor = Color.white; buttonStyle.fontSize = 14; buttonStyle.padding = new RectOffset(15, 15, 8, 8); buttonStyle.margin = new RectOffset(5, 5, 5, 5); buttonStyle.alignment = (TextAnchor)4; smallButtonStyle = new GUIStyle(buttonStyle); smallButtonStyle.padding = new RectOffset(8, 8, 4, 4); smallButtonStyle.fontSize = 12; textFieldStyle = new GUIStyle(GUI.skin.textField); textFieldStyle.normal.background = textFieldBackgroundTexture; textFieldStyle.normal.textColor = new Color(1f, 1f, 1f); textFieldStyle.fontSize = 14; textFieldStyle.padding = new RectOffset(10, 10, 8, 8); scrollViewStyle = new GUIStyle(GUI.skin.scrollView); scrollViewStyle.normal.background = textFieldBackgroundTexture; scrollViewStyle.border = new RectOffset(2, 2, 2, 2); scrollViewStyle.padding = new RectOffset(0, 0, 0, 0); labelStyle = new GUIStyle(GUI.skin.label); labelStyle.normal.textColor = Color.white; labelStyle.fontSize = 14; labelStyle.margin = new RectOffset(0, 0, 10, 5); statusStyle = new GUIStyle(GUI.skin.label); statusStyle.normal.textColor = Color.cyan; statusStyle.fontSize = 14; statusStyle.wordWrap = true; statusStyle.alignment = (TextAnchor)4; sliderStyle = new GUIStyle(GUI.skin.horizontalSlider); sliderStyle.normal.background = sliderBackgroundTexture; queueHeaderStyle = new GUIStyle(headerStyle); queueHeaderStyle.fontSize = 16; queueHeaderStyle.alignment = (TextAnchor)3; queueHeaderStyle.margin = new RectOffset(5, 0, 10, 5); queueEntryStyle = new GUIStyle(textFieldStyle); queueEntryStyle.normal.background = CreateColorTexture(new Color(0.1f, 0.1f, 0.1f, 0.7f)); queueEntryStyle.hover.background = CreateColorTexture(new Color(0.2f, 0.2f, 0.2f, 0.8f)); queueEntryStyle.alignment = (TextAnchor)3; currentSongStyle = new GUIStyle(queueEntryStyle); currentSongStyle.normal.background = CreateColorTexture(new Color(0.1f, 0.3f, 0.1f, 0.9f)); currentSongStyle.normal.textColor = Color.yellow; currentSongStyle.hover.background = currentSongStyle.normal.background; stylesInitialized = true; } } private void OnGUI() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (showUI) { if (!stylesInitialized) { InitializeStyles(); } windowRect = GUILayout.Window(0, windowRect, new WindowFunction(DrawUI), "Boombox Controller", windowStyle, Array.Empty<GUILayoutOption>()); } } private void DrawUI(int windowID) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (boombox?.data == null) { return; } object obj; if (!Instance.baseListener.audioMuted) { obj = ""; } else { Key value = Instance.GlobalMuteKey.Value; obj = " - MUTED(" + ((object)(Key)(ref value)).ToString() + ")"; } GUILayout.Label("Control The Boombox In The Cart" + (string?)obj, headerStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(400f) }); DrawMainPanel(boombox); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(420f), GUILayout.ExpandHeight(true) }); DrawQueue(boombox); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Close", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(300f), GUILayout.Height(36f) })) { if ((Object)(object)controller != (Object)null) { controller.ReleaseControl(); } else { HideUI(); } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUI.DragWindow(); } private void DrawMainPanel(Boombox boombox) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_07ec: Unknown result type (might be due to invalid IL or missing references) //IL_075e: Unknown result type (might be due to invalid IL or missing references) //IL_0780: Unknown result type (might be due to invalid IL or missing references) //IL_07f3: Unknown result type (might be due to invalid IL or missing references) //IL_07f8: Unknown result type (might be due to invalid IL or missing references) //IL_0801: Unknown result type (might be due to invalid IL or missing references) //IL_092b: Unknown result type (might be due to invalid IL or missing references) //IL_0932: Unknown result type (might be due to invalid IL or missing references) //IL_0937: Unknown result type (might be due to invalid IL or missing references) //IL_0940: Unknown result type (might be due to invalid IL or missing references) //IL_0a27: Unknown result type (might be due to invalid IL or missing references) //IL_0a2e: Unknown result type (might be due to invalid IL or missing references) //IL_0a33: Unknown result type (might be due to invalid IL or missing references) //IL_0a3c: Unknown result type (might be due to invalid IL or missing references) scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Enter Video URL:", labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button("Clear", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { urlInput = ""; GUI.FocusControl((string)null); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); urlScrollPosition = GUILayout.BeginScrollView(urlScrollPosition, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(60f) }); urlInput = GUILayout.TextField(urlInput, textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) }); urlInput = Regex.Replace(urlInput, "\\s+", ""); GUILayout.EndScrollView(); GUILayout.EndHorizontal(); float num = 0f; float num2 = 0f; float num3 = 0f; string text = "??"; int num4; if ((Object)(object)boombox != (Object)null) { AudioSource audioSource = boombox.audioSource; num4 = (((Object)(object)((audioSource != null) ? audioSource.clip : null) != (Object)null) ? 1 : 0); } else { num4 = 0; } bool flag = (byte)num4 != 0; if (flag) { num = boombox.audioSource.time; num2 = boombox.audioSource.clip.length; if (num2 > 0f) { num3 = num / num2; text = PrintTime(num2); } else { flag = false; } } GUILayout.Label(PrintTime(num) + " / " + text, labelStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); Rect lastRect; if (flag && (int)Event.current.type == 0) { lastRect = GUILayoutUtility.GetLastRect(); if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition)) { isTimeSliderBeingDragged = true; } } float num5 = GUILayout.HorizontalSlider(num3, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>()); int currentSongIndex = boombox.GetCurrentSongIndex(); if (flag && currentSongIndex != -1 && num5 != num3) { if (!isTimeSliderBeingDragged) { isTimeSliderBeingDragged = true; } if (songIndexForTime == -2) { songIndexForTime = currentSongIndex; } if (songIndexForTime == currentSongIndex) { float time = Math.Max(0f, Math.Min(num5 * num2, boombox.audioSource.clip.length - 0.05f)); boombox.audioSource.time = time; songTimePerc = num5; } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("<<", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(40f), GUILayout.Height(40f) }) && (Object)(object)boombox != (Object)null) { AudioSource audioSource2 = boombox.audioSource; if ((Object)(object)((audioSource2 != null) ? audioSource2.clip : null) != (Object)null) { BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, boombox.GetCurrentSongIndex(), boombox.GetRelativePlaybackMilliseconds() + 10000, PhotonNetwork.LocalPlayer.ActorNumber); } } string text2 = ((boombox?.data != null && boombox.data.playbackQueue.Count > 0) ? "+ ENQUEUE" : "▶ PLAY"); if (GUILayout.Button(text2, buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) })) { var (text3, num6) = IsValidVideoUrl(urlInput); if (string.IsNullOrEmpty(text3)) { ShowErrorMessage("Invalid Video URL!"); } else if (lastUrl != text3) { lastUrl = text3; BaseListener.RPC(photonView, "RequestSong", (RpcTarget)0, text3, num6, PhotonNetwork.LocalPlayer.ActorNumber); GUI.FocusControl((string)null); } } string text4 = (((Object)(object)boombox.data.currentSong?.GetAudioClip() == (Object)null) ? "..." : (boombox.audioSource.isPlaying ? "▌▌ PAUSE" : "▶ RESUME")); if (GUILayout.Button(text4, buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }) && (Object)(object)boombox.data.currentSong?.GetAudioClip() != (Object)null) { BaseListener.RPC(photonView, "PlayPausePlayback", (RpcTarget)0, !boombox.audioSource.isPlaying, boombox.GetRelativePlaybackMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); } if (GUILayout.Button(">>", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(40f), GUILayout.Height(40f) }) && (Object)(object)boombox != (Object)null) { AudioSource audioSource3 = boombox.audioSource; if ((Object)(object)((audioSource3 != null) ? audioSource3.clip : null) != (Object)null) { BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, boombox.GetCurrentSongIndex(), boombox.GetRelativePlaybackMilliseconds() - 10000, PhotonNetwork.LocalPlayer.ActorNumber); } } GUILayout.EndHorizontal(); if ((Object)(object)boombox != (Object)null && boombox.downloadHelper.IsProcessingQueue() && (Object)(object)boombox.data.currentSong?.GetAudioClip() == (Object)null && boombox.downloadHelper.GetCurrentDownloadUrl() != null) { GUILayout.Space(10f); GUILayout.Label("Download in progress...", statusStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Force Cancel Download", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(200f), GUILayout.Height(30f) })) { boombox.downloadHelper.ForceCancelDownload(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Space(15f); if (!string.IsNullOrEmpty(statusMessage)) { GUILayout.Label(statusMessage, statusStyle, Array.Empty<GUILayoutOption>()); GUILayout.Space(5f); } if (!string.IsNullOrEmpty(errorMessage)) { GUI.color = Color.red; GUILayout.Label(errorMessage, labelStyle, Array.Empty<GUILayoutOption>()); GUI.color = Color.white; GUILayout.Space(5f); } GUILayout.Space(15f); float num7 = boombox.data.absVolume * 100f; GUILayout.Label($"Volume: {Mathf.Round(num7)}%", labelStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if ((int)Event.current.type == 0) { lastRect = GUILayoutUtility.GetLastRect(); if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition)) { isVolumeSliderBeingDragged = true; } } float num8 = GUILayout.HorizontalSlider(boombox.data.absVolume, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>()); if (num8 != boombox.data.absVolume) { if (!isVolumeSliderBeingDragged) { isVolumeSliderBeingDragged = true; } if ((Object)(object)boombox != (Object)null && (Object)(object)boombox.audioSource != (Object)null) { float volume = boombox.data.absVolume * boombox.data.personalVolumePercentage; boombox.audioSource.volume = volume; } boombox.data.absVolume = num8; } GUILayout.EndHorizontal(); GUILayout.Space(15f); GUILayout.Label($"Personal Volume %: {Mathf.Round(boombox.data.personalVolumePercentage * 100.001f)}%", labelStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if ((int)Event.current.type == 0) { lastRect = GUILayoutUtility.GetLastRect(); if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition)) { isIndividualVolumeBeingDragged = true; } } float num9 = GUILayout.HorizontalSlider(boombox.data.personalVolumePercentage, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>()); if (num9 != boombox.data.personalVolumePercentage) { isIndividualVolumeBeingDragged = true; boombox.data.personalVolumePercentage = num9; boombox.audioSource.volume = boombox.data.absVolume * boombox.data.personalVolumePercentage; } GUILayout.EndHorizontal(); GUILayout.Space(15f); GUILayout.Label("Audio Quality: " + qualityLabels[qualityLevel], labelStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if ((int)Event.current.type == 0) { lastRect = GUILayoutUtility.GetLastRect(); if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition)) { isQualitySliderBeingDragged = true; } } float num10 = GUILayout.HorizontalSlider((float)qualityLevel, 0f, 4f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty<GUILayoutOption>()); int num11 = Mathf.RoundToInt(num10); if (num11 != qualityLevel && !isQualitySliderBeingDragged) { isQualitySliderBeingDragged = true; } qualityLevel = num11; if ((Object)(object)boombox != (Object)null) { boombox.SetQuality(qualityLevel); } GUILayout.EndHorizontal(); GUILayout.Space(10f); bool applyQualityToDownloads = Boombox.ApplyQualityToDownloads; bool flag2 = GUILayout.Toggle(applyQualityToDownloads, "Apply Quality Setting to Downloads", Array.Empty<GUILayoutOption>()); if (flag2 != applyQualityToDownloads) { Boombox.ApplyQualityToDownloads = flag2; } GUILayout.Space(10f); bool monstersCanHearMusic = Boombox.MonstersCanHearMusic; bool flag3 = GUILayout.Toggle(monstersCanHearMusic, "Monsters can hear audio", Array.Empty<GUILayoutOption>()); if (flag3 != monstersCanHearMusic) { Boombox.MonstersCanHearMusic = flag3; } GUILayout.Space(10f); bool loopQueue = boombox.LoopQueue; bool flag4 = GUILayout.Toggle(loopQueue, "Loop queue", Array.Empty<GUILayoutOption>()); if (flag4 != loopQueue && PhotonNetwork.IsMasterClient) { BaseListener.RPC(photonView, "UpdateLooping", (RpcTarget)0, flag4, PhotonNetwork.LocalPlayer.ActorNumber); } GUILayout.Space(10f); bool flag5 = (Object)(object)visualEffects != (Object)null && visualEffects.AreLightsOn(); bool flag6 = GUILayout.Toggle(flag5, "RGB Lights enabled", Array.Empty<GUILayoutOption>()); if (flag6 != flag5 && (Object)(object)visualEffects != (Object)null) { visualEffects.SetLights(flag6); } GUILayout.Space(10f); bool flag7 = (Object)(object)visualizer != (Object)null; bool flag8 = GUILayout.Toggle(flag7, "Audio Visualizer enabled", Array.Empty<GUILayoutOption>()); if (flag8 != flag7) { if (flag8) { visualizer = ((Component)this).gameObject.AddComponent<Visualizer>(); visualizer.audioSource = boombox.audioSource; } else { Object.Destroy((Object)(object)visualizer); visualizer = null; } } GUILayout.EndScrollView(); } private void DrawQueue(Boombox boombox) { //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" Playback Queue", queueHeaderStyle, Array.Empty<GUILayoutOption>()); if (PhotonNetwork.IsMasterClient) { float num = Time.time - (refreshObjectStart ?? Time.time); int num2 = 1 + (int)Mathf.Floor(num / 1f); string text = ((num > 0f && num <= 5f) ? (new string('!', num2) + new string('.', Math.Max(0, 5 - num2))) : "RESET"); if (GUILayout.RepeatButton(text, smallButtonStyle, Array.Empty<GUILayoutOption>())) { if (!refreshObjectStart.HasValue) { refreshObjectStart = Time.time; } else if (!refreshObjectSent && num >= 5f) { boombox.ResetData(); lastUrl = null; refreshObjectSent = true; } } } if (GUILayout.Button("Dismiss Queue", smallButtonStyle, Array.Empty<GUILayoutOption>())) { BaseListener.RPC(photonView, "DismissQueue", (RpcTarget)((!PhotonNetwork.IsMasterClient) ? 2 : 0), PhotonNetwork.LocalPlayer.ActorNumber); lastUrl = null; } GUILayout.EndHorizontal(); queueScrollPosition = GUILayout.BeginScrollView(queueScrollPosition, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); List<Boombox.AudioEntry> playbackQueue = boombox.data.playbackQueue; int currentSongIndex = boombox.GetCurrentSongIndex(); if (playbackQueue.Count == 0) { GUILayout.Label(" Queue is empty and no song is playing.", labelStyle, Array.Empty<GUILayoutOption>()); } for (int i = 0; i < playbackQueue.Count; i++) { Boombox.AudioEntry audioEntry = playbackQueue[i]; bool flag = i == currentSongIndex; GUIStyle val = (flag ? currentSongStyle : queueEntryStyle); string text2 = (flag ? "▶ " : $"{i - ((currentSongIndex != -1) ? currentSongIndex : 0)}. "); string text3 = ClipText(text2 + audioEntry.Title, 280f, val); GUILayout.BeginHorizontal(val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) }); if (GUILayout.Button(text3, val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(32f) }) && !flag) { BaseListener.RPC(photonView, "SyncPlayback", (RpcTarget)0, i, Boombox.GetCurrentTimeMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); } if (!flag) { if (i > 0) { if (GUILayout.Button("▲", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(25f), GUILayout.Height(28f) })) { BaseListener.RPC(photonView, "MoveQueueItem", (RpcTarget)0, i, i - 1, PhotonNetwork.LocalPlayer.ActorNumber); } } else { GUILayout.Space(30f); } if (i + 1 < playbackQueue.Count) { if (GUILayout.Button("▼", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(25f), GUILayout.Height(28f) })) { BaseListener.RPC(photonView, "MoveQueueItem", (RpcTarget)0, i, i + 1, PhotonNetwork.LocalPlayer.ActorNumber); } } else { GUILayout.Space(30f); } if (GUILayout.Button("X", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(25f), GUILayout.Height(28f) })) { BaseListener.RPC(photonView, "RemoveQueueItem", (RpcTarget)0, i, PhotonNetwork.LocalPlayer.ActorNumber); } } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private string ClipText(string text, float maxWidth, GUIStyle style) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) Vector2 val = style.CalcSize(new GUIContent(text)); string text2 = text; int length = text.Length; while (val.x > maxWidth) { text2 = text2.Substring(0, text2.Length - 4) + "..."; val = style.CalcSize(new GUIContent(text2)); } return text2; } private string PrintTime(float time) { return $"{(int)Math.Floor(time / 60f)}:{(int)(time % 60f)}"; } private (string cleanedUrl, int seconds) IsValidVideoUrl(string url) { return DownloadHelper.IsValidVideoUrl(url); } private void ShowErrorMessage(string message) { errorMessage = message; errorMessageTime = Time.time + 3f; } private void OnDestroy() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (showUI) { anyUISHown = false; showUI = false; Cursor.lockState = previousLockMode; Cursor.visible = previousCursorVisible; } if ((Object)(object)backgroundTexture != (Object)null) { Object.Destroy((Object)(object)backgroundTexture); } if ((Object)(object)buttonTexture != (Object)null) { Object.Destroy((Object)(object)buttonTexture); } if ((Object)(object)sliderBackgroundTexture != (Object)null) { Object.Destroy((Object)(object)sliderBackgroundTexture); } if ((Object)(object)sliderThumbTexture != (Object)null) { Object.Destroy((Object)(object)sliderThumbTexture); } if ((Object)(object)textFieldBackgroundTexture != (Object)null) { Object.Destroy((Object)(object)textFieldBackgroundTexture); } } } public class DownloadHelper : MonoBehaviourPunCallbacks { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; private Boombox boomboxParent = null; public static Dictionary<string, AudioClip> downloadedClips = new Dictionary<string, AudioClip>(); public static Dictionary<string, string> songTitles = new Dictionary<string, string>(); public static Dictionary<string, HashSet<int>> downloadsReady = new Dictionary<string, HashSet<int>>(); public static Dictionary<string, HashSet<int>> downloadErrors = new Dictionary<string, HashSet<int>>(); private const float DOWNLOAD_TIMEOUT = 40f; private const float TIMEOUT_THRESHOLD = 10f; private Dictionary<string, Coroutine> timeoutCoroutines = new Dictionary<string, Coroutine>(); private Queue<string> downloadJobQueue = new Queue<string>(); private bool isProcessingQueue = false; private string currentRequestId = null; private string currentDownloadUrl = null; private bool isTimeoutRecovery = false; private Coroutine processingCoroutine; private static readonly Regex[] supportedVideoUrlRegexes = new Regex[5] { new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?(((?:www|m)\\.)?((?:youtube(?:-nocookie)?\\.com|youtu\\.be))|music\\.youtube\\.com)(\\/(?:[\\w\\-]+\\?v=|embed\\/|live\\/|v\\/)?)([\\w\\-]+))(?<TrailingParams>(&(\\S+&)*?(t=(?<TimeStamp>(?<Seconds>\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?((?:www)?\\.?)(rutube\\.ru)(\\/video\\/)([\\w\\-]+))(?<TrailingParams>(\\?(?:\\S+&)*?(t=(?<TimeStamp>(?<Seconds>\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?((?:www)?\\.?)(music\\.yandex\\.ru)(\\/album\\/\\d+\\/track\\/)([\\w\\-]+))(?<TrailingParams>(?:\\?(\\S+&)*?(t=(?<TimeStamp>(?<Seconds>\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?((?:www|m)\\.)?(bilibili\\.com)(\\/video\\/)([\\w\\-]+))(?<TrailingParams>(\\?(?:\\S+&)*?(t=(?<TimeStamp>(?<Seconds>\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), new Regex("^(?<CleanedUrl>((?:https?:)?\\/\\/)?((?:www|m)\\.)?(soundcloud\\.com|snd\\.sc)\\/([\\w\\-]+\\/[\\w\\-]+))(?<TrailingParams>(?:\\?(?:\\S+&*?#)*?t=(?<TimeStamp>(?<Minutes>\\d+)(?:\\/|(?:%3A))(?<Seconds>\\d{1,2})))?\\S*)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled) }; private static ManualLogSource Logger => Instance.logger; private void Awake() { boomboxParent = ((Component)this).gameObject.GetComponent<Boombox>(); } private void OnDestroy() { downloadJobQueue.Clear(); foreach (Coroutine value in timeoutCoroutines.Values) { if (value != null) { ((MonoBehaviour)this).StopCoroutine(value); } } timeoutCoroutines.Clear(); } public bool IsProcessingQueue() { return isProcessingQueue; } public string GetCurrentDownloadUrl() { return currentDownloadUrl; } public static (string cleanedUrl, int seconds) IsValidVideoUrl(string url) { if (!string.IsNullOrWhiteSpace(url)) { Regex[] array = supportedVideoUrlRegexes; foreach (Regex regex in array) { Match match = regex.Match(url); if (!match.Success) { continue; } Group group = match.Groups["CleanedUrl"]; if (!group.Success) { continue; } Group group2 = match.Groups["TimeStamp"]; int num = 0; if (group2.Success) { Group group3 = match.Groups["Seconds"]; Group group4 = match.Groups["Minutes"]; if (group3.Success && int.TryParse(group3.Value, out var result)) { num = result; } if (group4.Success && int.TryParse(group4.Value, out var result2)) { num += result2 * 60; } } return (group.Value, num); } } return (null, 0); } public static int CheckDownloadCount(string url, bool includeErrors = false) { if (string.IsNullOrEmpty(url)) { return 0; } int num = 0; if (downloadsReady.ContainsKey(url)) { num += downloadsReady[url].Count; } if (downloadErrors.ContainsKey(url)) { num += downloadErrors[url].Count; } return num; } public void EnqueueDownload(string Url) { downloadJobQueue.Enqueue(Url); } public void DismissDownloadQueue() { downloadJobQueue.Clear(); } public void StartDownloadJob() { if (!isProcessingQueue) { processingCoroutine = ((MonoBehaviour)this).StartCoroutine(ProcessDownloadQueue()); } } public static float EstimateDownloadTimeSeconds(float songLength) { return 1.5f * songLength / 60f / 1.25f; } public void DownloadQueue(int startIndex) { if (startIndex < 0 || startIndex >= boomboxParent.data.playbackQueue.Count) { startIndex = 0; } for (int i = startIndex; i < boomboxParent.data.playbackQueue.Count; i++) { downloadJobQueue.Enqueue(boomboxParent.data.playbackQueue.ElementAt(i).Url); } for (int j = 0; j < startIndex; j++) { downloadJobQueue.Enqueue(boomboxParent.data.playbackQueue.ElementAt(j).Url); } StartDownloadJob(); } [PunRPC] public void NotifyPlayersOfErrors(string message) { Logger.LogWarning((object)message); boomboxParent.UpdateUIStatus(message); } [PunRPC] public void ReportDownloadError(int actorNumber, string url, string errorMessage) { if (actorNumber == PhotonNetwork.LocalPlayer.ActorNumber) { boomboxParent.UpdateUIStatus("Error: " + errorMessage); } if (PhotonNetwork.IsMasterClient) { Logger.LogError((object)$"Player {actorNumber} reported download error for {url}: {errorMessage}"); if (!downloadErrors.ContainsKey(url)) { downloadErrors[url] = new HashSet<int>(); } downloadErrors[url].Add(actorNumber); } } [PunRPC] public void SetSongTitle(string url, string title) { songTitles[url] = title; if (boomboxParent.data.currentSong != null && boomboxParent.data.currentSong.Url == url) { boomboxParent.data.currentSong.Title = title; if ((Object)(object)boomboxParent.data.currentSong.GetAudioClip() != (Object)null) { boomboxParent.UpdateUIStatus("Now playing: " + title); } else { boomboxParent.UpdateUIStatus("Loading: " + title); } } } private IEnumerator ProcessDownloadQueue() { isProcessingQueue = true; Logger.LogInfo((object)"Master Client Download Queue Processor started."); while (downloadJobQueue.Count > 0 && isProcessingQueue) { string url = downloadJobQueue.Dequeue(); yield return MasterClientInitiateSync(url); if ((Object)(object)this == (Object)null) { yield break; } } if (!((Object)(object)this == (Object)null)) { isProcessingQueue = false; currentDownloadUrl = null; Logger.LogInfo((object)"Master Client Download Queue Processor finished."); } } private IEnumerator MasterClientInitiateSync(string url) { if (!boomboxParent.data.playbackQueue.Any((Boombox.AudioEntry entry) => entry.Url == url)) { yield break; } string requestId = Guid.NewGuid().ToString(); currentDownloadUrl = url; currentRequestId = requestId; if (downloadsReady.ContainsKey(url)) { if (downloadsReady[url].Count >= Instance.baseListener.GetAllModUsers().Count) { if (!boomboxParent.data.isPlaying && !boomboxParent.audioSource.isPlaying && boomboxParent.isAwaitingSyncPlayback && url == boomboxParent.data.currentSong?.Url) { boomboxParent.isAwaitingSyncPlayback = false; BaseListener.RPC(((MonoBehaviourPun)this).photonView, "PlayPausePlayback", (RpcTarget)0, boomboxParent.startPlayBackOnDownload, Boombox.GetCurrentTimeMilliseconds() - boomboxParent.data.currentSong.UseStartTime(boomboxParent) * 1000, PhotonNetwork.LocalPlayer.ActorNumber); boomboxParent.startPlayBackOnDownload = true; } yield break; } downloadsReady[url].Clear(); } else { downloadsReady[url] = new HashSet<int>(); } if (downloadErrors.ContainsKey(url)) { downloadErrors[url].Clear(); } else { downloadErrors[url] = new HashSet<int>(); } BaseListener.RPC(((MonoBehaviourPun)this).photonView, "StartDownloadAndSync", (RpcTarget)0, url, PhotonNetwork.LocalPlayer.ActorNumber); timeoutCoroutines[requestId] = ((MonoBehaviour)this).StartCoroutine(DownloadTimeoutCoroutine(requestId, url)); yield return WaitForPlayersReadyOrFailed(url); Coroutine coroutine = default(Coroutine); if ((timeoutCoroutines?.TryGetValue(requestId, out coroutine) ?? false) && coroutine != null) { ((MonoBehaviour)this).StopCoroutine(coroutine); timeoutCoroutines.Remove(requestId); } if (!(currentDownloadUrl != url)) { currentDownloadUrl = null; currentRequestId = null; Logger.LogInfo((object)("Consensus finished for " + url + ", Starting Playback.")); if ((boomboxParent?.isAwaitingSyncPlayback ?? false) && url == boomboxParent.data.currentSong?.Url) { boomboxParent.isAwaitingSyncPlayback = false; BaseListener.RPC(((MonoBehaviourPun)this).photonView, "PlayPausePlayback", (RpcTarget)0, boomboxParent.startPlayBackOnDownload, Boombox.GetCurrentTimeMilliseconds() - boomboxParent.data.currentSong.UseStartTime(boomboxParent) * 1000, PhotonNetwork.LocalPlayer.ActorNumber); boomboxParent.startPlayBackOnDownload = true; } } } private IEnumerator DownloadTimeoutCoroutine(string requestId, string url) { yield return (object)new WaitForSeconds(40f); if (currentRequestId == requestId && isProcessingQueue) { Logger.LogWarning((object)("Download timeout for url: " + url)); Logger.LogInfo((object)"Master client initiating timeout recovery"); isTimeoutRecovery = true; foreach (int player in Instance.baseListener.GetAllModUsers()) { if (!downloadsReady[url].Contains(player)) { if (!downloadErrors.ContainsKey(url)) { downloadErrors[url] = new HashSet<int>(); } downloadErrors[url].Add(player); Logger.LogWarning((object)$"Player {player} timed out during download"); } } if (downloadsReady.ContainsKey(url) && downloadsReady[url].Count > 0) { string timeoutMessage = $"Some players timed out. Continuing playback for {downloadsReady[url].Count} players."; BaseListener.RPC(((MonoBehaviourPun)this).photonView, "NotifyPlayersOfErrors", (RpcTarget)0, timeoutMessage); if (boomboxParent.isAwaitingSyncPlayback && boomboxParent.data.currentSong?.Url == url) { boomboxParent.isAwaitingSyncPlayback = false; BaseListener.RPC(((MonoBehaviourPun)this).photonView, "PlayPausePlayback", (RpcTarget)0, boomboxParent.startPlayBackOnDownload, Boombox.GetCurrentTimeMilliseconds() - boomboxParent.data.currentSong.UseStartTime(boomboxParent) * 1000, PhotonNetwork.LocalPlayer.ActorNumber); boomboxParent.startPlayBackOnDownload = true; } } else { BaseListener.RPC(((MonoBehaviourPun)this).photonView, "NotifyPlayersOfErrors", (RpcTarget)0, "Download timed out for all players."); } currentDownloadUrl = null; currentRequestId = null; isTimeoutRecovery = false; } timeoutCoroutines.Remove(requestId); } [PunRPC] public async void StartDownloadAndSync(string url, int requesterId) { if (url == null) { return; } if (currentDownloadUrl != url) { currentDownloadUrl = url; if (!downloadsReady.ContainsKey(url)) { downloadsReady[url] = new HashSet<int>(); } if (!downloadErrors.ContainsKey(url)) { downloadErrors[url] = new HashSet<int>(); } } if (downloadedClips.ContainsKey(url)) { BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber); } else if (await StartAudioDownload(url) && (Object)(object)this != (Object)null) { BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber); } } [PunRPC] public void ReportDownloadComplete(string url, int actorNumber) { if (!PhotonNetwork.IsMasterClient || url == null) { return; } if (!downloadsReady.ContainsKey(url)) { downloadsReady[url] = new HashSet<int>(); } if (downloadErrors.ContainsKey(url) && downloadErrors[url].Contains(actorNumber)) { downloadErrors[url].Remove(actorNumber); } if (!boomboxParent.isAwaitingSyncPlayback && boomboxParent.data.currentSong?.Url == url) { PhotonNetwork.CurrentRoom.Players.TryGetValue(actorNumber, out var value); if (!downloadsReady[url].Contains(actorNumber) && value != null) { Logger.LogInfo((object)$"Player {actorNumber} reported ready late. Sending start playback."); BaseListener.RPC(((MonoBehaviourPun)this).photonView, "PlayPausePlayback", value, boomboxParent.audioSource.isPlaying && boomboxParent.data.isPlaying, boomboxParent.GetRelativePlaybackMilliseconds(), PhotonNetwork.LocalPlayer.ActorNumber); } } if (!downloadsReady[url].Contains(actorNumber)) { downloadsReady[url].Add(actorNumber); } } private IEnumerator WaitForPlayersReadyOrFailed(string url) { int totalPlayers = Instance.baseListener.GetAllModUsers().Count; int readyCount = 0; int errorCount = 0; float waitTime = 0.1f; float? partialConsensusStartTime = null; while ((Object)(object)this != (Object)null) { readyCount = (downloadsReady.ContainsKey(url) ? downloadsReady[url].Count : 0); errorCount = (downloadErrors.ContainsKey(url) ? downloadErrors[url].Count : 0); bool allAccountedFor = readyCount + errorCount >= totalPlayers; bool partialConsensus = readyCount > 0 && errorCount > 0; bool timeOutReached = false; if (!partialConsensus) { partialConsensusStartTime = null; } else if (!partialConsensusStartTime.HasValue) { partialConsensusStartTime = Time.time; } else { timeOutReached = Time.time - partialConsensusStartTime.Value >= 10f; } if (allAccountedFor || timeOutReached) { break; } yield return (object)new WaitForSeconds(waitTime); } if ((Object)(object)this != (Object)null) { Logger.LogInfo((object)$"Ready to proceed with playback. Ready: {readyCount}, Errors: {errorCount}, Total: {totalPlayers} for url: {url}"); } } public static async Task<AudioClip> GetAudioClipAsync(string filePath) { await Task.Yield(); if (!File.Exists(filePath)) { throw new Exception("Audio file not found at path: " + filePath); } Uri fileUri = new Uri(filePath); string uri = fileUri.AbsoluteUri; UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(uri, (AudioType)13); try { www.timeout = 40; ((DownloadHandlerAudioClip)www.downloadHandler).streamAudio = true; TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); UnityWebRequestAsyncOperation operation = www.SendWebRequest(); ((AsyncOperation)operation).completed += delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if ((int)www.result != 1) { Logger.LogError((object)("Web request failed: " + www.error + ", URI: " + uri)); tcs.SetException(new Exception("Failed to load audio file: " + www.error)); } else { tcs.SetResult(result: true); } }; await tcs.Task; AudioClip clip = DownloadHandlerAudioClip.GetContent(www); if ((Object)(object)clip == (Object)null) { throw new Exception("Failed to get AudioClip content."); } return clip; } finally { if (www != null) { ((IDisposable)www).Dispose(); } } } public async Task<bool> StartAudioDownload(string url) { if (!downloadedClips.ContainsKey(url)) { try { string title = await YoutubeDL.DownloadAudioTitleAsync(url); songTitles[url] = title; BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetSongTitle", (RpcTarget)0, url, title); string filePath = await YoutubeDL.DownloadAudioAsync(url, title); if (boomboxParent?.data?.currentSong?.Url == url) { boomboxParent?.UpdateUIStatus("Processing audio: " + title); } AudioClip clip = await GetAudioClipAsync(filePath); downloadedClips[url] = clip; Logger.LogInfo((object)("Downloaded and cached clip for video: " + title)); } catch (Exception ex2) { Exception ex = ex2; if (boomboxParent?.data?.currentSong?.Url == url) { boomboxParent?.UpdateUIStatus("Error: " + ex.Message); } BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadError", (RpcTarget)0, PhotonNetwork.LocalPlayer.ActorNumber, url, ex.Message); return false; } } if (boomboxParent?.data?.playbackQueue != null) { foreach (Boombox.AudioEntry song in boomboxParent.data.playbackQueue.FindAll((Boombox.AudioEntry entry) => entry.Url == url)) { if (songTitles.ContainsKey(url)) { song.Title = songTitles[url]; } } } return true; } public void RemoveFromCache(string url) { if (downloadedClips.ContainsKey(url)) { AudioClip val = downloadedClips[url]; downloadedClips.Remove(url); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } if (songTitles.ContainsKey(url)) { songTitles.Remove(url); } if (downloadsReady.ContainsKey(url)) { downloadsReady.Remove(url); } if (downloadErrors.ContainsKey(url)) { downloadErrors.Remove(url); } } public override void OnPlayerLeftRoom(Player otherPlayer) { if (isProcessingQueue && !string.IsNullOrEmpty(currentDownloadUrl) && downloadsReady.ContainsKey(currentDownloadUrl) && !downloadsReady[currentDownloadUrl].Contains(otherPlayer.ActorNumber)) { if (!downloadErrors.ContainsKey(currentDownloadUrl)) { downloadErrors[currentDownloadUrl] = new HashSet<int>(); } downloadErrors[currentDownloadUrl].Add(otherPlayer.ActorNumber); Logger.LogInfo((object)$"Player {otherPlayer.ActorNumber} left during download - marking as error"); } ((MonoBehaviourPunCallbacks)this).OnPlayerLeftRoom(otherPlayer); } public void ForceCancelDownload() { if (!isProcessingQueue) { return; } string text = currentDownloadUrl; if (text == null) { text = "Unknown"; } currentDownloadUrl = null; foreach (Coroutine value in timeoutCoroutines.Values) { if (value != null) { ((MonoBehaviour)this).StopCoroutine(value); } } timeoutCoroutines.Clear(); BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadError", (RpcTarget)0, PhotonNetwork.LocalPlayer.ActorNumber, text, "Download cancelled."); Logger.LogInfo((object)"Download was force cancelled by user."); } } public class VisualEffects : MonoBehaviour { private Light frontLight; private Light backLight; private float rgbSpeed = 0.5f; private bool lightsOn = false; private void Start() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("BoomboxFrontLight"); val.transform.SetParent(((Component)this).transform); val.transform.localPosition = new Vector3(0f, 0f, 1f); frontLight = val.AddComponent<Light>(); frontLight.type = (LightType)2; frontLight.range = 6f; frontLight.intensity = 2f; ((Behaviour)frontLight).enabled = false; GameObject val2 = new GameObject("BoomboxBackLight"); val2.transform.SetParent(((Component)this).transform); val2.transform.localPosition = new Vector3(0f, 0f, -1f); backLight = val2.AddComponent<Light>(); backLight.type = (LightType)2; backLight.range = 6f; backLight.intensity = 2f; ((Behaviour)backLight).enabled = false; } private void Update() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (lightsOn) { float num = Time.time * rgbSpeed; Color color = Color.HSVToRGB(num % 1f, 1f, 1f); if ((Object)(object)frontLight != (Object)null) { frontLight.color = color; } if ((Object)(object)backLight != (Object)null) { backLight.color = color; } } } public void SetLights(bool on) { lightsOn = on; if ((Object)(object)frontLight != (Object)null) { ((Behaviour)frontLight).enabled = on; } if ((Object)(object)backLight != (Object)null) { ((Behaviour)backLight).enabled = on; } } public bool AreLightsOn() { return lightsOn; } } public class Visualizer : MonoBehaviour { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; public AudioSource audioSource; private bool visible = true; public int numBars = 16; public int spectrumSize = 128; public float radius = 0.7f; public float barWidth = 0.1f; public float barMaxHeight = 1.5f; public float barMinHeight = 0.15f; public float heightMultiplier = 24f; private float[] spectrum; private Transform[] bars; private bool lastPlaying = false; public bool IsVisible { get { return visible; } set { visible = value; for (int i = 0; i < numBars; i++) { ((Component)bars[i]).GetComponent<Renderer>().enabled = value || Instance.VisualizerBehaviourPaused.Value != BoomBoxCartMod.VisualizerPaused.Hide; } } } private void Start() { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)audioSource == (Object)null) { audioSource = ((Component)this).GetComponent<AudioSource>(); } spectrum = new float[spectrumSize]; bars = (Transform[])(object)new Transform[numBars]; Vector3 localPosition = default(Vector3); for (int i = 0; i < numBars; i++) { GameObject val = GameObject.CreatePrimitive((PrimitiveType)3); val.transform.SetParent(((Component)this).transform); float num = Mathf.Lerp(-(float)Math.PI / 2f, (float)Math.PI / 2f, (float)i / (float)(numBars - 1)); ((Vector3)(ref localPosition))..ctor(Mathf.Sin(num) * radius, 0.5f, Mathf.Cos(num) * radius + 0.7f); val.transform.localPosition = localPosition; val.transform.localScale = new Vector3(barWidth, barMinHeight, barWidth); val.GetComponent<Renderer>().material.color = Color.HSVToRGB((float)i / (float)numBars, 1f, 1f); Object.Destroy((Object)(object)val.GetComponent<Collider>()); bars[i] = val.transform; } IsVisible = audioSource.isPlaying || Instance.VisualizerBehaviourPaused.Value != BoomBoxCartMod.VisualizerPaused.Hide; lastPlaying = audioSource.isPlaying; } private void Update() { //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) AudioSource obj = audioSource; bool flag = obj != null && obj.isPlaying; if (lastPlaying != flag) { IsVisible = flag || Instance.VisualizerBehaviourPaused.Value != BoomBoxCartMod.VisualizerPaused.Hide; lastPlaying = flag; } if (!visible) { return; } if (flag) { audioSource.GetSpectrumData(spectrum, 0, (FFTWindow)4); for (int i = 0; i < numBars; i++) { int num = (int)Mathf.Pow((float)spectrumSize, (float)i / (float)numBars); int num2 = (int)Mathf.Pow((float)spectrumSize, (float)(i + 1) / (float)numBars); num2 = Mathf.Clamp(num2, num + 1, spectrumSize); float num3 = 0f; for (int j = num; j < num2; j++) { num3 += spectrum[j]; } num3 /= (float)(num2 - num); float y = Mathf.Clamp(Mathf.Pow(num3 * heightMultiplier, 0.5f), barMinHeight, barMaxHeight); Vector3 localScale = bars[i].localScale; localScale.y = y; bars[i].localScale = localScale; } } else if (Instance.VisualizerBehaviourPaused.Value == BoomBoxCartMod.VisualizerPaused.Show) { for (int k = 0; k < numBars; k++) { Vector3 localScale2 = bars[k].localScale; localScale2.y = barMinHeight; bars[k].localScale = localScale2; } } } private void OnDestroy() { if (bars == null) { return; } Transform[] array = bars; foreach (Transform val in array) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } } } [BepInPlugin("Doppelclick.BoomboxCartUpgrade", "BoomboxCartUpgrade", "1.3.1")] public class BoomBoxCartMod : BaseUnityPlugin { public enum VisualizerPaused { Show, PausePosition, Hide } public const string modGUID = "Doppelclick.BoomboxCartUpgrade"; public const string modName = "BoomboxCartUpgrade"; public const string modVersion = "1.3.1"; private readonly Harmony harmony = new Harmony("Doppelclick.BoomboxCartUpgrade"); public BaseListener baseListener = null; internal static BoomBoxCartMod instance; internal ManualLogSource logger; private bool modDisabledValue = false; public PersistentData data; public bool modDisabled { get { return modDisabledValue; } set { if (modDisabledValue != value) { logger.LogInfo((object)("Mod " + (value ? "Disabled" : "Enabled"))); } modDisabledValue = value; if (!value || data == null) { return; } foreach (Boombox allBoombox in data.GetAllBoomboxes()) { PhotonView photonView = allBoombox.photonView; Object.Destroy((Object)(object)allBoombox); if (photonView != null) { photonView.RefreshRpcMonoBehaviourCache(); } } data.GetAllBoomboxes().Clear(); data.GetBoomboxData().Clear(); } } public ConfigEntry<Key> OpenUIKey { get; private set; } public ConfigEntry<Key> GlobalMuteKey { get; private set; } public ConfigEntry<bool> MasterClientDismissQueue { get; private set; } public ConfigEntry<bool> UseTimeStampOnce { get; private set; } public ConfigEntry<bool> RestoreBoomboxes { get; private set; } public ConfigEntry<bool> AutoResume { get; private set; } public ConfigEntry<VisualizerPaused> VisualizerBehaviourPaused { get; private set; } private void Awake() { if ((Object)(object)instance == (Object)null) { instance = this; } data = new PersistentData(); logger = Logger.CreateLogSource("Doppelclick.BoomboxCartUpgrade"); logger.LogInfo((object)"BoomBoxCartMod loaded!"); harmony.PatchAll(); Task.Run(delegate { YoutubeDL.InitializeAsync().Wait(); }); OpenUIKey = ((BaseUnityPlugin)this).Config.Bind<Key>("General", "OpenUIKey", (Key)39, "Key to open the Boombox UI when grabbing a cart."); GlobalMuteKey = ((BaseUnityPlugin)this).Config.Bind<Key>("General", "GlobalMuteKey", (Key)27, "Key to mute all playback."); MasterClientDismissQueue = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "MasterClientDismissQueue", true, "Allow only the master client to dismiss the queue."); UseTimeStampOnce = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UseTimeStampOnce", false, "Only use the timestamp provided with a Url the first time the song is played."); RestoreBoomboxes = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RestoreBoomboxes", true, "Restore BoomBoxes and their Queues when you load back into a level."); AutoResume = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AutoResume", false, "Automatically resume playback when entering a new lobby."); VisualizerBehaviourPaused = ((BaseUnityPlugin)this).Config.Bind<VisualizerPaused>("General", "VisualizerPaused", VisualizerPaused.Hide, "Visualizer behaviour when music is paused."); logger.LogInfo((object)"BoomBoxCartMod initialization finished!"); } private void OnDestroy() { YoutubeDL.CleanUp(); } } } namespace BoomBoxCartMod.Util { public class BaseListener : MonoBehaviourPunCallbacks { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; public int lastUserAmount = 0; private List<int> modList = new List<int>(); public bool audioMuted = false; private bool mutePressed = false; public static bool downloaderAvailable = true; public List<int> GetAllModUsers() { return modList; } public void AddModUser(int id) { if (!modList.Contains(id)) { modList.Add(id); SendModListUpdate(); } } public void RemoveModUser(int id) { modList.Remove(id); SendModListUpdate(); } private void SendModListUpdate() { if (PhotonNetwork.IsMasterClient && PhotonNetwork.IsMasterClient) { RPC(((MonoBehaviourPun)this).photonView, "UpdateModUsers", (RpcTarget)1, modList.ToArray(), PhotonNetwork.LocalPlayer.ActorNumber); } } private void Update() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (!mutePressed && Keyboard.current != null && ((ButtonControl)Keyboard.current[Instance.GlobalMuteKey.Value]).wasPressedThisFrame) { mutePressed = true; audioMuted = !audioMuted; } else if (mutePressed && (Keyboard.current == null || ((ButtonControl)Keyboard.current[Instance.GlobalMuteKey.Value]).wasReleasedThisFrame)) { mutePressed = false; } } public static void RPC(PhotonView view, string methodName, RpcTarget target, params object[] parameters) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)view == (Object)null) { return; } if ((int)target == 0 || (int)target == 1) { List<int> allModUsers = Instance.baseListener.GetAllModUsers(); Dictionary<int, Player> players = PhotonNetwork.CurrentRoom.Players; { foreach (KeyValuePair<int, Player> item in players) { if (allModUsers.Contains(item.Key) && ((int)target == 0 || item.Key != PhotonNetwork.LocalPlayer.ActorNumber)) { view.RPC(methodName, item.Value, parameters); } } return; } } view.RPC(methodName, target, parameters); } public static void RPC(PhotonView view, string methodName, Player target, params object[] parameters) { if ((Object)(object)view != (Object)null && Instance.baseListener.GetAllModUsers().Contains(target.ActorNumber)) { view.RPC(methodName, PhotonNetwork.MasterClient, parameters); } } public static void ReportDownloaderStatus(bool available) { if (available == downloaderAvailable) { return; } Instance.logger.LogInfo((object)("Downloader status: " + (available ? "Available" : "Unavailable"))); downloaderAvailable = available; if (!available) { Instance.modDisabled = true; } if (PhotonNetwork.IsMasterClient) { if (available) { Instance.modDisabled = false; Instance.baseListener.AddModUser(PhotonNetwork.LocalPlayer.ActorNumber); } else {