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.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BoomBoxCartMod.Patches; using BoomBoxCartMod.Util; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Photon.Pun; using Photon.Realtime; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("CarretaFuracao")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("CarretaFuracao")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("37e852e0-b511-4315-8182-68c0a54e1ba9")] [assembly: AssemblyFileVersion("0.7.9.0")] [assembly: AssemblyVersion("0.7.9.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 { [BepInPlugin("LucasGPT.CarretaFuracao", "CarretaFuracao", "0.7.9")] public class BoomBoxCartMod : BaseUnityPlugin { public enum VisualizerPaused { Show, PausePosition, Hide } public enum CookieUsage { OFF, FILE, BROWSER } public enum BrowserType { chrome, firefox, edge, brave, safari } public const string modGUID = "LucasGPT.CarretaFuracao"; public const string modName = "CarretaFuracao"; public const string modVersion = "0.7.9"; private readonly Harmony harmony = new Harmony("LucasGPT.CarretaFuracao"); public BaseListener baseListener; internal static BoomBoxCartMod instance; internal ManualLogSource logger; public PersistentData data; private bool _modDisabled; private bool resourcesReinstalling; public bool initialized { get; private set; } public bool modDisabled { get { return _modDisabled; } set { if (_modDisabled != value) { logger.LogInfo((object)("Mod " + (value ? "Disabled" : "Enabled"))); } _modDisabled = 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 CookiePassthrough { get; private set; } public ConfigEntry CookiePath { get; private set; } public ConfigEntry Browser { get; private set; } public ConfigEntry DownloadSpeed { get; private set; } public ConfigEntry OpenUIKey { get; private set; } public ConfigEntry MenuKey { get; private set; } public ConfigEntry GlobalMuteKey { get; private set; } public ConfigEntry MasterClientDismissQueue { get; private set; } public ConfigEntry UseTimeStampOnce { get; private set; } public ConfigEntry RestoreBoomboxes { get; private set; } public ConfigEntry AutoResume { get; private set; } public ConfigEntry UnderglowBeatSpeed { get; private set; } public ConfigEntry UnderglowBassBias { get; private set; } public ConfigEntry VisualizerBehaviourPaused { get; private set; } public ConfigEntry SyncVisuals { get; private set; } public ConfigEntry ReinstallResources { get; private set; } private void Update() { Boombox.TickRuntime(); } private void Awake() { //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Expected O, but got Unknown //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Expected O, but got Unknown if ((Object)(object)instance == (Object)null) { instance = this; } data = new PersistentData(); logger = Logger.CreateLogSource("LucasGPT.CarretaFuracao"); logger.LogInfo((object)"BoomBoxCartMod loaded!"); logger.LogInfo((object)"CarretaFuracao 0.7.9"); AudioSettings.Initialize(((BaseUnityPlugin)this).Config); LocalMusic.Initialize(); harmony.PatchAll(); CookiePassthrough = ((BaseUnityPlugin)this).Config.Bind("Downloader", "CookiePassthrough", CookieUsage.OFF, "Pass cookies from either your browser or an exported file. (OFF, FILE, BROWSER)"); CookiePath = ((BaseUnityPlugin)this).Config.Bind("Downloader", "CustomCookiePath", "", "Path to where the browser cookies are located, if the browser selection does not work."); Browser = ((BaseUnityPlugin)this).Config.Bind("Downloader", "BrowserCookies", BrowserType.firefox, "Select the browser you are using for cookies (chrome, firefox, edge, brave, safari). This is unlikely to work."); DownloadSpeed = ((BaseUnityPlugin)this).Config.Bind("Downloader", "DownloadSpeed", 10, new ConfigDescription("Estimated download speed in Mbps (not MBps). Used for download timeout estimation.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); OpenUIKey = ((BaseUnityPlugin)this).Config.Bind("Binds", "OpenUIKey", (Key)19, "Press E to turn CarretaFuracao on/off while grabbing the cart."); MenuKey = ((BaseUnityPlugin)this).Config.Bind("Binds", "MenuKey", (Key)39, "Press Y to open/close the compact CarretaFuracao controls while grabbing the cart."); GlobalMuteKey = ((BaseUnityPlugin)this).Config.Bind("Binds", "GlobalMuteKey", (Key)27, "Key to mute all playback."); MasterClientDismissQueue = ((BaseUnityPlugin)this).Config.Bind("Queue", "MasterClientDismissQueue", true, "Allow only the master client to dismiss the queue."); UseTimeStampOnce = ((BaseUnityPlugin)this).Config.Bind("Queue", "UseTimeStampOnce", false, "Only use the timestamp provided with a Url the first time the song is played."); RestoreBoomboxes = ((BaseUnityPlugin)this).Config.Bind("Queue", "RestoreBoomboxes", true, "Restore BoomBoxes and their Queues when you load back into a level."); AutoResume = ((BaseUnityPlugin)this).Config.Bind("Queue", "AutoResume", false, "Automatically resume playback when entering a new lobby."); UnderglowBeatSpeed = ((BaseUnityPlugin)this).Config.Bind("Visual", "UnderglowBeatSpeed", 1.2f, new ConfigDescription("Scales how much detected beat energy speeds up the RGB underglow cycle. 0 = disabled, 6 = more aggressive.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 6f), Array.Empty())); UnderglowBassBias = ((BaseUnityPlugin)this).Config.Bind("Visual", "UnderglowBassBias", 0.8f, new ConfigDescription("Blends between broad beat response and isolated low-band response. 0 = broad response, 1 = strongest bass isolation.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); VisualizerBehaviourPaused = ((BaseUnityPlugin)this).Config.Bind("Visual", "VisualizerOnPaused", VisualizerPaused.Hide, "Visualizer behaviour when music is paused."); SyncVisuals = ((BaseUnityPlugin)this).Config.Bind("Visual", "SyncVisuals", true, "Sync if underglow/visualizer are enabled with the lobby, or only apply it to yourself."); ReinstallResources = ((BaseUnityPlugin)this).Config.Bind("Debug", "ReinstallResources", false, "Reinstall resources on startup."); ReinstallResources.SettingChanged += delegate(object s, EventArgs _) { if (initialized) { ConfigEntry val = (ConfigEntry)s; if (resourcesReinstalling) { if (!val.Value) { val.Value = true; } } else if (YoutubeDL.IsUpdatingResources) { logger.LogInfo((object)"Already updating resources..."); val.Value = false; } else { if (val.Value) { logger.LogInfo((object)"Reinstalling resources..."); resourcesReinstalling = true; } Task.Run(async delegate { await YoutubeDL.Reinstall(); logger.LogInfo((object)"Finished reinstalling resources."); ReinstallResources.Value = false; resourcesReinstalling = false; }); } } }; logger.LogInfo((object)"BoomBoxCartMod initialization finished!"); initialized = true; } private void Start() { if (ReinstallResources.Value) { ReinstallResources.Value = true; } else { logger.LogInfo((object)"CarretaFuracao: modo local pronto. Downloader online nao sera inicializado no startup."); } } private void OnDestroy() { YoutubeDL.CleanUp(); } } public class AudioPlayer : MonoBehaviourPunCallbacks { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; public float minDistance = 3f; public float maxDistanceBase = 15f; public float maxDistanceAddition = 30f; private static int qualityLevel = 4; private AudioLowPassFilter lowPassFilter; private AudioEchoFilter cartDelay; public AudioSource audioSource; public string currentUrl; private static ManualLogSource Logger => Instance.logger; private void Awake() { audioSource = ((Component)this).gameObject.AddComponent(); 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(); ((Behaviour)lowPassFilter).enabled = false; UpdateAudioRangeBasedOnVolume(); } private void Update() { if (Instance.baseListener.audioMuted != audioSource.mute) { audioSource.mute = Instance.baseListener.audioMuted; } } public AudioClip GetClip() { if (!((Object)(object)audioSource == (Object)null)) { return audioSource.clip; } return null; } public void SetDelayedOutput(bool delayed) { if ((Object)(object)cartDelay == (Object)null && delayed) { cartDelay = ((Component)this).gameObject.AddComponent(); cartDelay.delay = 25f; cartDelay.decayRatio = 0f; cartDelay.dryMix = 0f; cartDelay.wetMix = 1f; } if ((Object)(object)cartDelay != (Object)null) { ((Behaviour)cartDelay).enabled = delayed; } } public void SetVolume(float volume) { audioSource.volume = volume; UpdateAudioRangeBasedOnVolume(); } public void UpdateAudioRangeBasedOnVolume() { UpdateAudioRangeBasedOnVolume(audioSource.volume); } public void UpdateAudioRangeBasedOnVolume(float volume) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown float num = Mathf.Lerp(maxDistanceBase, maxDistanceBase + maxDistanceAddition, volume); audioSource.minDistance = minDistance; audioSource.maxDistance = num; AnimationCurve val = new AnimationCurve((Keyframe[])(object)new Keyframe[3] { new Keyframe(0f, 1f), new Keyframe(minDistance, 0.9f), new Keyframe(num, 0f) }); audioSource.SetCustomCurve((AudioSourceCurveType)0, val); } public void SetClip(string url) { if (url != null && (!(url == currentUrl) || !((Object)(object)audioSource.clip != (Object)null)) && DownloadHelper.downloadedClips.TryGetValue(url, out var value) && (Object)(object)value != (Object)null) { audioSource.clip = value; currentUrl = url; } } public void SetQuality(int level) { qualityLevel = Mathf.Clamp(level, 0, 4); switch (qualityLevel) { case 0: ((Behaviour)lowPassFilter).enabled = true; lowPassFilter.cutoffFrequency = 1500f; break; case 1: ((Behaviour)lowPassFilter).enabled = true; lowPassFilter.cutoffFrequency = 3000f; break; case 2: ((Behaviour)lowPassFilter).enabled = true; lowPassFilter.cutoffFrequency = 4500f; break; case 3: ((Behaviour)lowPassFilter).enabled = true; lowPassFilter.cutoffFrequency = 6000f; break; case 4: ((Behaviour)lowPassFilter).enabled = false; break; } } public static int GetQuality() { return qualityLevel; } public float GetTime() { return audioSource.time; } public void SetTime(float time) { if ((Object)(object)audioSource.clip != (Object)null && time > audioSource.clip.length) { time = Mathf.Max(0f, audioSource.clip.length - 0.05f); Logger.LogDebug((object)$"SetTime: Clamped time from {audioSource.time} to {time} (clip length: {audioSource.clip.length})"); } audioSource.time = time; } public bool IsPlaying() { return audioSource.isPlaying; } public bool SongReachedEnd() { if ((Object)(object)audioSource.clip != (Object)null) { return audioSource.time >= Math.Max(0f, audioSource.clip.length - 0.05f); } return false; } public void Play() { audioSource.Play(); } public void Pause() { audioSource.Pause(); } public void Stop() { if (!((Object)(object)audioSource == (Object)null)) { audioSource.Stop(); audioSource.clip = null; currentUrl = null; } } private void OnDestroy() { Stop(); Object.Destroy((Object)(object)lowPassFilter); if ((Object)(object)cartDelay != (Object)null) { Object.Destroy((Object)(object)cartDelay); } Object.Destroy((Object)(object)audioSource); } } public class Boombox : MonoBehaviourPunCallbacks { private class SharedAudioEntry { public string title; public string url; public double duration; public int startTime; } public class BoomboxData { public string key = Guid.NewGuid().ToString(); public AudioEntry currentSong; public List playbackQueue = new List(); public bool isPlaying; public float absVolume = AudioSettings.MasterValue; public float personalVolumePercentage = AudioSettings.PersonalValue; public bool loopQueue; public bool delayedOutput; public bool underglowEnabled; public bool visualizerEnabled = true; public bool pendingPlaybackStart; public int stateVersion; public int playbackTime; public int playbackStartTimestamp; } public class AudioEntry { public string Title; public string Url; public double Duration = -1.0; public int StartTime; public AudioEntry(string url, SongInfo info) { Url = url; Title = info.title; Duration = info.duration; } private int PeekStartTime(Boombox boombox) { if (boombox.data != null && boombox.data.playbackTime != 0) { return boombox.data.playbackTime; } return StartTime; } public int UseStartTime(Boombox boombox) { int result = PeekStartTime(boombox); if (boombox.data != null && boombox.data.playbackTime != 0) { boombox.data.playbackTime = 0; return result; } if (Instance.UseTimeStampOnce.Value) { StartTime = 0; } return result; } public bool ClipLoaded() { if (DownloadHelper.downloadedClips.ContainsKey(Url)) { return (Object)(object)DownloadHelper.downloadedClips[Url] != (Object)null; } return false; } } public PhotonView photonView; public AudioPlayer audioPlayer; public Visualizer visualizer; public VisualEffects visualEffects; public DownloadHelper downloadHelper; private bool syncFinished; public bool startPlayBackOnDownload = true; public BoomboxData data = new BoomboxData(); private static bool mutePressed = false; private bool isApplyingSharedState; private string pendingCurrentSongDownloadUrl; private int lastAppliedSharedStateVersion = -1; private Hashtable lastSharedState; private static readonly List levelPlaylistOrderKeys = new List(); private static int levelPlaylistIndex = 0; private static bool levelHasStartedPlayback = false; private static readonly HashSet liveCarretas = new HashSet(); private static int lastRuntimeFrame = -1; private static int lastGlobalAdvanceServerTimestamp = int.MinValue; private float nextFollowerSyncTime; private bool runtimeStartLogged; private string nextPreparedUrl; private float monsterAttractTimer; private const float monsterAttractInterval = 1f; private static bool applyQualityToDownloads = false; private static bool monstersCanHearMusic = false; private static BoomBoxCartMod Instance => BoomBoxCartMod.instance; private static ManualLogSource Logger => Instance.logger; public static bool ApplyQualityToDownloads { get { return applyQualityToDownloads; } set { applyQualityToDownloads = value; } } public static bool MonstersCanHearMusic { get { return monstersCanHearMusic; } set { monstersCanHearMusic = value; } } public bool LoopQueue { get { return data.loopQueue; } set { data.loopQueue = value; } } private void Awake() { audioPlayer = ((Component)this).gameObject.AddComponent(); downloadHelper = ((Component)this).gameObject.AddComponent(); photonView = ((Component)this).GetComponent(); if ((Object)(object)photonView == (Object)null) { Logger.LogError((object)"PhotonView not found on Boombox object."); return; } if ((Object)(object)((Component)this).GetComponent() == (Object)null) { ((Component)this).gameObject.AddComponent(); } if ((Object)(object)((Component)this).GetComponent() == (Object)null) { visualizer = ((Component)this).gameObject.AddComponent(); visualizer.audioSource = audioPlayer.audioSource; } if ((Object)(object)((Component)this).GetComponent() == (Object)null) { visualEffects = ((Component)this).gameObject.AddComponent(); } RegisterRuntimeCart(this); PersistentData.SetBoomboxViewInitialized(photonView.ViewID); Logger.LogInfo((object)$"Boombox initialized on this cart. AudioPlayer: {audioPlayer}, PhotonView: {photonView}"); } private void Start() { audioPlayer.SetVolume(data.absVolume * data.personalVolumePercentage); audioPlayer.SetDelayedOutput(data.delayedOutput); ApplyVisualStateFromData(); LoadSharedStateFromRoom(); } public static void ResetLevelPlaylistSession() { levelPlaylistOrderKeys.Clear(); levelPlaylistIndex = 0; levelHasStartedPlayback = false; lastGlobalAdvanceServerTimestamp = int.MinValue; if (!PhotonNetwork.IsMasterClient) { return; } List list = new List(LocalMusic.Tracks); Random random = new Random(Guid.NewGuid().GetHashCode()); for (int num = list.Count - 1; num > 0; num--) { int index = random.Next(num + 1); LocalMusic.LocalTrack value = list[num]; list[num] = list[index]; list[index] = value; } foreach (LocalMusic.LocalTrack item in list) { if (item != null && !string.IsNullOrWhiteSpace(item.Key)) { levelPlaylistOrderKeys.Add(item.Key); } } BoomBoxCartMod instance = BoomBoxCartMod.instance; if (instance != null) { ManualLogSource logger = instance.logger; if (logger != null) { logger.LogInfo((object)("CarretaFuracao: nova ordem do level criada com " + levelPlaylistOrderKeys.Count + " faixa(s).")); } } int i; for (i = 0; i < levelPlaylistOrderKeys.Count; i++) { LocalMusic.LocalTrack localTrack = LocalMusic.Tracks.FirstOrDefault((LocalMusic.LocalTrack item) => item != null && item.Key == levelPlaylistOrderKeys[i]); BoomBoxCartMod instance2 = BoomBoxCartMod.instance; if (instance2 != null) { ManualLogSource logger2 = instance2.logger; if (logger2 != null) { logger2.LogInfo((object)(" ORDEM " + (i + 1) + ": " + ((localTrack != null) ? localTrack.Title : levelPlaylistOrderKeys[i]))); } } } } private void EnsureLevelPlaylistSession() { if (PhotonNetwork.IsMasterClient && (levelPlaylistOrderKeys.Count != LocalMusic.Tracks.Count || levelPlaylistOrderKeys.Any((string key) => FindLocalTrackByKey(key) == null))) { ResetLevelPlaylistSession(); } } private int GetLevelPlaylistIndexForKey(string key) { if (string.IsNullOrWhiteSpace(key)) { return -1; } return levelPlaylistOrderKeys.IndexOf(key); } public static void RegisterRuntimeCart(Boombox cart) { if (!((Object)(object)cart == (Object)null) && liveCarretas.Add(cart)) { Logger.LogInfo((object)("CarretaFuracao REGISTER cart=" + cart.photonView.ViewID)); } } public static void TickRuntime() { if (Instance == null || Instance.modDisabled || lastRuntimeFrame == Time.frameCount) { return; } lastRuntimeFrame = Time.frameCount; foreach (Boombox activeCarreta in GetActiveCarretas()) { try { activeCarreta.TickPlayback(); } catch (Exception ex) { Logger.LogError((object)("CarretaFuracao runtime cart=" + activeCarreta.photonView.ViewID + ": " + ex)); } } } public static List GetRuntimeCarretas() { return liveCarretas.Where((Boombox cart) => (Object)(object)cart != (Object)null && cart.data != null).ToList(); } private async void PrepareNextTrack() { if (AudioSettings.Preload == null || !AudioSettings.Preload.Value || data.currentSong == null || !data.currentSong.ClipLoaded() || data.playbackQueue.Count < 2) { return; } int currentSongIndex = GetCurrentSongIndex(); if (currentSongIndex < 0) { return; } string url = data.playbackQueue[(currentSongIndex + 1) % data.playbackQueue.Count].Url; if (url == nextPreparedUrl || !LocalMusic.IsLocalUrl(url)) { return; } nextPreparedUrl = url; try { await LocalClipCache.Load(url); } catch (Exception ex) { Logger.LogWarning((object)("CarretaFuracao preload: " + ex.Message)); } } private static List GetActiveCarretas() { return (from cart in liveCarretas where (Object)(object)cart != (Object)null && cart.data != null && cart.data.currentSong != null && cart.data.playbackQueue != null && cart.data.playbackQueue.Count > 0 && (cart.data.isPlaying || cart.data.pendingPlaybackStart) orderby (!((Object)(object)cart.photonView != (Object)null)) ? int.MaxValue : cart.photonView.ViewID select cart).ToList(); } private static Boombox GetActiveLeader() { return GetActiveCarretas().FirstOrDefault(); } private Boombox GetOtherActiveCarreta() { return GetActiveCarretas().FirstOrDefault((Boombox cart) => (Object)(object)cart != (Object)null && (Object)(object)cart != (Object)(object)this); } private bool EnsureQueueUsesLevelOrder() { EnsureLevelPlaylistSession(); bool flag = data.playbackQueue != null && data.playbackQueue.Count == levelPlaylistOrderKeys.Count; if (flag) { for (int i = 0; i < levelPlaylistOrderKeys.Count; i++) { AudioEntry audioEntry = data.playbackQueue[i]; if (audioEntry == null || audioEntry.Url != levelPlaylistOrderKeys[i]) { flag = false; break; } } } if (flag) { return false; } data.playbackQueue.Clear(); foreach (string levelPlaylistOrderKey in levelPlaylistOrderKeys) { SongInfo info = (DownloadHelper.songInfo.ContainsKey(levelPlaylistOrderKey) ? DownloadHelper.songInfo[levelPlaylistOrderKey] : new SongInfo()); data.playbackQueue.Add(new AudioEntry(levelPlaylistOrderKey, info)); } data.currentSong = null; return true; } private void ApplySyncedLevelTrackInternal(int index, int sharedPlaybackStartTimestamp, bool startPlaying) { if (PhotonNetwork.IsMasterClient) { bool updateQueue = EnsureQueueUsesLevelOrder(); if (data.playbackQueue != null && data.playbackQueue.Count != 0) { index = (index % data.playbackQueue.Count + data.playbackQueue.Count) % data.playbackQueue.Count; RegisterRuntimeCart(this); runtimeStartLogged = false; levelHasStartedPlayback = true; levelPlaylistIndex = index; data.currentSong = data.playbackQueue[index]; StopLocalPlayback(updateSharedFlag: true); data.playbackTime = 0; data.pendingPlaybackStart = false; data.isPlaying = startPlaying; data.loopQueue = true; data.playbackStartTimestamp = sharedPlaybackStartTimestamp; startPlayBackOnDownload = true; ApplyNativeCarretaLoopState(); PublishSharedState(updateTime: true, updateQueue, force: true); ApplySharedPlaybackState(); } } } private static void SyncActiveCarretasToIndex(int index, int sharedPlaybackStartTimestamp, Boombox includeIfNone = null) { if (!PhotonNetwork.IsMasterClient) { return; } List activeCarretas = GetActiveCarretas(); if (activeCarretas.Count == 0 && (Object)(object)includeIfNone != (Object)null) { activeCarretas.Add(includeIfNone); } foreach (Boombox item in activeCarretas) { if (!((Object)(object)item == (Object)null)) { item.ApplySyncedLevelTrackInternal(index, sharedPlaybackStartTimestamp, startPlaying: true); } } } private void StartOrJoinSynchronizedPlaybackInternal() { if (!PhotonNetwork.IsMasterClient) { return; } RegisterRuntimeCart(this); EnsureLevelPlaylistSession(); if (levelPlaylistOrderKeys.Count == 0) { Logger.LogWarning((object)(photonView.ViewID + " CarretaFuracao: nenhum arquivo local encontrado.")); return; } Boombox otherActiveCarreta = GetOtherActiveCarreta(); data.delayedOutput = (Object)(object)otherActiveCarreta != (Object)null; int num = (((Object)(object)otherActiveCarreta != (Object)null) ? otherActiveCarreta.GetCurrentSongIndex() : levelPlaylistIndex); if (num < 0) { num = levelPlaylistIndex; } int index = (levelHasStartedPlayback ? ((num + 1) % levelPlaylistOrderKeys.Count) : 0); int sharedPlaybackStartTimestamp = (int)GetCurrentServerTimeMilliseconds(); foreach (Boombox activeCarreta in GetActiveCarretas()) { if ((Object)(object)activeCarreta != (Object)(object)this) { activeCarreta.ApplySyncedLevelTrackInternal(index, sharedPlaybackStartTimestamp, startPlaying: true); } } ApplySyncedLevelTrackInternal(index, sharedPlaybackStartTimestamp, startPlaying: true); Logger.LogInfo((object)(photonView.ViewID + " CarretaFuracao POWER NEXT: index " + index)); } private static void MoveSynchronizedPlaylist(Boombox requester, int direction) { if (PhotonNetwork.IsMasterClient && !((Object)(object)requester == (Object)null) && levelPlaylistOrderKeys.Count != 0) { Boombox activeLeader = GetActiveLeader(); int num = (((Object)(object)activeLeader != (Object)null) ? activeLeader.GetCurrentSongIndex() : levelPlaylistIndex); if (num < 0) { num = levelPlaylistIndex; } int index = ((num + direction) % levelPlaylistOrderKeys.Count + levelPlaylistOrderKeys.Count) % levelPlaylistOrderKeys.Count; int sharedPlaybackStartTimestamp = (int)GetCurrentServerTimeMilliseconds(); levelPlaylistIndex = index; SyncActiveCarretasToIndex(index, sharedPlaybackStartTimestamp, requester); } } private static void AdvanceSynchronizedPlaylistAtEnd(Boombox requester) { if (!PhotonNetwork.IsMasterClient || (Object)(object)requester == (Object)null || levelPlaylistOrderKeys.Count == 0 || (Object)(object)GetActiveLeader() != (Object)(object)requester) { return; } int num = (int)GetCurrentServerTimeMilliseconds(); if (lastGlobalAdvanceServerTimestamp == int.MinValue || (uint)(num - lastGlobalAdvanceServerTimestamp) >= 500u) { lastGlobalAdvanceServerTimestamp = num; int currentSongIndex = requester.GetCurrentSongIndex(); if (currentSongIndex < 0) { currentSongIndex = levelPlaylistIndex; } int num2 = (levelPlaylistIndex = (currentSongIndex + 1) % levelPlaylistOrderKeys.Count); SyncActiveCarretasToIndex(num2, num); Logger.LogInfo((object)(requester.photonView.ViewID + " CarretaFuracao PLAYLIST NEXT: " + (num2 + 1) + "/" + levelPlaylistOrderKeys.Count)); } } private static void SyncActiveCarretasSeek(float playbackSeconds, Boombox requester) { if (!PhotonNetwork.IsMasterClient || (Object)(object)requester == (Object)null) { return; } float num = Math.Max(0f, playbackSeconds); int playbackStartTimestamp = (int)((double)GetCurrentServerTimeMilliseconds() - Math.Round(num * 1000f)); List activeCarretas = GetActiveCarretas(); if (activeCarretas.Count == 0) { activeCarretas.Add(requester); } foreach (Boombox item in activeCarretas) { if (!((Object)(object)item == (Object)null) && item.data != null && item.data.currentSong != null) { item.data.playbackStartTimestamp = playbackStartTimestamp; item.data.playbackTime = 0; item.data.pendingPlaybackStart = false; item.data.isPlaying = true; if ((Object)(object)item.audioPlayer != (Object)null && (Object)(object)item.audioPlayer.GetClip() != (Object)null) { item.audioPlayer.SetTime(num); } item.PublishSharedState(updateTime: true, updateQueue: false, force: true); } } } private void ApplyFollowerCartLocalSync() { if (Time.unscaledTime < nextFollowerSyncTime || !data.isPlaying || data.currentSong == null || (Object)(object)audioPlayer == (Object)null || (Object)(object)audioPlayer.GetClip() == (Object)null) { return; } nextFollowerSyncTime = Time.unscaledTime + 0.25f; float num = PlaybackElapsedSeconds(data.playbackStartTimestamp); if (!(num >= audioPlayer.GetClip().length)) { if (!audioPlayer.IsPlaying()) { audioPlayer.Play(); } if (Math.Abs(audioPlayer.GetTime() - num) > 0.08f) { audioPlayer.SetTime(num); } } } private void TickPlayback() { //IL_0119: Unknown result type (might be due to invalid IL or missing references) if (!runtimeStartLogged) { runtimeStartLogged = true; Logger.LogInfo((object)("CarretaFuracao CLOCK cart=" + photonView.ViewID + " componentEnabled=" + ((Behaviour)this).enabled + " objectActive=" + ((Component)this).gameObject.activeInHierarchy + " host=" + PhotonNetwork.IsMasterClient + " position=" + PlaybackElapsedSeconds(data.playbackStartTimestamp).ToString("0.000") + "s")); } if (PhotonNetwork.IsMasterClient && data.isPlaying && MonstersCanHearMusic && (Object)(object)EnemyDirector.instance != (Object)null) { monsterAttractTimer += Time.deltaTime; if (monsterAttractTimer >= 1f) { EnemyDirector.instance.SetInvestigate(((Component)this).transform.position, 5f, false); monsterAttractTimer = 0f; } } else { monsterAttractTimer = 0f; } ApplyFollowerCartLocalSync(); PrepareNextTrack(); if (PhotonNetwork.IsMasterClient && data.isPlaying && data.currentSong != null && (Object)(object)audioPlayer != (Object)null && (Object)(object)audioPlayer.GetClip() != (Object)null) { float length = audioPlayer.GetClip().length; float val = Math.Max(0f, audioPlayer.GetTime()); float val2 = Math.Max(0f, PlaybackElapsedSeconds(data.playbackStartTimestamp)); float num = Math.Max(val, val2); if (length > 0.05f && num >= length - 0.05f) { AdvanceSynchronizedPlaylistAtEnd(this); } } } public void ToggleRandomJukeboxLocal() { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestToggleRandomJukebox", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber); } else if (data != null && (data.isPlaying || data.pendingPlaybackStart)) { StopRandomJukeboxInternal(); } else { StartRandomJukeboxInternal(); } } private void StopRandomJukeboxInternal() { if (PhotonNetwork.IsMasterClient) { string text = data?.currentSong?.Title ?? "(nenhuma)"; Logger.LogInfo((object)(photonView.ViewID + " CarretaFuracao E JUKEBOX: STOP | " + text)); int currentSongIndex = GetCurrentSongIndex(); if (currentSongIndex >= 0) { levelPlaylistIndex = currentSongIndex; } DismissQueueLocal(); } } private LocalMusic.LocalTrack FindLocalTrackByKey(string key) { if (string.IsNullOrWhiteSpace(key)) { return null; } return LocalMusic.Tracks.FirstOrDefault((LocalMusic.LocalTrack track) => track != null && track.Key == key); } private void StartRandomJukeboxInternal() { StartOrJoinSynchronizedPlaybackInternal(); } public void PlayPreviousRandomJukeboxLocal() { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestPreviousRandomJukebox", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber); } else { PlayPreviousRandomJukeboxInternal(); } } private void PlayPreviousRandomJukeboxInternal() { if (PhotonNetwork.IsMasterClient) { EnsureLevelPlaylistSession(); MoveSynchronizedPlaylist(this, -1); } } public void PlayNextRandomJukeboxLocal() { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestNextRandomJukebox", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber); } else { PlayNextRandomJukeboxInternal(); } } private void PlayNextRandomJukeboxInternal() { if (PhotonNetwork.IsMasterClient) { EnsureLevelPlaylistSession(); MoveSynchronizedPlaylist(this, 1); } } [PunRPC] public void RequestPreviousRandomJukebox(int requesterId) { if (PhotonNetwork.IsMasterClient) { PlayPreviousRandomJukeboxInternal(); } } [PunRPC] public void RequestNextRandomJukebox(int requesterId) { if (PhotonNetwork.IsMasterClient) { PlayNextRandomJukeboxInternal(); } } [PunRPC] public void RequestToggleRandomJukebox(int requesterId) { if (PhotonNetwork.IsMasterClient) { ToggleRandomJukeboxLocal(); } } public void TogglePlaying(bool value) { data.isPlaying = value; } public static long GetCurrentServerTimeMilliseconds() { return PhotonNetwork.ServerTimestamp; } public static float PlaybackElapsedSeconds(int startedAt) { return Math.Max(0f, (float)(PhotonNetwork.ServerTimestamp - startedAt) / 1000f); } public long GetRelativePlaybackMilliseconds() { return GetCurrentServerTimeMilliseconds() - (long)Math.Round(GetTrackedPlaybackSeconds() * 1000f); } public int GetCurrentSongIndex() { if (data.currentSong == null) { return -1; } return data.playbackQueue.IndexOf(data.currentSong); } private string GetPropertyKey(string propertyName) { if ((Object)(object)photonView == (Object)null) { return propertyName; } return $"boombox_{photonView.ViewID}_{propertyName}"; } private Hashtable CreateSharedState(bool includeTime, bool updateQueue) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown //IL_0156: Expected O, but got Unknown Hashtable val = new Hashtable(); ((Dictionary)val).Add((object)GetPropertyKey("propVersion"), (object)data.stateVersion); ((Dictionary)val).Add((object)GetPropertyKey("IDKey"), (object)data.key); ((Dictionary)val).Add((object)GetPropertyKey("isPlaying"), (object)(data.currentSong != null && data.isPlaying)); ((Dictionary)val).Add((object)GetPropertyKey("pendingPlaybackStart"), (object)data.pendingPlaybackStart); ((Dictionary)val).Add((object)GetPropertyKey("absVolume"), (object)data.absVolume); ((Dictionary)val).Add((object)GetPropertyKey("loopQueue"), (object)data.loopQueue); ((Dictionary)val).Add((object)GetPropertyKey("delayedOutput"), (object)data.delayedOutput); ((Dictionary)val).Add((object)GetPropertyKey("underglow"), (object)data.underglowEnabled); ((Dictionary)val).Add((object)GetPropertyKey("visualizer"), (object)data.visualizerEnabled); ((Dictionary)val).Add((object)GetPropertyKey("currentSongIndex"), (object)GetCurrentSongIndex()); Hashtable val2 = val; if (includeTime) { ((Dictionary)(object)val2).Add((object)GetPropertyKey("playbackStartTimestamp"), (object)data.playbackStartTimestamp); } if (updateQueue) { ((Dictionary)(object)val2).Add((object)GetPropertyKey("queue"), (object)JsonConvert.SerializeObject((object)data.playbackQueue.Select((AudioEntry entry) => new SharedAudioEntry { title = entry.Title, url = entry.Url, duration = entry.Duration, startTime = entry.StartTime }).ToArray())); Logger.LogDebug((object)(photonView.ViewID + string.Format("Created table with queue size: {0}", JsonConvert.DeserializeObject((string)val2[(object)GetPropertyKey("queue")]).Count()))); } return val2; } private void LoadSharedStateFromRoom() { if (PhotonNetwork.IsConnected && PhotonNetwork.CurrentRoom != null && !((Object)(object)photonView == (Object)null)) { Hashtable customProperties = ((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties; ApplyChangedValues(customProperties); if (PhotonNetwork.IsMasterClient && ((Dictionary)(object)customProperties).Count > 0) { PublishSharedState(updateTime: true, updateQueue: true, force: true); } } } public override void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged) { ((MonoBehaviourPunCallbacks)this).OnRoomPropertiesUpdate(propertiesThatChanged); if (!((Object)(object)photonView == (Object)null) && propertiesThatChanged != null && !PhotonNetwork.IsMasterClient) { ApplyChangedValues(propertiesThatChanged); } } private void ApplyChangedValues(Hashtable propertiesThatChanged) { isApplyingSharedState = true; string propertyKey = GetPropertyKey("propVersion"); if (!((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey)) { isApplyingSharedState = false; return; } int num = (int)propertiesThatChanged[(object)propertyKey]; if (num < data.stateVersion || num <= lastAppliedSharedStateVersion) { isApplyingSharedState = false; return; } int currentSongIndex = GetCurrentSongIndex(); string propertyKey2 = GetPropertyKey("queue"); bool flag = ((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey2); try { if (flag) { SharedAudioEntry[] array = JsonConvert.DeserializeObject((string)propertiesThatChanged[(object)propertyKey2]); if (array != null) { data.playbackQueue = array.Select(delegate(SharedAudioEntry entry) { string title = (string.IsNullOrWhiteSpace(entry.title) ? "Unknown Title" : entry.title); _ = entry.duration; double duration = entry.duration; string url = entry.url; SongInfo info = new SongInfo(title, duration); return new AudioEntry(url, info) { StartTime = entry.startTime }; }).ToList(); Logger.LogDebug((object)(photonView.ViewID + $"Updated queue to size {data.playbackQueue.Count}.")); } else { data.playbackQueue = new List(); } } string propertyKey3 = GetPropertyKey("currentSongIndex"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey3)) { int num2 = (int)propertiesThatChanged[(object)propertyKey3]; if (num2 >= 0 && num2 < data.playbackQueue.Count) { data.currentSong = data.playbackQueue[num2]; } else { data.currentSong = null; data.pendingPlaybackStart = false; audioPlayer.Stop(); UpdateUIStatus("Ready to play music! Enter a Video URL"); Logger.LogDebug((object)(photonView.ViewID + $"Invalid currentsong index: {num2}.")); } } else if (flag) { data.currentSong = data.playbackQueue[currentSongIndex]; } string propertyKey4 = GetPropertyKey("pendingPlaybackStart"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey4)) { bool pendingPlaybackStart = (bool)propertiesThatChanged[(object)propertyKey4]; data.pendingPlaybackStart = pendingPlaybackStart; } string propertyKey5 = GetPropertyKey("isPlaying"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey5)) { bool flag2 = (bool)propertiesThatChanged[(object)propertyKey5]; data.isPlaying = data.currentSong != null && flag2; } string propertyKey6 = GetPropertyKey("absVolume"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey6)) { float num3 = Mathf.Clamp01(Convert.ToSingle((float)propertiesThatChanged[(object)propertyKey6])); AudioSettings.RememberMaster(num3); if (data.absVolume != num3) { data.absVolume = num3; float volume = num3 * data.personalVolumePercentage; audioPlayer.SetVolume(volume); } } string propertyKey7 = GetPropertyKey("delayedOutput"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey7)) { data.delayedOutput = (bool)propertiesThatChanged[(object)propertyKey7]; } string propertyKey8 = GetPropertyKey("loopQueue"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey8)) { bool loopQueue = (bool)propertiesThatChanged[(object)propertyKey8]; data.loopQueue = loopQueue; } string propertyKey9 = GetPropertyKey("underglow"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey9) && Instance.SyncVisuals.Value) { bool underglowEnabled = (bool)propertiesThatChanged[(object)propertyKey9]; data.underglowEnabled = underglowEnabled; ApplyVisualStateFromData(); ((Component)this).GetComponent()?.UpdateDataFromBoomBox(); } string propertyKey10 = GetPropertyKey("visualizer"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey10) && Instance.SyncVisuals.Value) { bool visualizerEnabled = (bool)propertiesThatChanged[(object)propertyKey10]; data.visualizerEnabled = visualizerEnabled; ApplyVisualStateFromData(); ((Component)this).GetComponent()?.UpdateDataFromBoomBox(); } string propertyKey11 = GetPropertyKey("playbackStartTimestamp"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey11)) { int num4 = (int)propertiesThatChanged[(object)propertyKey11]; data.playbackStartTimestamp = num4; SetPlaybackTime(num4); } string propertyKey12 = GetPropertyKey("IDKey"); if (((Dictionary)(object)propertiesThatChanged).ContainsKey((object)propertyKey12)) { string text = (string)propertiesThatChanged[(object)propertyKey12]; if (!string.IsNullOrWhiteSpace(text)) { data.key = text; } } } catch (Exception ex) { Logger.LogWarning((object)("Error occured while reading properties: " + ex.Message)); } ApplySharedPlaybackState(); ((Component)this).GetComponent()?.UpdateDataFromBoomBox(); data.stateVersion = num; lastAppliedSharedStateVersion = num; syncFinished = true; isApplyingSharedState = false; } private bool ShouldUseNativeCarretaLoop() { return false; } private void ApplyNativeCarretaLoopState() { if (!((Object)(object)audioPlayer == (Object)null) && !((Object)(object)audioPlayer.audioSource == (Object)null)) { bool flag = ShouldUseNativeCarretaLoop(); if (audioPlayer.audioSource.loop != flag) { audioPlayer.audioSource.loop = flag; Logger.LogInfo((object)(photonView.ViewID + " CarretaFuracao NATIVE LOOP: " + (flag ? "ON" : "OFF"))); } } } private void ApplySharedPlaybackState() { ApplyNativeCarretaLoopState(); audioPlayer.SetVolume(data.absVolume * data.personalVolumePercentage); audioPlayer.SetDelayedOutput(data.delayedOutput); if (data.currentSong?.Url == null) { Logger.LogDebug((object)(photonView.ViewID + "ApplySharedPlaybackState: No current song" + ((data.currentSong == null) ? "" : "URL") + "!")); data.pendingPlaybackStart = false; audioPlayer.Stop(); UpdateUIStatus("Ready to play music! Enter a Video URL"); return; } AudioEntry currentSong = data.currentSong; if (currentSong == null || !currentSong.ClipLoaded()) { audioPlayer.Stop(); startPlayBackOnDownload = data.pendingPlaybackStart; if (data.pendingPlaybackStart) { UpdateUIStatus("Loading: " + data.currentSong.Title); } if (!data.pendingPlaybackStart) { EnsureCurrentSongDownloaded(); } return; } if (data.currentSong.Url != audioPlayer.currentUrl) { audioPlayer.Stop(); audioPlayer.SetClip(data.currentSong.Url); audioPlayer.SetQuality(AudioPlayer.GetQuality()); audioPlayer.UpdateAudioRangeBasedOnVolume(); } ApplyVisualStateFromData(); if (data.pendingPlaybackStart) { if (audioPlayer.IsPlaying()) { audioPlayer.Pause(); } UpdateUIStatus("Loading: " + data.currentSong.Title); } else if (data.isPlaying) { if (!audioPlayer.IsPlaying() && PlaybackElapsedSeconds(data.playbackStartTimestamp) < audioPlayer.GetClip().length) { Logger.LogDebug((object)(photonView.ViewID + $"ApplySharedPlaybackState: Starting playback - timestamp={data.playbackStartTimestamp}")); audioPlayer.Play(); SetPlaybackTime(data.playbackStartTimestamp); Logger.LogDebug((object)(photonView.ViewID + $"ApplySharedPlaybackState: After Play(), audioPlayer.GetTime()={audioPlayer.GetTime()}")); } UpdateUIStatus("Now playing: " + data.currentSong.Title); } else { if (audioPlayer.IsPlaying()) { audioPlayer.Pause(); } UpdateUIStatus("Ready to play: " + data.currentSong.Title); } } public void ApplyVisualStateFromData() { if ((Object)(object)visualEffects == (Object)null) { visualEffects = ((Component)this).gameObject.AddComponent(); if ((Object)(object)visualizer != (Object)null) { visualizer.audioSource = audioPlayer.audioSource; } } visualEffects.SetLights(data.underglowEnabled); if (data.visualizerEnabled) { if ((Object)(object)visualizer == (Object)null) { visualizer = ((Component)this).gameObject.AddComponent(); } visualizer.audioSource = audioPlayer.audioSource; } else if ((Object)(object)visualizer != (Object)null) { Object.Destroy((Object)(object)visualizer); visualizer = null; } } private async void EnsureCurrentSongDownloaded() { string url = data.currentSong?.Url; if (string.IsNullOrWhiteSpace(url) || (Object)(object)downloadHelper == (Object)null || DownloadHelper.downloadedClips.ContainsKey(url) || pendingCurrentSongDownloadUrl == url) { return; } pendingCurrentSongDownloadUrl = url; try { if (await downloadHelper.StartAudioDownload(url)) { BaseListener.RPC(photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber); } } finally { if (pendingCurrentSongDownloadUrl == url) { pendingCurrentSongDownloadUrl = null; } } if (!((Object)(object)this == (Object)null) && !(data.currentSong?.Url != url)) { ApplySharedPlaybackState(); } } private void UpdateStatusFromState() { if (data.currentSong == null) { UpdateUIStatus("Ready to play music! Enter a Video URL"); } else if (data.pendingPlaybackStart) { UpdateUIStatus("Loading: " + data.currentSong.Title); } else if (data.isPlaying) { UpdateUIStatus("Now playing: " + data.currentSong.Title); } else { UpdateUIStatus("Ready to play: " + data.currentSong.Title); } } public bool CanPublish() { if (PhotonNetwork.IsMasterClient && !isApplyingSharedState && PhotonNetwork.IsConnected && PhotonNetwork.CurrentRoom != null) { return (Object)(object)photonView != (Object)null; } return false; } public void PublishSharedState(bool updateTime, bool updateQueue = false, bool force = false) { if (CanPublish()) { if (PhotonNetwork.IsMasterClient) { data.stateVersion++; } Hashtable val = CreateSharedState(updateTime, updateQueue); PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); lastSharedState = val; syncFinished = true; } } private void StopLocalPlayback(bool updateSharedFlag) { audioPlayer.Stop(); if (updateSharedFlag) { TogglePlaying(value: false); } } private void SetPlaybackReferenceFromSeconds(float totalSeconds) { float num = Math.Max(0f, totalSeconds); data.playbackStartTimestamp = (int)((double)GetCurrentServerTimeMilliseconds() - Math.Round(num * 1000f)); Logger.LogDebug((object)(photonView.ViewID + $"SetPlaybackReferenceFromSeconds: Set to {num}s, timestamp={data.playbackStartTimestamp}")); } private float GetTrackedPlaybackSeconds() { if ((Object)(object)audioPlayer?.GetClip() != (Object)null) { if (!audioPlayer.IsPlaying() && data.playbackTime > 0 && audioPlayer.GetTime() <= 0f) { Logger.LogDebug((object)(photonView.ViewID + $"GetTrackedPlaybackSeconds: Returning cached playbackTime={data.playbackTime} (not playing, time <= 0)")); return data.playbackTime; } float time = audioPlayer.GetTime(); Logger.LogDebug((object)(photonView.ViewID + $"GetTrackedPlaybackSeconds: Returning audioPlayer.GetTime()={time}, isPlaying={audioPlayer.IsPlaying()}")); return time; } if (data.playbackTime > 0) { Logger.LogDebug((object)(photonView.ViewID + $"GetTrackedPlaybackSeconds: Returning cached playbackTime={data.playbackTime} (no clip)")); return data.playbackTime; } float num = Math.Max(0f, PlaybackElapsedSeconds(data.playbackStartTimestamp)); Logger.LogDebug((object)(photonView.ViewID + $"GetTrackedPlaybackSeconds: Calculating from timestamp: {num}s")); return num; } private void CleanupCurrentPlayback() { StopLocalPlayback(updateSharedFlag: true); } private bool ShouldRequestMasterMutation() { if (PhotonNetwork.IsConnected && PhotonNetwork.CurrentRoom != null) { return !PhotonNetwork.IsMasterClient; } return false; } public void SetPlaybackTime(long relativeStartTimeMillis) { float num = Math.Max(0f, PlaybackElapsedSeconds((int)relativeStartTimeMillis)); Logger.LogDebug((object)(photonView.ViewID + $"SetPlaybackTime: Setting audioPlayer time to {num}s (from timestamp {relativeStartTimeMillis})")); audioPlayer.SetTime(num); } public void EnqueueSongLocal(string url, int seconds) { if (string.IsNullOrWhiteSpace(url)) { return; } if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestEnqueueSong", (RpcTarget)2, url, seconds, PhotonNetwork.LocalPlayer.ActorNumber); return; } SongInfo info = (DownloadHelper.songInfo.ContainsKey(url) ? DownloadHelper.songInfo[url] : new SongInfo()); AudioEntry audioEntry = new AudioEntry(url, info) { StartTime = seconds }; data.playbackQueue.Add(audioEntry); bool flag = data.currentSong == null; if (flag) { data.currentSong = audioEntry; data.playbackTime = 0; Logger.LogDebug((object)(photonView.ViewID + $"Set currentsong local {data.currentSong}")); StopLocalPlayback(updateSharedFlag: true); data.pendingPlaybackStart = true; startPlayBackOnDownload = true; SetPlaybackReferenceFromSeconds(audioEntry.UseStartTime(this)); } PublishSharedState(flag, updateQueue: true); if (PhotonNetwork.IsMasterClient) { downloadHelper.EnqueueDownload(url); downloadHelper.StartDownloadJob(); } } public void CommitPlaybackSeek() { if (data.currentSong != null) { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestCommitPlaybackSeek", (RpcTarget)2, audioPlayer.GetTime(), PhotonNetwork.LocalPlayer.ActorNumber); } else { SyncActiveCarretasSeek(audioPlayer.GetTime(), this); } } } public void SetPlaybackStateLocal(bool startPlaying) { if (data.currentSong != null) { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestSetPlaybackState", (RpcTarget)2, startPlaying, PhotonNetwork.LocalPlayer.ActorNumber); } else if (!data.currentSong.ClipLoaded()) { startPlayBackOnDownload = startPlaying; data.isPlaying = false; data.pendingPlaybackStart = startPlaying; PublishSharedState(updateTime: true); } else { float trackedPlaybackSeconds = GetTrackedPlaybackSeconds(); Logger.LogDebug((object)(photonView.ViewID + $"SetPlaybackStateLocal: trackedPlaybackSeconds={trackedPlaybackSeconds}, isPlaying={startPlaying}")); SetPlaybackReferenceFromSeconds(trackedPlaybackSeconds); Logger.LogDebug((object)(photonView.ViewID + $"SetPlaybackStateLocal: Final state - isPlaying={startPlaying}, timestamp={data.playbackStartTimestamp}")); data.isPlaying = startPlaying; data.pendingPlaybackStart = false; PublishSharedState(updateTime: true, updateQueue: false, force: true); } } } public void JumpPlaybackBySeconds(float seconds) { if (!data.currentSong.ClipLoaded()) { return; } if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestJumpPlaybackBySeconds", (RpcTarget)2, seconds, PhotonNetwork.LocalPlayer.ActorNumber); return; } float num = Math.Max(0f, GetTrackedPlaybackSeconds() + seconds); if ((Object)(object)audioPlayer?.GetClip() != (Object)null) { num = Math.Min(num, Math.Max(0f, audioPlayer.GetClip().length - 0.5f)); } SetPlaybackReferenceFromSeconds(num); data.pendingPlaybackStart = false; PublishSharedState(updateTime: true); } public void SelectSongIndex(int index) { if (index < 0 || index >= data.playbackQueue.Count) { return; } if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestSelectSongIndex", (RpcTarget)2, index, PhotonNetwork.LocalPlayer.ActorNumber); return; } data.currentSong = data.playbackQueue[index]; StopLocalPlayback(updateSharedFlag: true); data.playbackTime = 0; data.pendingPlaybackStart = true; startPlayBackOnDownload = true; PublishSharedState(updateTime: true); if (PhotonNetwork.IsMasterClient) { string text = data.currentSong?.Url; if (text != null && DownloadHelper.downloadsReady.ContainsKey(text) && DownloadHelper.downloadsReady[text].Count >= Instance.baseListener.GetAllModUsers().Count && data.pendingPlaybackStart) { Logger.LogDebug((object)(photonView.ViewID + "Skipping download queue, as all users are ready to play.")); FinalizePendingPlaybackStart(startPlayBackOnDownload); } else { downloadHelper.DismissDownloadQueue(); downloadHelper.DownloadQueue(index); } } } public void SetVolumeLocal(float volume) { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestSetVolume", (RpcTarget)2, volume, PhotonNetwork.LocalPlayer.ActorNumber); return; } AudioSettings.RememberMaster(volume); foreach (Boombox runtimeCarreta in GetRuntimeCarretas()) { if (!((Object)(object)runtimeCarreta == (Object)(object)this)) { runtimeCarreta.data.absVolume = Mathf.Clamp01(volume); if ((Object)(object)runtimeCarreta.audioPlayer != (Object)null) { runtimeCarreta.audioPlayer.SetVolume(runtimeCarreta.data.absVolume * runtimeCarreta.data.personalVolumePercentage); } runtimeCarreta.PublishSharedState(updateTime: false); } } data.absVolume = Mathf.Clamp01(volume); float volume2 = data.absVolume * data.personalVolumePercentage; audioPlayer.SetVolume(volume2); PublishSharedState(updateTime: false); } public void SetLoopQueueLocal(bool loop) { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestSetLoopQueue", (RpcTarget)2, loop, PhotonNetwork.LocalPlayer.ActorNumber); } else { data.loopQueue = loop; ApplyNativeCarretaLoopState(); PublishSharedState(updateTime: false); } } public void SetUnderglowEnabledLocal(bool enabled) { if (Instance.SyncVisuals.Value) { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestSetUnderglowEnabled", (RpcTarget)2, enabled, PhotonNetwork.LocalPlayer.ActorNumber); } else { data.underglowEnabled = enabled; ApplyVisualStateFromData(); ((Component)this).GetComponent()?.UpdateDataFromBoomBox(); PublishSharedState(updateTime: false); } } else { data.underglowEnabled = enabled; ApplyVisualStateFromData(); ((Component)this).GetComponent()?.UpdateDataFromBoomBox(); } } public void SetVisualizerEnabledLocal(bool enabled) { if (Instance.SyncVisuals.Value) { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestSetVisualizerEnabled", (RpcTarget)2, enabled, PhotonNetwork.LocalPlayer.ActorNumber); } else { data.visualizerEnabled = enabled; ApplyVisualStateFromData(); ((Component)this).GetComponent()?.UpdateDataFromBoomBox(); PublishSharedState(updateTime: false); } } else { data.visualizerEnabled = enabled; ApplyVisualStateFromData(); ((Component)this).GetComponent()?.UpdateDataFromBoomBox(); } } public void DismissQueueLocal() { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestDismissQueue", (RpcTarget)2, PhotonNetwork.LocalPlayer.ActorNumber); return; } if ((Object)(object)audioPlayer != (Object)null && (Object)(object)audioPlayer.audioSource != (Object)null) { audioPlayer.audioSource.loop = false; } StopLocalPlayback(updateSharedFlag: true); data.playbackQueue.Clear(); data.currentSong = null; data.pendingPlaybackStart = false; data.playbackTime = 0; SetPlaybackReferenceFromSeconds(0f); PublishSharedState(updateTime: true, updateQueue: true); if (PhotonNetwork.IsMasterClient) { downloadHelper.DismissDownloadQueue(); downloadHelper.ForceCancelDownload(); } UpdateUIStatus("Ready to play music! Enter a Video URL"); } public void MoveQueueItemLocal(int index, int newIndex) { if (index >= 0 && index < data.playbackQueue.Count && newIndex >= 0 && newIndex < data.playbackQueue.Count && index != newIndex) { if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestMoveQueueItem", (RpcTarget)2, index, newIndex, PhotonNetwork.LocalPlayer.ActorNumber); } else { AudioEntry item = data.playbackQueue[index]; data.playbackQueue.RemoveAt(index); data.playbackQueue.Insert(newIndex, item); PublishSharedState(updateTime: false, updateQueue: true); } } } public void RemoveQueueItemLocal(int index) { if (index < 0 || index >= data.playbackQueue.Count) { return; } if (ShouldRequestMasterMutation()) { BaseListener.RPC(photonView, "RequestRemoveQueueItem", (RpcTarget)2, index, PhotonNetwork.LocalPlayer.ActorNumber); return; } bool flag = data.playbackQueue[index] == data.currentSong; data.playbackQueue.RemoveAt(index); if (flag) { if (data.playbackQueue.Count == 0) { data.currentSong = null; data.isPlaying = false; data.playbackTime = 0; data.pendingPlaybackStart = false; SetPlaybackReferenceFromSeconds(0f); } else { int index2 = Math.Min(index, data.playbackQueue.Count - 1); if (index >= data.playbackQueue.Count && LoopQueue) { index2 = 0; } data.currentSong = data.playbackQueue[index2]; data.isPlaying = false; data.playbackTime = 0; data.pendingPlaybackStart = true; startPlayBackOnDownload = true; SetPlaybackReferenceFromSeconds(data.currentSong.UseStartTime(this)); } } if (PhotonNetwork.IsMasterClient && data.currentSong?.Url != null) { downloadHelper.DismissDownloadQueue(); downloadHelper.DownloadQueue(GetCurrentSongIndex()); } PublishSharedState(flag, updateQueue: true); } public void FinalizePendingPlaybackStart(bool startPlaying) { if (data.currentSong != null) { data.pendingPlaybackStart = false; data.isPlaying = startPlaying; SetPlaybackReferenceFromSeconds(data.currentSong.UseStartTime(this)); startPlayBackOnDownload = true; PublishSharedState(updateTime: true, updateQueue: false, force: true); } } [PunRPC] public void RequestEnqueueSong(string url, int seconds, int requesterId) { if (PhotonNetwork.IsMasterClient) { EnqueueSongLocal(url, seconds); } } [PunRPC] public void RequestCommitPlaybackSeek(float playbackSeconds, int requesterId) { if (PhotonNetwork.IsMasterClient && data.currentSong != null) { SyncActiveCarretasSeek(playbackSeconds, this); } } [PunRPC] public void RequestSetPlaybackState(bool startPlaying, int requesterId) { if (PhotonNetwork.IsMasterClient) { SetPlaybackStateLocal(startPlaying); } } [PunRPC] public void RequestJumpPlaybackBySeconds(float seconds, int requesterId) { if (PhotonNetwork.IsMasterClient) { JumpPlaybackBySeconds(seconds); } } [PunRPC] public void RequestSelectSongIndex(int index, int requesterId) { if (PhotonNetwork.IsMasterClient) { SelectSongIndex(index); } } [PunRPC] public void RequestSetVolume(float volume, int requesterId) { if (PhotonNetwork.IsMasterClient) { SetVolumeLocal(volume); } } [PunRPC] public void RequestSetLoopQueue(bool loop, int requesterId) { if (PhotonNetwork.IsMasterClient) { SetLoopQueueLocal(loop); } } [PunRPC] public void RequestSetUnderglowEnabled(bool enabled, int requesterId) { if (PhotonNetwork.IsMasterClient && Instance.SyncVisuals.Value) { SetUnderglowEnabledLocal(enabled); } } [PunRPC] public void RequestSetVisualizerEnabled(bool enabled, int requesterId) { if (PhotonNetwork.IsMasterClient && Instance.SyncVisuals.Value) { SetVisualizerEnabledLocal(enabled); } } [PunRPC] public void RequestDismissQueue(int requesterId) { if (PhotonNetwork.IsMasterClient && (!Instance.MasterClientDismissQueue.Value || PhotonNetwork.MasterClient.ActorNumber == requesterId)) { DismissQueueLocal(); } } [PunRPC] public void RequestMoveQueueItem(int index, int newIndex, int requesterId) { if (PhotonNetwork.IsMasterClient) { MoveQueueItemLocal(index, newIndex); } } [PunRPC] public void RequestRemoveQueueItem(int index, int requesterId) { if (PhotonNetwork.IsMasterClient) { RemoveQueueItemLocal(index); } } public void HandleDownloadedCurrentSong() { AudioEntry currentSong = data.currentSong; if (currentSong != null && currentSong.ClipLoaded()) { ApplySharedPlaybackState(); } } public double EstimateDownloadTimeSeconds(double durationSeconds, int downloadSpeedMbps) { double num = (double)DownloadHelper.GetBitrateKbps(ApplyQualityToDownloads ? 4 : AudioPlayer.GetQuality()) * 1000.0 / 8.0 * durationSeconds; double num2 = (double)Math.Max(1, downloadSpeedMbps) / 8.0 * 1024.0 * 1024.0; double num3 = num / num2 * 2.5; double num4 = durationSeconds * 0.005; return (5.0 + num3 + num4) * 1.3; } public void UpdateUIStatus(string message) { if (!((Object)(object)this == (Object)null)) { BoomboxUI component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null && component.IsUIVisible()) { component.UpdateStatus(message); } } } public override void OnPlayerEnteredRoom(Player newPlayer) { ((MonoBehaviourPunCallbacks)this).OnPlayerEnteredRoom(newPlayer); if (PhotonNetwork.IsMasterClient && !Instance.modDisabled) { PhotonView obj = ((MonoBehaviourPun)Instance.baseListener).photonView; if (obj != null) { obj.RPC("BaseListener", newPlayer, new object[2] { "0.7.9", PhotonNetwork.LocalPlayer.ActorNumber }); } } } public override void OnDisable() { ((MonoBehaviourPunCallbacks)this).OnDisable(); PersistentData.RemoveBoomboxViewInitialized(photonView.ViewID); data.playbackTime = (int)Math.Round(audioPlayer.GetTime()); } private void OnDestroy() { liveCarretas.Remove(this); Instance.data.GetAllBoomboxes().Remove(this); Object.Destroy((Object)(object)((Component)this).GetComponent()); if ((Object)(object)visualizer != (Object)null) { Object.Destroy((Object)(object)visualizer); } Object.Destroy((Object)(object)((Component)this).gameObject.GetComponent()); Object.Destroy((Object)(object)audioPlayer); Object.Destroy((Object)(object)downloadHelper); Object.Destroy((Object)(object)((Component)this).gameObject.GetComponent()); photonView.RefreshRpcMonoBehaviourCache(); } public void ResetData() { if (PhotonNetwork.IsMasterClient) { Logger.LogInfo((object)$"Resetting Boombox {photonView.ViewID}"); startPlayBackOnDownload = true; data.pendingPlaybackStart = false; UpdateUIStatus("Ready to play music! Enter a Video URL"); CleanupCurrentPlayback(); downloadHelper.DismissDownloadQueue(); List boomboxData = Instance.data.GetBoomboxData(); int index = boomboxData.Count; if (boomboxData.Contains(data)) { index = boomboxData.IndexOf(data); boomboxData.Remove(data); } int stateVersion = data.stateVersion; data = new BoomboxData(); data.stateVersion = stateVersion + 1; ApplyVisualStateFromData(); if (Instance.RestoreBoomboxes.Value) { boomboxData.Insert(index, data); } PublishSharedState(updateTime: true, updateQueue: true, force: true); } } } public class BoomboxController : MonoBehaviourPun { private static BoomboxController openPanel; private BoomboxUI panel; public void RequestBoomboxControl() { if ((Object)(object)panel != (Object)null && panel.IsUIVisible()) { ReleaseControl(); } else if (PlayerGrabbingTracker.IsLocalPlayerGrabbingCart(((Component)this).gameObject)) { if ((Object)(object)openPanel != (Object)null && (Object)(object)openPanel != (Object)(object)this) { openPanel.ReleaseControl(); } if ((Object)(object)panel == (Object)null) { panel = ((Component)this).GetComponent() ?? ((Component)this).gameObject.AddComponent(); } panel.ShowUI(); openPanel = this; } } public void ReleaseControl() { if ((Object)(object)panel != (Object)null) { panel.HideUI(); } if ((Object)(object)openPanel == (Object)(object)this) { openPanel = null; } } public void LocalPlayerReleasedCart() { ReleaseControl(); } private void OnDisable() { ReleaseControl(); } private void OnDestroy() { ReleaseControl(); } } public class BoomboxUI : MonoBehaviourPun { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; public PhotonView photonView; public static bool anyUISHown = false; public bool showUI; private string urlInput = ""; private bool isTimeSliderBeingDragged; private int songIndexForTime = -2; private float songTimePerc; private float lastSentSongTimePerc = -1f; private float lastSentVolume = 0.2f; private bool isVolumeSliderBeingDragged; private bool isIndividualVolumeBeingDragged; private int qualityLevel = 3; private string[] qualityLabels = new string[5] { "REALLY Low (You Freak)", "Low", "Medium-Low", "Medium-High", "High" }; private bool isQualitySliderBeingDragged; 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; public string statusMessage = ""; private CursorLockMode previousLockMode; private bool previousCursorVisible; private bool stylesInitialized; private Vector2 scrollPosition = Vector2.zero; private Vector2 queueScrollPosition = Vector2.zero; private const float refreshHoldTime = 5f; private const int maxRefreshSymbols = 5; private float? refreshObjectStart; private bool refreshObjectSent; private string lastUrl; private static ManualLogSource Logger => Instance.logger; private void Awake() { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) try { boombox = ((Component)this).GetComponent(); if ((Object)(object)boombox != (Object)null) { photonView = boombox.photonView; } else { Logger.LogError((object)"BoomboxUI: Failed to find Boombox component"); photonView = ((Component)this).GetComponent(); } controller = ((Component)this).GetComponent(); visualEffects = ((Component)this).GetComponent(); if ((Object)(object)visualEffects == (Object)null) { visualEffects = ((Component)this).gameObject.AddComponent(); } visualizer = ((Component)this).GetComponent(); boombox?.ApplyVisualStateFromData(); RefreshVisualComponentRefs(); if ((Object)(object)photonView == (Object)null) { Logger.LogError((object)"BoomboxUI: Failed to find PhotonView component"); } windowRect = new Rect((float)(Screen.width / 2 - 230), (float)(Screen.height / 2 - 170), 460f, 340f); } 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; AudioSettings.SetPersonal(boombox.data.personalVolumePercentage); } 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (!showUI) { if ((Object)(object)boombox == (Object)null || (Object)(object)photonView == (Object)null) { Logger.LogError((object)"Cannot show UI - boombox or photonView is null"); return; } anyUISHown = true; showUI = true; previousLockMode = Cursor.lockState; previousCursorVisible = Cursor.visible; Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; UpdateDataFromBoomBox(); UpdateStatusFromBoombox(); } } public void UpdateDataFromBoomBox() { if ((Object)(object)boombox != (Object)null) { lastSentVolume = boombox.data.absVolume; qualityLevel = AudioPlayer.GetQuality(); lastSentQualityLevel = qualityLevel; RefreshVisualComponentRefs(); } } private void RefreshVisualComponentRefs() { visualEffects = ((Component)this).GetComponent(); visualizer = ((Component)this).GetComponent(); } public void UpdateStatusFromBoombox() { if ((Object)(object)boombox != (Object)null) { if (YoutubeDL.IsUpdatingResources) { statusMessage = YoutubeDL.ResourceUpdateStatus; } else if (boombox.downloadHelper.IsProcessingQueue() && boombox.data.currentSong != null && !boombox.data.currentSong.ClipLoaded()) { statusMessage = "Downloading audio from " + boombox.downloadHelper.GetCurrentDownloadUrl() + "..."; } else if (boombox.data.currentSong != null && boombox.data.pendingPlaybackStart) { statusMessage = "Loading: " + boombox.data.currentSong.Title; } else if (boombox.data.currentSong != null && boombox.data.isPlaying) { statusMessage = "Now playing: " + boombox.data.currentSong.Title; } else if (!string.IsNullOrEmpty(boombox.data.currentSong?.Url)) { statusMessage = "Ready to play: " + boombox.data.currentSong.Title; } else { statusMessage = "Ready to play music! Enter a Video URL"; } } } private void SendTimeUpdate() { if (songTimePerc != lastSentSongTimePerc) { lastSentSongTimePerc = songTimePerc; if (boombox?.GetCurrentSongIndex() == songIndexForTime && songIndexForTime != -1 && (Object)(object)boombox?.audioPlayer?.GetClip() != (Object)null && boombox.audioPlayer.GetClip().length > 0f) { float time = Math.Max(0f, Math.Min(songTimePerc * boombox.audioPlayer.GetClip().length, boombox.audioPlayer.GetClip().length - 0.05f)); boombox.audioPlayer.SetTime(time); boombox.CommitPlaybackSeek(); } else if (songIndexForTime != boombox.GetCurrentSongIndex()) { UpdateDataFromBoomBox(); } } } private void SendVolumeUpdate() { if (boombox.data.absVolume != lastSentVolume) { lastSentVolume = boombox.data.absVolume; boombox.SetVolumeLocal(boombox.data.absVolume); } } private void SendQualityUpdate() { if (qualityLevel != lastSentQualityLevel) { lastSentQualityLevel = qualityLevel; if ((Object)(object)boombox != (Object)null) { boombox.audioPlayer?.SetQuality(qualityLevel); } } } public void HideUI() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (showUI) { if ((Object)(object)boombox != (Object)null && isVolumeSliderBeingDragged) { isVolumeSliderBeingDragged = false; SendVolumeUpdate(); } if ((Object)(object)boombox != (Object)null && isIndividualVolumeBeingDragged) { isIndividualVolumeBeingDragged = false; AudioSettings.SetPersonal(boombox.data.personalVolumePercentage); } 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_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, color); val.Apply(); return val; } private void InitializeStyles() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Expected O, but got Unknown //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Expected O, but got Unknown //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Expected O, but got Unknown //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Expected O, but got Unknown //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Expected O, but got Unknown //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Expected O, but got Unknown //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Expected O, but got Unknown //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Expected O, but got Unknown //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Expected O, but got Unknown //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Expected O, but got Unknown //IL_0390: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Expected O, but got Unknown //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Expected O, but got Unknown //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_03e6: Expected O, but got Unknown //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_03fb: Expected O, but got Unknown //IL_0406: Unknown result type (might be due to invalid IL or missing references) //IL_0440: Unknown result type (might be due to invalid IL or missing references) //IL_044a: Expected O, but got Unknown //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_0471: Expected O, but got Unknown //IL_0495: Unknown result type (might be due to invalid IL or missing references) //IL_049f: Expected O, but got Unknown //IL_04a6: Unknown result type (might be due to invalid IL or missing references) //IL_04b0: Expected O, but got Unknown //IL_04d0: Unknown result type (might be due to invalid IL or missing references) //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_0521: Unknown result type (might be due to invalid IL or missing references) //IL_052b: Expected O, but got Unknown //IL_054b: Unknown result type (might be due to invalid IL or missing references) //IL_0565: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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) if (showUI) { if (!stylesInitialized) { InitializeStyles(); } windowRect = GUILayout.Window(0, windowRect, new WindowFunction(DrawUI), "CarretaFuracao", windowStyle, Array.Empty()); } } private void DrawUI(int windowID) { if (boombox?.data != null) { DrawCompactPanel(boombox); GUI.DragWindow(); } } private void DrawCompactPanel(Boombox boombox) { GUILayout.Space(6f); float num = 0f; float num2 = 0f; float num3 = 0f; string text = "??"; bool flag = (Object)(object)boombox.audioPlayer?.GetClip() != (Object)null; if (flag) { num = boombox.audioPlayer.audioSource.time; num2 = boombox.audioPlayer.GetClip().length; if (num2 > 0f) { num3 = num / num2; text = PrintTime(num2); } else { flag = false; } } GUILayout.Label(PrintTime(num) + " / " + text, labelStyle, Array.Empty()); float num4 = GUILayout.HorizontalSlider(num3, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty()); int currentSongIndex = boombox.GetCurrentSongIndex(); if (flag && currentSongIndex != -1 && num4 != num3) { isTimeSliderBeingDragged = true; if (songIndexForTime == -2) { songIndexForTime = currentSongIndex; } if (songIndexForTime == currentSongIndex) { float time = Math.Max(0f, Math.Min(num4 * num2, boombox.audioPlayer.GetClip().length - 0.05f)); boombox.audioPlayer.audioSource.time = time; songTimePerc = num4; } } GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("◀ Anterior", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { boombox.PlayPreviousRandomJukeboxLocal(); } if (GUILayout.Button("Pular ▶", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { boombox.PlayNextRandomJukeboxLocal(); } GUILayout.EndHorizontal(); GUILayout.Space(14f); GUILayout.Label($"Volume Master: {Mathf.Round(boombox.data.absVolume * 100f)}%", labelStyle, Array.Empty()); float num5 = GUILayout.HorizontalSlider(boombox.data.absVolume, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty()); if (num5 != boombox.data.absVolume) { isVolumeSliderBeingDragged = true; boombox.data.absVolume = num5; if ((Object)(object)boombox.audioPlayer?.audioSource != (Object)null) { boombox.audioPlayer.SetVolume(boombox.data.absVolume * boombox.data.personalVolumePercentage); } } GUILayout.Space(12f); GUILayout.Label($"Volume Pessoal: {Mathf.Round(boombox.data.personalVolumePercentage * 100f)}%", labelStyle, Array.Empty()); float num6 = GUILayout.HorizontalSlider(boombox.data.personalVolumePercentage, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty()); if (num6 != boombox.data.personalVolumePercentage) { isIndividualVolumeBeingDragged = true; boombox.data.personalVolumePercentage = num6; if ((Object)(object)boombox.audioPlayer?.audioSource != (Object)null) { boombox.audioPlayer.SetVolume(boombox.data.absVolume * boombox.data.personalVolumePercentage); } } GUILayout.Space(10f); GUILayout.Label("E: ligar/desligar", statusStyle, Array.Empty()); GUILayout.Space(8f); if (GUILayout.Button("Fechar", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) })) { if ((Object)(object)controller != (Object)null) { controller.ReleaseControl(); } else { HideUI(); } } } private void DrawMainPanel(Boombox boombox) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_05ea: Unknown result type (might be due to invalid IL or missing references) //IL_0564: Unknown result type (might be due to invalid IL or missing references) //IL_0584: Unknown result type (might be due to invalid IL or missing references) //IL_05f1: Unknown result type (might be due to invalid IL or missing references) //IL_05f6: Unknown result type (might be due to invalid IL or missing references) //IL_05ff: Unknown result type (might be due to invalid IL or missing references) //IL_0701: Unknown result type (might be due to invalid IL or missing references) //IL_0708: Unknown result type (might be due to invalid IL or missing references) //IL_070d: Unknown result type (might be due to invalid IL or missing references) //IL_0716: Unknown result type (might be due to invalid IL or missing references) //IL_07fe: Unknown result type (might be due to invalid IL or missing references) //IL_0805: Unknown result type (might be due to invalid IL or missing references) //IL_080a: Unknown result type (might be due to invalid IL or missing references) //IL_0813: 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()); 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()); urlScrollPosition = GUILayout.BeginScrollView(urlScrollPosition, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(60f) }); urlInput = GUILayout.TextField(urlInput, textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) }); urlInput = Regex.Replace(urlInput, "\\s+", ""); GUILayout.EndScrollView(); GUILayout.EndHorizontal(); float num = 0f; float num2 = 0f; float num3 = 0f; string text = "??"; bool flag = (Object)(object)boombox != (Object)null && (Object)(object)boombox.audioPlayer?.GetClip() != (Object)null; if (flag) { num = boombox.audioPlayer.audioSource.time; num2 = boombox.audioPlayer.GetClip().length; if (num2 > 0f) { num3 = num / num2; text = PrintTime(num2); } else { flag = false; } } GUILayout.Label(PrintTime(num) + " / " + text, labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); Rect lastRect; if (flag && (int)Event.current.type == 0) { lastRect = GUILayoutUtility.GetLastRect(); if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition)) { isTimeSliderBeingDragged = true; } } float num4 = GUILayout.HorizontalSlider(num3, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty()); int currentSongIndex = boombox.GetCurrentSongIndex(); if (flag && currentSongIndex != -1 && num4 != num3) { if (!isTimeSliderBeingDragged) { isTimeSliderBeingDragged = true; } if (songIndexForTime == -2) { songIndexForTime = currentSongIndex; } if (songIndexForTime == currentSongIndex) { float time = Math.Max(0f, Math.Min(num4 * num2, boombox.audioPlayer.GetClip().length - 0.05f)); boombox.audioPlayer.audioSource.time = time; songTimePerc = num4; } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("<<", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(40f), GUILayout.Height(40f) }) && (Object)(object)boombox != (Object)null && boombox.data.currentSong != null) { boombox.JumpPlaybackBySeconds(-10f); } if (GUILayout.Button((boombox?.data != null && boombox.data.playbackQueue.Count > 0) ? "+ ENQUEUE" : "▶ PLAY", buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) })) { var (text2, seconds) = IsValidVideoUrl(urlInput); if (string.IsNullOrEmpty(text2)) { ShowErrorMessage("Invalid Video URL!"); } else if (lastUrl != text2) { lastUrl = text2; boombox.EnqueueSongLocal(text2, seconds); GUI.FocusControl((string)null); } } if (GUILayout.Button((boombox.data.currentSong == null) ? "..." : (boombox.data.isPlaying ? "▌▌ PAUSE" : "▶ RESUME"), buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }) && boombox.data.currentSong != null) { boombox.SetPlaybackStateLocal(!boombox.data.isPlaying); } if (GUILayout.Button(">>", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(40f), GUILayout.Height(40f) }) && (Object)(object)boombox != (Object)null && boombox.data.currentSong != null) { boombox.JumpPlaybackBySeconds(10f); } GUILayout.EndHorizontal(); if ((Object)(object)boombox != (Object)null && boombox.downloadHelper.IsProcessingQueue()) { Boombox.AudioEntry currentSong = boombox.data.currentSong; if ((currentSong == null || !currentSong.ClipLoaded()) && boombox.downloadHelper.GetCurrentDownloadUrl() != null) { GUILayout.Space(10f); GUILayout.Label("Download in progress...", statusStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); 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()); GUILayout.Space(5f); } if (!string.IsNullOrEmpty(errorMessage)) { GUI.color = Color.red; GUILayout.Label(errorMessage, labelStyle, Array.Empty()); GUI.color = Color.white; GUILayout.Space(5f); } GUILayout.Space(15f); float num5 = boombox.data.absVolume * 100f; GUILayout.Label($"Volume: {Mathf.Round(num5)}%", labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if ((int)Event.current.type == 0) { lastRect = GUILayoutUtility.GetLastRect(); if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition)) { isVolumeSliderBeingDragged = true; } } float num6 = GUILayout.HorizontalSlider(boombox.data.absVolume, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty()); if (num6 != boombox.data.absVolume) { if (!isVolumeSliderBeingDragged) { isVolumeSliderBeingDragged = true; } if ((Object)(object)boombox.audioPlayer?.audioSource != (Object)null) { float volume = boombox.data.absVolume * boombox.data.personalVolumePercentage; boombox.audioPlayer.SetVolume(volume); } boombox.data.absVolume = num6; } GUILayout.EndHorizontal(); GUILayout.Space(15f); GUILayout.Label($"Personal Volume Multiplier: {Mathf.Round(boombox.data.personalVolumePercentage * 100.001f)}%", labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if ((int)Event.current.type == 0) { lastRect = GUILayoutUtility.GetLastRect(); if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition)) { isIndividualVolumeBeingDragged = true; } } float num7 = GUILayout.HorizontalSlider(boombox.data.personalVolumePercentage, 0f, 1f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty()); if (num7 != boombox.data.personalVolumePercentage) { isIndividualVolumeBeingDragged = true; boombox.data.personalVolumePercentage = num7; if ((Object)(object)boombox.audioPlayer?.audioSource != (Object)null) { boombox.audioPlayer.SetVolume(boombox.data.absVolume * boombox.data.personalVolumePercentage); } } GUILayout.EndHorizontal(); GUILayout.Space(15f); GUILayout.Label("Audio Quality: " + qualityLabels[qualityLevel], labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if ((int)Event.current.type == 0) { lastRect = GUILayoutUtility.GetLastRect(); if (((Rect)(ref lastRect)).Contains(Event.current.mousePosition)) { isQualitySliderBeingDragged = true; } } int num8 = Mathf.RoundToInt(GUILayout.HorizontalSlider((float)qualityLevel, 0f, 4f, sliderStyle, GUI.skin.horizontalSliderThumb, Array.Empty())); if (num8 != qualityLevel && !isQualitySliderBeingDragged) { isQualitySliderBeingDragged = true; } qualityLevel = num8; if ((Object)(object)boombox?.audioPlayer != (Object)null) { boombox.audioPlayer.SetQuality(qualityLevel); } GUILayout.EndHorizontal(); GUILayout.Space(10f); bool applyQualityToDownloads = Boombox.ApplyQualityToDownloads; bool flag2 = GUILayout.Toggle(applyQualityToDownloads, "Apply Quality Setting to Downloads", Array.Empty()); if (flag2 != applyQualityToDownloads) { Boombox.ApplyQualityToDownloads = flag2; } GUILayout.Space(10f); bool monstersCanHearMusic = Boombox.MonstersCanHearMusic; bool flag3 = GUILayout.Toggle(monstersCanHearMusic, "Monsters can hear audio", Array.Empty()); if (flag3 != monstersCanHearMusic) { Boombox.MonstersCanHearMusic = flag3; } GUILayout.Space(10f); bool loopQueue = boombox.LoopQueue; bool flag4 = GUILayout.Toggle(loopQueue, "Loop queue", Array.Empty()); if (flag4 != loopQueue && PhotonNetwork.IsMasterClient) { boombox.SetLoopQueueLocal(flag4); } GUILayout.Space(10f); bool underglowEnabled = boombox.data.underglowEnabled; bool flag5 = GUILayout.Toggle(underglowEnabled, "RGB Underglow enabled", Array.Empty()); if (flag5 != underglowEnabled) { boombox.SetUnderglowEnabledLocal(flag5); UpdateDataFromBoomBox(); } GUILayout.Space(10f); bool visualizerEnabled = boombox.data.visualizerEnabled; bool flag6 = GUILayout.Toggle(visualizerEnabled, "Audio Visualizer enabled", Array.Empty()); if (flag6 != visualizerEnabled) { boombox.SetVisualizerEnabledLocal(flag6); UpdateDataFromBoomBox(); } GUILayout.EndScrollView(); } private void DrawQueue(Boombox boombox) { //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(" Playback Queue", queueHeaderStyle, Array.Empty()); if (PhotonNetwork.IsMasterClient) { float num = Time.time - (refreshObjectStart ?? Time.time); int num2 = 1 + (int)Mathf.Floor(num / 1f); if (GUILayout.RepeatButton((num > 0f && num <= 5f) ? (new string('!', num2) + new string('.', Math.Max(0, 5 - num2))) : "RESET", smallButtonStyle, Array.Empty())) { 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())) { boombox.DismissQueueLocal(); lastUrl = null; } GUILayout.EndHorizontal(); queueScrollPosition = GUILayout.BeginScrollView(queueScrollPosition, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); List 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()); } for (int i = 0; i < playbackQueue.Count; i++) { Boombox.AudioEntry audioEntry = playbackQueue[i]; bool flag = i == currentSongIndex; GUIStyle val = (flag ? currentSongStyle : queueEntryStyle); string text = (flag ? "▶ " : $"{i - ((currentSongIndex != -1) ? currentSongIndex : 0)}. "); string text2 = ClipText(text + audioEntry.Title, 280f, val); GUILayout.BeginHorizontal(val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) }); if (GUILayout.Button(text2, val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(32f) }) && !flag) { boombox.SelectSongIndex(i); } if (!flag) { if (i > 0) { if (GUILayout.Button("▲", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(25f), GUILayout.Height(28f) })) { boombox.MoveQueueItemLocal(i, i - 1); } } else { GUILayout.Space(30f); } if (i + 1 < playbackQueue.Count) { if (GUILayout.Button("▼", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(25f), GUILayout.Height(28f) })) { boombox.MoveQueueItemLocal(i, i + 1); } } else { GUILayout.Space(30f); } if (GUILayout.Button("X", smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(25f), GUILayout.Height(28f) })) { boombox.RemoveQueueItemLocal(i); } } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private string ClipText(string text, float maxWidth, GUIStyle style) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) Vector2 val = style.CalcSize(new GUIContent(text)); string text2 = text; _ = 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_0016: 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; public static Dictionary downloadedClips = new Dictionary(); public static Dictionary songInfo = new Dictionary(); public static Dictionary> downloadsReady = new Dictionary>(); public static Dictionary> downloadErrors = new Dictionary>(); public const int INITIAL_DOWNLOAD_TIMEOUT = 11; private const int TIMEOUT_THRESHOLD = 10; private Dictionary timeoutCoroutines = new Dictionary(); private Queue downloadJobQueue = new Queue(); private bool isProcessingQueue; private string currentRequestId; private string currentDownloadUrl; private bool isTimeoutRecovery; private Coroutine processingCoroutine; private static readonly Regex[] supportedVideoUrlRegexes = new Regex[5] { new Regex("^(?((?:https?:)?\\/\\/)?(((?:www|m)\\.)?((?:youtube(?:-nocookie)?\\.com|youtu\\.be))|music\\.youtube\\.com)(\\/(?:[\\w\\-]+\\?v=|embed\\/|live\\/|v\\/)?)([\\w\\-]+))(?(&(\\S+&)*?(t=(?(?\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), new Regex("^(?((?:https?:)?\\/\\/)?((?:www)?\\.?)(rutube\\.ru)(\\/video\\/)([\\w\\-]+))(?(\\?(?:\\S+&)*?(t=(?(?\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), new Regex("^(?((?:https?:)?\\/\\/)?((?:www)?\\.?)(music\\.yandex\\.ru)(\\/album\\/\\d+\\/track\\/)([\\w\\-]+))(?(?:\\?(\\S+&)*?(t=(?(?\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), new Regex("^(?((?:https?:)?\\/\\/)?((?:www|m)\\.)?(bilibili\\.com)(\\/video\\/)([\\w\\-]+))(?(\\?(?:\\S+&)*?(t=(?(?\\d+)))\\S*)?\\S*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled), new Regex("^(?((?:https?:)?\\/\\/)?((?:www|m|on)\\.)?(?:soundcloud\\.com|snd\\.sc)(\\/[\\w\\-]+(?:\\/[\\w\\-]+)?))(?(?:\\?(?:\\S+&*?#)*?t=(?(?\\d+)(?:\\/|(?:%3A))(?\\d{1,2})))?\\S*)?$", RegexOptions.IgnoreCase | RegexOptions.Compiled) }; private static ManualLogSource Logger => Instance.logger; private void Awake() { boomboxParent = ((Component)this).gameObject.GetComponent(); } 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; for (int i = 0; i < array.Length; i++) { Match match = array[i].Match(url); if (!match.Success) { continue; } Group obj = match.Groups["CleanedUrl"]; if (!obj.Success) { continue; } Group obj2 = match.Groups["TimeStamp"]; int num = 0; if (obj2.Success) { Group obj3 = match.Groups["Seconds"]; Group obj4 = match.Groups["Minutes"]; if (obj3.Success && int.TryParse(obj3.Value, out var result)) { num = result; } if (obj4.Success && int.TryParse(obj4.Value, out var result2)) { num += result2 * 60; } } return (cleanedUrl: obj.Value, seconds: num); } } return (cleanedUrl: null, seconds: 0); } public static int CheckDownloadCount(string url, bool includeErrors = false) { if (string.IsNullOrEmpty(url)) { return 0; } int num = 0; if (downloadsReady.ContainsKey(url)) { num += downloadsReady[url].Count; } if (downloadErrors.ContainsKey(url)) { num += downloadErrors[url].Count; } return num; } public void EnqueueDownload(string Url) { downloadJobQueue.Enqueue(Url); } public void DismissDownloadQueue() { downloadJobQueue.Clear(); } public void StartDownloadJob() { if (!isProcessingQueue) { processingCoroutine = ((MonoBehaviour)this).StartCoroutine(ProcessDownloadQueue()); } } public static int GetBitrateKbps(string quality) { return quality switch { "32K" => 32, "64K" => 64, "96K" => 96, "128K" => 128, _ => 192, }; } public static int GetBitrateKbps(int qualityLevel) { return qualityLevel switch { 0 => 32, 1 => 64, 2 => 96, 3 => 128, _ => 192, }; } public static double EstimateSizeBytes(int bitrateKbps, double durationSeconds) { return (double)bitrateKbps * 1000.0 / 8.0 * durationSeconds; } public void DownloadQueue(int startIndex) { if (startIndex < 0 || startIndex >= boomboxParent.data.playbackQueue.Count) { startIndex = 0; } for (int i = startIndex; i < boomboxParent.data.playbackQueue.Count; i++) { downloadJobQueue.Enqueue(boomboxParent.data.playbackQueue.ElementAt(i).Url); } for (int j = 0; j < startIndex; j++) { downloadJobQueue.Enqueue(boomboxParent.data.playbackQueue.ElementAt(j).Url); } StartDownloadJob(); } [PunRPC] public void NotifyPlayersOfErrors(string message) { Logger.LogWarning((object)message); boomboxParent.UpdateUIStatus(message); } [PunRPC] public void ReportDownloadError(int actorNumber, string url, string errorMessage) { if (actorNumber == PhotonNetwork.LocalPlayer.ActorNumber) { boomboxParent.UpdateUIStatus("Error: " + errorMessage); } if (PhotonNetwork.IsMasterClient) { if (!downloadErrors.ContainsKey(url)) { downloadErrors[url] = new HashSet(); } if (!downloadErrors[url].Contains(actorNumber)) { downloadErrors[url].Add(actorNumber); Logger.LogError((object)$"Player {actorNumber} reported download error for {url}: {errorMessage}"); } } } [PunRPC] public void SetSongInfo(string url, string title, double duration, int actorNumber) { if (!PhotonNetwork.IsMasterClient || string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(title) || (PhotonNetwork.IsMasterClient && songInfo.ContainsKey(url) && actorNumber != PhotonNetwork.LocalPlayer.ActorNumber && !songInfo[url].IsInvalid())) { return; } songInfo[url] = new SongInfo(title, duration); bool flag = false; if (boomboxParent?.data?.playbackQueue != null) { foreach (Boombox.AudioEntry item in boomboxParent.data.playbackQueue.FindAll((Boombox.AudioEntry entry) => entry.Url == url)) { if (item.Title != title) { item.Title = title; flag = true; } if (item.Duration != duration) { item.Duration = duration; } flag = true; } } if (boomboxParent?.data?.currentSong != null && boomboxParent.data.currentSong.Url == url) { boomboxParent.data.currentSong.Title = title; if (boomboxParent.data.pendingPlaybackStart) { boomboxParent.UpdateUIStatus("Loading: " + title); } else if (boomboxParent.data.currentSong.ClipLoaded() && boomboxParent.data.isPlaying) { boomboxParent.UpdateUIStatus("Now playing: " + title); } else if (boomboxParent.data.currentSong.ClipLoaded()) { boomboxParent.UpdateUIStatus("Ready to play: " + title); } else { boomboxParent.UpdateUIStatus("Loading: " + title); } } if (flag) { Boombox boombox = boomboxParent; if (boombox != null) { ((Component)boombox).GetComponent()?.UpdateDataFromBoomBox(); } if (PhotonNetwork.IsMasterClient) { boomboxParent.PublishSharedState(updateTime: false, updateQueue: true); } } } private IEnumerator ProcessDownloadQueue() { isProcessingQueue = true; Logger.LogDebug((object)(((MonoBehaviourPun)this).photonView.ViewID + "Master Client Download Queue Processor started.")); while (downloadJobQueue.Count > 0 && isProcessingQueue) { string url = downloadJobQueue.Dequeue(); yield return MasterClientInitiateSync(url); if ((Object)(object)this == (Object)null) { yield break; } } if (!((Object)(object)this == (Object)null)) { isProcessingQueue = false; currentDownloadUrl = null; Logger.LogDebug((object)(((MonoBehaviourPun)this).photonView.ViewID + "Master Client Download Queue Processor finished.")); } } private IEnumerator MasterClientInitiateSync(string url) { if (!boomboxParent.data.playbackQueue.Any((Boombox.AudioEntry entry) => entry.Url == url)) { yield break; } string requestId = Guid.NewGuid().ToString(); currentDownloadUrl = url; currentRequestId = requestId; if (downloadsReady.ContainsKey(url)) { if (downloadsReady[url].Count >= Instance.baseListener.GetAllModUsers().Count) { if (boomboxParent.data.pendingPlaybackStart && url == boomboxParent.data.currentSong?.Url) { boomboxParent.FinalizePendingPlaybackStart(boomboxParent.startPlayBackOnDownload); } yield break; } downloadsReady[url].Clear(); } else { downloadsReady[url] = new HashSet(); } if (downloadErrors.ContainsKey(url)) { downloadErrors[url].Clear(); } else { downloadErrors[url] = new HashSet(); } BaseListener.RPC(((MonoBehaviourPun)this).photonView, "StartDownloadAndSync", (RpcTarget)0, url, PhotonNetwork.LocalPlayer.ActorNumber); timeoutCoroutines[requestId] = ((MonoBehaviour)this).StartCoroutine(DownloadTimeoutCoroutine(requestId, url)); yield return WaitForPlayersReadyOrFailed(url); Coroutine coroutine = default(Coroutine); if ((timeoutCoroutines?.TryGetValue(requestId, out coroutine) ?? false) && coroutine != null) { ((MonoBehaviour)this).StopCoroutine(coroutine); timeoutCoroutines.Remove(requestId); } if (!(currentDownloadUrl != url)) { currentDownloadUrl = null; currentRequestId = null; Logger.LogDebug((object)(((MonoBehaviourPun)this).photonView.ViewID + "Consensus finished for " + url + ", Starting Playback.")); Boombox boombox = boomboxParent; if (boombox != null && boombox.data?.pendingPlaybackStart == true && url == boomboxParent.data.currentSong?.Url) { boomboxParent.FinalizePendingPlaybackStart(boomboxParent.startPlayBackOnDownload); } } } private int SongTimeout(string url) { double num = songInfo[url].duration; if (num < 0.0) { num = 180.0; } return (int)Math.Round(boomboxParent.EstimateDownloadTimeSeconds(num, Instance.DownloadSpeed.Value)); } private IEnumerator DownloadTimeoutCoroutine(string requestId, string url) { if (songInfo.ContainsKey(url)) { _ = songInfo[url].duration; } else { yield return (object)new WaitForSeconds(11f); } while (YoutubeDL.isInitializing) { yield return (object)new WaitForSeconds(1f); } if (songInfo.ContainsKey(url)) { _ = songInfo[url].duration; int num = SongTimeout(url); Logger.LogDebug((object)(((MonoBehaviourPun)this).photonView.ViewID + $"Using download timeout: {num}")); yield return (object)new WaitForSeconds((float)num); } if (currentRequestId == requestId && isProcessingQueue) { Logger.LogWarning((object)("Download timeout for url: " + url)); Logger.LogInfo((object)"Master client initiating timeout recovery"); isTimeoutRecovery = true; foreach (int allModUser in Instance.baseListener.GetAllModUsers()) { if (!downloadsReady[url].Contains(allModUser)) { if (!downloadErrors.ContainsKey(url)) { downloadErrors[url] = new HashSet(); } downloadErrors[url].Add(allModUser); Logger.LogWarning((object)$"Player {allModUser} timed out during download"); } } if (downloadsReady.ContainsKey(url) && downloadsReady[url].Count > 0) { string text = $"Some players timed out. Continuing playback for {downloadsReady[url].Count} players."; BaseListener.RPC(((MonoBehaviourPun)this).photonView, "NotifyPlayersOfErrors", (RpcTarget)0, text); if (boomboxParent.data.pendingPlaybackStart && boomboxParent.data.currentSong?.Url == url) { boomboxParent.FinalizePendingPlaybackStart(boomboxParent.startPlayBackOnDownload); } } else { BaseListener.RPC(((MonoBehaviourPun)this).photonView, "NotifyPlayersOfErrors", (RpcTarget)0, "Download timed out for all players."); } currentDownloadUrl = null; currentRequestId = null; isTimeoutRecovery = false; } timeoutCoroutines.Remove(requestId); } [PunRPC] public async void StartDownloadAndSync(string url, int requesterId) { if (url == null || requesterId != PhotonNetwork.MasterClient.ActorNumber) { return; } if (currentDownloadUrl != url) { currentDownloadUrl = url; if (!downloadsReady.ContainsKey(url)) { downloadsReady[url] = new HashSet(); } if (!downloadErrors.ContainsKey(url)) { downloadErrors[url] = new HashSet(); } } if (downloadedClips.ContainsKey(url)) { boomboxParent?.HandleDownloadedCurrentSong(); BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber); } else if (await StartAudioDownload(url) && (Object)(object)this != (Object)null) { boomboxParent?.HandleDownloadedCurrentSong(); BaseListener.RPC(((MonoBehaviourPun)this).photonView, "ReportDownloadComplete", (RpcTarget)2, url, PhotonNetwork.LocalPlayer.ActorNumber); } } [PunRPC] public void ReportDownloadComplete(string url, int actorNumber) { if (PhotonNetwork.IsMasterClient && url != null) { if (!downloadsReady.ContainsKey(url)) { downloadsReady[url] = new HashSet(); } if (downloadErrors.ContainsKey(url) && downloadErrors[url].Contains(actorNumber)) { downloadErrors[url].Remove(actorNumber); } if (!downloadsReady[url].Contains(actorNumber)) { downloadsReady[url].Add(actorNumber); } } } private IEnumerator WaitForPlayersReadyOrFailed(string url) { int totalPlayers = Instance.baseListener.GetAllModUsers().Count; int readyCount = 0; int errorCount = 0; float waitTime = 0.1f; float? partialConsensusStartTime = null; while ((Object)(object)this != (Object)null) { readyCount = (downloadsReady.ContainsKey(url) ? downloadsReady[url].Count : 0); errorCount = (downloadErrors.ContainsKey(url) ? downloadErrors[url].Count : 0); bool num = readyCount + errorCount >= totalPlayers; bool num2 = readyCount > 0 && errorCount > 0; bool flag = false; if (!num2) { partialConsensusStartTime = null; } else if (!partialConsensusStartTime.HasValue) { partialConsensusStartTime = Time.time; } else { flag = Time.time - partialConsensusStartTime.Value >= 10f; } if (num || flag) { 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 GetAudioClipAsync(string filePath, SongInfo info) { await Task.Yield(); if (!File.Exists(filePath)) { throw new Exception("Audio file not found at path: " + filePath); } Uri uri = new Uri(filePath); string uri2 = uri.AbsoluteUri; UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(uri2, (AudioType)13); try { www.timeout = 11; ((DownloadHandlerAudioClip)www.downloadHandler).compressed = AudioSettings.Light; ((DownloadHandlerAudioClip)www.downloadHandler).streamAudio = false; TaskCompletionSource tcs = new TaskCompletionSource(); ((AsyncOperation)www.SendWebRequest()).completed += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 if ((int)www.result != 1) { Logger.LogError((object)("Web request failed: " + www.error + ", URI: " + uri2)); tcs.SetException(new Exception("Failed to load audio file: " + www.error)); } else { tcs.SetResult(result: true); } }; await tcs.Task; AudioClip content = DownloadHandlerAudioClip.GetContent(www); if ((Object)(object)content == (Object)null) { throw new Exception("Failed to get AudioClip content."); } return content; } finally { if (www != null) { ((IDisposable)www).Dispose(); } } } public async Task StartAudioDownload(string url) { if (downloadedClips.ContainsKey(url)) { return true; } try { if (LocalMusic.IsLocalUrl(url)) { if (!LocalMusic.TryGetTrack(url, out var localTrack)) { throw new Exception("Faixa local nao encontrada neste cliente: " + url); } SongInfo value = new SongInfo(localTrack.Title, -1.0); DownloadHelper.songInfo[url] = value; if (boomboxParent?.data?.currentSong?.Url == url) { boomboxParent.UpdateUIStatus("Carregando local: " + localTrack.Title); } Logger.LogInfo((object)(((MonoBehaviourPun)this).photonView.ViewID + " CarretaFuracao carregando arquivo local: " + localTrack.FileName)); AudioClip val = await LocalClipCache.Load(url); if ((Object)(object)val == (Object)null) { throw new Exception("AudioClip local retornou null: " + localTrack.Title); } ((Object)val).name = localTrack.Title; SongInfo songInfo = new SongInfo(localTrack.Title, val.length); downloadedClips[url] = val; DownloadHelper.songInfo[url] = songInfo; BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetSongInfo", (RpcTarget)0, url, songInfo.title, songInfo.duration, PhotonNetwork.LocalPlayer.ActorNumber); Logger.LogInfo((object)(((MonoBehaviourPun)this).photonView.ViewID + " CarretaFuracao MP3 LOCAL PRONTO: " + songInfo.title + " | " + val.length.ToString("0.00") + "s")); return true; } SongInfo info = (DownloadHelper.songInfo.ContainsKey(url) ? DownloadHelper.songInfo[url] : null); if (info?.IsInvalid() ?? true) { SongInfo songInfo2 = await YoutubeDL.DownloadAudioInfoAsync(url); if (!DownloadHelper.songInfo.ContainsKey(url) || DownloadHelper.songInfo[url].IsInvalid()) { DownloadHelper.songInfo[url] = songInfo2; info = songInfo2; if (!songInfo2.IsInvalid()) { BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetSongInfo", (RpcTarget)0, url, info.title, info.duration, PhotonNetwork.LocalPlayer.ActorNumber); } } } string filePath = await YoutubeDL.DownloadAudioAsync(url, info); if (boomboxParent?.data?.currentSong?.Url == url) { boomboxParent?.UpdateUIStatus("Processing audio: " + info.title); } AudioClip val2 = await GetAudioClipAsync(filePath, info); downloadedClips[url] = val2; Logger.LogDebug((object)(((MonoBehaviourPun)this).photonView.ViewID + "Downloaded and cached clip for video: " + info.title)); if ((Object)(object)val2 != (Object)null && info.IsInvalid()) { SongInfo songInfo3 = await YoutubeDL.DownloadAudioInfoAsync(url); if (!songInfo3.IsInvalid() && !DownloadHelper.songInfo.ContainsKey(url)) { DownloadHelper.songInfo[url] = songInfo3; BaseListener.RPC(((MonoBehaviourPun)this).photonView, "SetSongInfo", (RpcTarget)0, url, songInfo3.title, songInfo3.duration, PhotonNetwork.LocalPlayer.ActorNumber); } } } catch (Exception ex) { 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; } return true; } public void RemoveFromCache(string url) { if (Instance.data.GetAllBoomboxes().Any((Boombox cart) => (Object)(object)cart != (Object)null && (Object)(object)cart.audioPlayer != (Object)null && cart.audioPlayer.currentUrl == url)) { return; } if (downloadedClips.ContainsKey(url)) { AudioClip val = downloadedClips[url]; downloadedClips.Remove(url); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } if (songInfo.ContainsKey(url)) { songInfo.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(); } 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 AudioSource audioSource; private const float BaseRgbSpeed = 0.18f; private const float BaseLightRange = 6f; private const float MaxLightRangeMultiplier = 1.3f; private const float BaseIntensity = 1f; private const float MaxLightIntensityMultiplier = 2f; private const float MinSpectrumNormalizationVolume = 0.1f; private const float MaxBassSpeedBoost = 0.85f; private const float BassResponse = 10f; private const float BassRelease = 4f; private const int SpectrumSize = 512; private const float BassMaxFrequency = 220f; private const float LowEndMaxFrequency = 900f; private const float NonBassMaxFrequency = 2500f; private const float PeakEmphasis = 0.75f; private const float AnalysisBarMin = 0.15f; private const float AnalysisBarMax = 1.5f; private const float AnalysisHeightMultiplier = 24f; private const float NonBassLeakRejection = 0.8f; private readonly float[] spectrum = new float[512]; private float hueProgress; private float bassIntensity; private bool lightsOn; private static BoomBoxCartMod Instance => BoomBoxCartMod.instance; private void Start() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_003d: 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_009b: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) audioSource = ((Component)this).GetComponent(); GameObject val = new GameObject("BoomboxFrontLight"); val.transform.SetParent(((Component)this).transform); val.transform.localPosition = new Vector3(0f, 0f, 1f); frontLight = val.AddComponent(); frontLight.type = (LightType)2; frontLight.range = 6f; frontLight.intensity = 1f; ((Behaviour)frontLight).enabled = lightsOn; GameObject val2 = new GameObject("BoomboxBackLight"); val2.transform.SetParent(((Component)this).transform); val2.transform.localPosition = new Vector3(0f, 0f, -1f); backLight = val2.AddComponent(); backLight.type = (LightType)2; backLight.range = 6f; backLight.intensity = 1f; ((Behaviour)backLight).enabled = lightsOn; } private void Update() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) if (lightsOn) { float num = 0.18f + GetBassSpeedBoost(); float range = 6f * Mathf.Lerp(1f, 1.3f, bassIntensity); float intensity = 1f * Mathf.Lerp(1f, 2f, bassIntensity); hueProgress = Mathf.Repeat(hueProgress + Time.deltaTime * num, 1f); Color color = Color.HSVToRGB(hueProgress, 1f, 1f); if ((Object)(object)frontLight != (Object)null) { frontLight.color = color; frontLight.range = range; frontLight.intensity = intensity; } if ((Object)(object)backLight != (Object)null) { backLight.color = color; backLight.range = range; backLight.intensity = intensity; } } } private float GetBassSpeedBoost() { if ((Object)(object)audioSource == (Object)null) { audioSource = ((Component)this).GetComponent(); } float num = (((Object)(object)audioSource != (Object)null) ? Mathf.Clamp01(audioSource.volume) : 0f); float num2 = 0f; if ((Object)(object)audioSource != (Object)null && audioSource.isPlaying && num > 0f) { audioSource.GetSpectrumData(spectrum, 0, (FFTWindow)4); float num3 = Mathf.Max(num, 0.1f); float spectrumNormalizationScale = 1f / num3; float frequencyRangeIntensity = GetFrequencyRangeIntensity(20f, 900f, spectrumNormalizationScale, 0.2625f); float frequencyRangeIntensity2 = GetFrequencyRangeIntensity(20f, 220f, spectrumNormalizationScale, 0.75f); float frequencyRangeIntensity3 = GetFrequencyRangeIntensity(220f, 2500f, spectrumNormalizationScale, 0.112500004f); float num4 = Mathf.Clamp01(frequencyRangeIntensity2 - frequencyRangeIntensity3 * 0.8f); num2 = Mathf.Lerp(frequencyRangeIntensity, num4, Instance.UnderglowBassBias.Value); bassIntensity = Mathf.Lerp(bassIntensity, num2, Time.deltaTime * 10f); } else { bassIntensity = Mathf.Lerp(bassIntensity, 0f, Time.deltaTime * 4f); } return bassIntensity * 0.85f * Instance.UnderglowBeatSpeed.Value; } private float GetFrequencyRangeIntensity(float minFrequency, float maxFrequency, float spectrumNormalizationScale, float peakBlend) { float num = (float)AudioSettings.outputSampleRate * 0.5f / 512f; int num2 = Mathf.Clamp(Mathf.FloorToInt(minFrequency / num), 1, 511); int num3 = Mathf.Clamp(Mathf.CeilToInt(maxFrequency / num), num2 + 1, 512); float num4 = 0f; float num5 = 0f; int num6 = 0; for (int i = num2; i < num3; i++) { float normalizedSpectrumSample = GetNormalizedSpectrumSample(spectrum[i], spectrumNormalizationScale); num4 += normalizedSpectrumSample; num5 = Mathf.Max(num5, normalizedSpectrumSample); num6++; } if (num6 == 0) { return 0f; } return Mathf.Lerp(num4 / (float)num6, num5, peakBlend); } private float GetNormalizedSpectrumSample(float sample, float spectrumNormalizationScale) { float num = Mathf.Clamp(Mathf.Pow(sample * spectrumNormalizationScale * 24f, 0.5f), 0.15f, 1.5f); return Mathf.InverseLerp(0.15f, 1.5f, num); } public void SetLights(bool on) { if (lightsOn == on) { return; } lightsOn = on; if ((Object)(object)frontLight != (Object)null) { ((Behaviour)frontLight).enabled = on; if (!on) { frontLight.range = 6f; frontLight.intensity = 1f; } } if ((Object)(object)backLight != (Object)null) { ((Behaviour)backLight).enabled = on; if (!on) { backLight.range = 6f; backLight.intensity = 1f; } } } 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; public bool IsVisible { get { return visible; } set { visible = value; for (int i = 0; i < numBars; i++) { ((Component)bars[i]).GetComponent().enabled = value || Instance.VisualizerBehaviourPaused.Value != BoomBoxCartMod.VisualizerPaused.Hide; } } } private void Start() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)audioSource == (Object)null) { audioSource = ((Component)this).GetComponent(); } 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().material.color = Color.HSVToRGB((float)i / (float)numBars, 1f, 1f); Object.Destroy((Object)(object)val.GetComponent()); bars[i] = val.transform; } IsVisible = audioSource.isPlaying || Instance.VisualizerBehaviourPaused.Value != BoomBoxCartMod.VisualizerPaused.Hide; lastPlaying = audioSource.isPlaying; } private void Update() { //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0123: 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); } } } } } namespace BoomBoxCartMod.Util { public enum AudioPreset { Leve, Original } public static class AudioSettings { public static ConfigEntry Preset; public static ConfigEntry Master; public static ConfigEntry Personal; public static ConfigEntry Preload; private static bool updating; public static bool Light { get { if (Preset != null) { return Preset.Value == AudioPreset.Leve; } return false; } } public static float MasterValue => (float)((Master == null) ? 20 : Master.Value) / 100f; public static float PersonalValue => (float)((Personal == null) ? 35 : Personal.Value) / 100f; public static void Initialize(ConfigFile config) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown Master = config.Bind("Audio", "Volume Master", 20, new ConfigDescription("Volume compartilhado (%) de todos os carrinhos. Alteracoes em partida sao enviadas ao host. / Shared cart volume (%); changes are sent to the host.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); Personal = config.Bind("Audio", "Volume Pessoal", 35, new ConfigDescription("Multiplicador local (%), afeta apenas o que voce ouve. / Local multiplier (%), affects only your audio.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); Preset = config.Bind("Audio", "Preset", AudioPreset.Leve, "Leve: audio comprimido em memoria. Original: audio decodificado em memoria. Ambos preservam o visualizador conforme sua opcao existente. Menos memoria pode exigir mais CPU durante a reproducao. Reinicie ao trocar. / Leve: compressed audio in memory. Original: decoded audio in memory. Both respect the existing visualizer setting. Lower memory use may require more playback CPU. Restart after changing this setting."); Preload = config.Bind("Audio", "Preparar Proxima Faixa", true, "Carrega a proxima faixa durante a atual para reduzir engasgos na troca. / Preload the next track during playback."); Master.SettingChanged += delegate { if (!updating) { List runtimeCarretas = Boombox.GetRuntimeCarretas(); if (runtimeCarretas.Count > 0) { runtimeCarretas[0].SetVolumeLocal(MasterValue); } } }; Personal.SettingChanged += delegate { ApplyPersonal(); }; } public static void RememberMaster(float value) { if (Master == null) { return; } updating = true; try { Master.Value = Mathf.RoundToInt(Mathf.Clamp01(value) * 100f); } finally { updating = false; } } public static void SetPersonal(float value) { if (Personal != null) { Personal.Value = Mathf.RoundToInt(Mathf.Clamp01(value) * 100f); } ApplyPersonal(); } private static void ApplyPersonal() { foreach (Boombox runtimeCarreta in Boombox.GetRuntimeCarretas()) { runtimeCarreta.data.personalVolumePercentage = PersonalValue; if ((Object)(object)runtimeCarreta.audioPlayer != (Object)null) { runtimeCarreta.audioPlayer.SetVolume(runtimeCarreta.data.absVolume * PersonalValue); } } } } public class BaseListener : MonoBehaviourPunCallbacks { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; public int lastUserAmount; private List modList = new List(); public bool audioMuted; private bool mutePressed; public static bool downloaderAvailable = true; public List 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_000a: 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_000f: Invalid comparison between Unknown and I4 //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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 allModUsers = Instance.baseListener.GetAllModUsers(); { foreach (KeyValuePair player in PhotonNetwork.CurrentRoom.Players) { if (allModUsers.Contains(player.Key) && ((int)target == 0 || player.Key != PhotonNetwork.LocalPlayer.ActorNumber)) { view.RPC(methodName, player.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 { Instance.baseListener.RemoveModUser(PhotonNetwork.LocalPlayer.ActorNumber); } } else if (PhotonNetwork.IsConnected) { PhotonView photonView = ((MonoBehaviourPun)Instance.baseListener).photonView; if (photonView != null) { photonView.RPC("ModFeedbackCheck", (RpcTarget)2, new object[2] { available ? "0.7.9" : "-1", PhotonNetwork.LocalPlayer.ActorNumber }); } } else if (available) { Instance.modDisabled = false; } } public override void OnPlayerLeftRoom(Player otherPlayer) { ((MonoBehaviourPunCallbacks)this).OnPlayerLeftRoom(otherPlayer); RemoveModUser(otherPlayer.ActorNumber); Instance.logger.LogInfo((object)$"Player {otherPlayer.ActorNumber} left the room."); } [PunRPC] public void ModFeedbackCheck(string modVersion, int actorNumber) { if ((Object)(object)Instance.baseListener == (Object)null) { return; } if (PhotonNetwork.IsMasterClient) { if (Instance.modDisabled) { return; } if (modVersion == "0.7.9") { Instance.baseListener.AddModUser(actorNumber); ManualLogSource logger = Instance.logger; if (logger != null) { logger.LogInfo((object)$"Player {actorNumber} is using a compatible version of the mod."); } } else { Instance.baseListener.RemoveModUser(actorNumber); ManualLogSource logger2 = Instance.logger; if (logger2 != null) { logger2.LogInfo((object)$"Player {actorNumber} will not be joining the jam session."); } } } else { Instance.modDisabled = !downloaderAvailable || modVersion != "0.7.9"; Instance.logger.LogInfo((object)("Mod " + (Instance.modDisabled ? "DISABLED" : "ENABLED") + ". Current version: 0.7.9, requested: " + modVersion)); PhotonView photonView = ((MonoBehaviourPun)Instance.baseListener).photonView; if (photonView != null) { photonView.RPC("ModFeedbackCheck", (RpcTarget)2, new object[2] { downloaderAvailable ? "0.7.9" : "-1", PhotonNetwork.LocalPlayer.ActorNumber }); } } } [PunRPC] public void UpdateModUsers(int[] modUsers, int actorNumber) { if (!PhotonNetwork.IsMasterClient) { modList.Clear(); modList.AddRange(modUsers); } } } public static class LocalClipCache { private static readonly Dictionary> loading = new Dictionary>(); public static Task Load(string key) { if (DownloadHelper.downloadedClips.TryGetValue(key, out var value) && (Object)(object)value != (Object)null) { return Task.FromResult(value); } if (loading.TryGetValue(key, out var value2)) { return value2; } value2 = LoadCore(key); loading[key] = value2; return value2; } private static async Task LoadCore(string key) { await Task.Yield(); try { if (!LocalMusic.TryGetTrack(key, out var track)) { throw new Exception("Faixa local nao encontrada: " + key); } AudioClip val = await DownloadHelper.GetAudioClipAsync(track.FilePath, new SongInfo(track.Title, -1.0)); ((Object)val).name = track.Title; DownloadHelper.downloadedClips[key] = val; DownloadHelper.songInfo[key] = new SongInfo(track.Title, val.length); Trim(key); return val; } finally { loading.Remove(key); } } public static void Trim(string keep) { HashSet hashSet = new HashSet(); if (keep != null) { hashSet.Add(keep); } foreach (Boombox runtimeCarreta in Boombox.GetRuntimeCarretas()) { if ((Object)(object)runtimeCarreta.audioPlayer != (Object)null && runtimeCarreta.audioPlayer.currentUrl != null) { hashSet.Add(runtimeCarreta.audioPlayer.currentUrl); } if (runtimeCarreta.data.currentSong != null) { hashSet.Add(runtimeCarreta.data.currentSong.Url); int currentSongIndex = runtimeCarreta.GetCurrentSongIndex(); if (currentSongIndex >= 0 && runtimeCarreta.data.playbackQueue.Count > 0) { hashSet.Add(runtimeCarreta.data.playbackQueue[(currentSongIndex + 1) % runtimeCarreta.data.playbackQueue.Count].Url); } } } string[] array = DownloadHelper.downloadedClips.Keys.ToArray(); foreach (string text in array) { if (LocalMusic.IsLocalUrl(text) && !hashSet.Contains(text) && !loading.ContainsKey(text)) { AudioClip val = DownloadHelper.downloadedClips[text]; DownloadHelper.downloadedClips.Remove(text); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } } } public static class LocalMusic { public class LocalTrack { public string Key; public string Title; public string FileName; public string FilePath; } public const string UrlPrefix = "carretafuracao://"; private static readonly List tracks = new List(); private static readonly Dictionary tracksByKey = new Dictionary(StringComparer.Ordinal); public static List Tracks => tracks; public static void Initialize() { tracks.Clear(); tracksByKey.Clear(); string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Audio"); BoomBoxCartMod.instance.logger.LogInfo((object)("CarretaFuracao: procurando arquivos de audio em: " + text)); if (!Directory.Exists(text)) { BoomBoxCartMod.instance.logger.LogWarning((object)"CarretaFuracao: pasta Audio nao encontrada."); return; } string[] files = Directory.GetFiles(text, "*.mp3", SearchOption.TopDirectoryOnly); Array.Sort(files, (IComparer?)StringComparer.OrdinalIgnoreCase); string[] array = files; foreach (string text2 in array) { string fileName = Path.GetFileName(text2); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2); string key = "carretafuracao://" + Uri.EscapeDataString(fileName); LocalTrack localTrack = new LocalTrack { Key = key, Title = fileNameWithoutExtension, FileName = fileName, FilePath = text2 }; tracks.Add(localTrack); tracksByKey[key] = localTrack; DownloadHelper.songInfo[key] = new SongInfo(fileNameWithoutExtension, -1.0); } BoomBoxCartMod.instance.logger.LogInfo((object)("CarretaFuracao: arquivos de audio encontrados: " + tracks.Count)); foreach (LocalTrack track in tracks) { BoomBoxCartMod.instance.logger.LogInfo((object)(" LOCAL: " + track.Title)); } } public static bool IsLocalUrl(string url) { if (!string.IsNullOrWhiteSpace(url)) { return url.StartsWith("carretafuracao://", StringComparison.Ordinal); } return false; } public static bool TryGetTrack(string key, out LocalTrack track) { return tracksByKey.TryGetValue(key, out track); } public static LocalTrack GetFirstTrack() { if (tracks.Count == 0) { return null; } return tracks[0]; } } public class PersistentData { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; private List initializedBoomBoxes = new List(); private List boomboxData = new List(); public static bool GetBoomboxViewStatus(Player player, int viewID) { if (!PhotonNetwork.IsConnected) { return false; } string key = "boomboxView" + viewID; if (((Dictionary)(object)player.CustomProperties).TryGetValue((object)key, out object value)) { if (value is bool) { return (bool)value; } return false; } return false; } public static void SetBoomboxViewInitialized(int viewID) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown string key = "boomboxView" + viewID; Hashtable val = new Hashtable(); ((Dictionary)(object)val).Add((object)key, (object)true); PhotonNetwork.LocalPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); } public static void RemoveBoomboxViewInitialized(int viewID) { if (PhotonNetwork.IsConnected) { string key = "boomboxView" + viewID; ((Dictionary)(object)PhotonNetwork.LocalPlayer.CustomProperties).Remove((object)key); } } public List GetAllBoomboxes() { return initializedBoomBoxes; } public List GetBoomboxData() { return boomboxData; } private void ApplyRestoreSettings(Boombox.BoomboxData data) { if (data != null && !Instance.AutoResume.Value) { data.isPlaying = false; data.pendingPlaybackStart = false; } } public void InitializeBoomboxData(Boombox boombox) { int num = initializedBoomBoxes.IndexOf(boombox); if (num == -1) { num = initializedBoomBoxes.Count; initializedBoomBoxes.Add(boombox); } Boombox.BoomboxData boomboxData; if (PhotonNetwork.IsMasterClient && Instance.RestoreBoomboxes.Value) { if (num < this.boomboxData.Count) { boomboxData = this.boomboxData[num]; ApplyRestoreSettings(boomboxData); } else { boomboxData = new Boombox.BoomboxData(); this.boomboxData.Add(boomboxData); } } else { boomboxData = new Boombox.BoomboxData(); } boombox.data = boomboxData; } } public class SongInfo { [JsonProperty("title")] public string title { get; set; } = "Unknown Title"; [JsonProperty("duration")] public double duration { get; set; } = -1.0; public SongInfo() { } public SongInfo(string title, double duration) { this.title = title; this.duration = duration; } public bool IsInvalid() { if (duration == -1.0) { if (!Utility.IsNullOrWhiteSpace(title)) { return title.Trim().StartsWith("Unknown Title"); } return true; } return false; } } public static class YoutubeDL { [Serializable] private class GitHubRelease { public string published_at; } private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; private static readonly string baseFolder = Path.Combine(Directory.GetCurrentDirectory(), "BoomboxedCart"); private static readonly string tempFolder = Path.Combine(baseFolder, "temp"); private static readonly string jsFolder = Path.Combine(Directory.GetCurrentDirectory(), "JavaScript"); private const string JS_RELEASE_URL = "https://nodejs.org/dist/v26.1.0/node-v26.1.0-win-x64.zip"; private const string YTDLP_RELEASE_URL = "https://api.github.com/repos/yt-dlp/yt-dlp-Builds/releases/latest"; private const string YTDLP_URL = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe"; private const string FFMPEG_RELEASE_URL = "https://api.github.com/repos/BtbN/FFmpeg-Builds/releases/latest"; private const string FFMPEG_URL = "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip"; private static readonly string jsRuntimePath = Path.Combine(jsFolder, "node-v26.1.0-win-x64/node.exe"); private static readonly string ytDlpPath = Path.Combine(baseFolder, "yt-dlp.exe"); private static readonly string ffmpegFolder = Path.Combine(baseFolder, "ffmpeg"); private static string ffmpegBinPath = Path.Combine(ffmpegFolder, "ffmpeg-master-latest-win64-gpl", "bin", "ffmpeg.exe"); private static readonly object initializationLock = new object(); private static Task initializeTask = Task.CompletedTask; public static bool isInitializing = false; private static bool jsInstalled = false; private static bool ffmpegUpdateChecked = false; private static bool ytDLPUpdateChecked = false; private static bool isUpdatingResources = false; private static string resourceUpdateStatus = string.Empty; private static ManualLogSource Logger => Instance.logger; public static bool IsUpdatingResources => isUpdatingResources; public static string ResourceUpdateStatus { get { if (!string.IsNullOrWhiteSpace(resourceUpdateStatus)) { return resourceUpdateStatus; } return "Updating resources..."; } } public static Task InitializeAsync() { lock (initializationLock) { bool flag = File.Exists(ytDlpPath) && File.Exists(ffmpegBinPath); if (initializeTask != null && !initializeTask.IsCompleted) { return initializeTask; } if (flag && ytDLPUpdateChecked && ffmpegUpdateChecked) { return Task.CompletedTask; } initializeTask = InitializeInternalAsync(); return initializeTask; } } private static async Task InitializeInternalAsync() { SetResourceUpdateStatus("Checking downloader resources...", updating: true); isInitializing = true; try { Directory.CreateDirectory(baseFolder); Directory.CreateDirectory(tempFolder); Directory.CreateDirectory(jsFolder); if (!File.Exists(jsRuntimePath)) { SetResourceUpdateStatus("Updating resources: downloading nodeJS...", updating: true); Logger.LogInfo((object)"nodeJS not found. Downloading..."); await DownloadAndExtractArchiveAsync("https://nodejs.org/dist/v26.1.0/node-v26.1.0-win-x64.zip", jsFolder, "node.exe", 6); Logger.LogInfo((object)"nodeJS download finished."); jsInstalled = true; } else { jsInstalled = true; } if (!File.Exists(ytDlpPath)) { SetResourceUpdateStatus("Updating resources: downloading yt-dlp...", updating: true); Logger.LogInfo((object)"yt-dlp not found. Downloading..."); await DownloadFileAsync("https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe", ytDlpPath, 1); Logger.LogInfo((object)"yt-dlp download finished."); } else if (!ytDLPUpdateChecked) { SetResourceUpdateStatus("Updating resources: checking yt-dlp...", updating: true); Logger.LogInfo((object)"yt-dlp found. Checking for updates..."); DateTime localBuildDate = File.GetLastWriteTimeUtc(ytDlpPath); DateTime? dateTime = await GetLatestGithubReleaseDate("https://api.github.com/repos/yt-dlp/yt-dlp-Builds/releases/latest"); if (dateTime.HasValue) { DateTime value = localBuildDate.AddHours(24.0); DateTime? dateTime2 = dateTime; if (value < dateTime2) { SetResourceUpdateStatus("Updating resources: updating yt-dlp...", updating: true); Logger.LogInfo((object)"yt-dlp update found. Downloading..."); await DownloadFileAsync("https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe", ytDlpPath, 1); goto IL_032e; } } Logger.LogInfo((object)((!dateTime.HasValue) ? "yt-dlp release date failed to parse." : "yt-dlp up to date.")); } goto IL_032e; IL_032e: ytDLPUpdateChecked = true; if (!File.Exists(ffmpegBinPath) || !Directory.Exists(Path.GetDirectoryName(ffmpegBinPath))) { SetResourceUpdateStatus("Updating resources: downloading FFmpeg...", updating: true); Logger.LogInfo((object)"ffmpeg not found. Downloading and extracting..."); await DownloadAndExtractArchiveAsync("https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip", ffmpegFolder, "ffmpeg.exe", 6); } else if (!ffmpegUpdateChecked) { SetResourceUpdateStatus("Updating resources: checking FFmpeg...", updating: true); Logger.LogInfo((object)"ffmpeg found. Checking for updates..."); DateTime localBuildDate = File.GetLastWriteTimeUtc(ffmpegBinPath); DateTime? dateTime3 = await GetLatestGithubReleaseDate("https://api.github.com/repos/BtbN/FFmpeg-Builds/releases/latest"); if (dateTime3.HasValue) { DateTime value = localBuildDate.AddHours(24.0); DateTime? dateTime2 = dateTime3; if (value < dateTime2) { SetResourceUpdateStatus("Updating resources: updating FFmpeg...", updating: true); Logger.LogInfo((object)"ffmpeg update found. Downloading and extracting..."); await DownloadAndExtractArchiveAsync("https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip", ffmpegFolder, "ffmpeg.exe", 6); goto IL_0567; } } Logger.LogInfo((object)((!dateTime3.HasValue) ? "ffmpeg release date failed to parse." : "ffmpeg up to date.")); } goto IL_0567; IL_0567: ffmpegUpdateChecked = true; if (!File.Exists(ytDlpPath)) { BaseListener.ReportDownloaderStatus(available: false); Logger.LogError((object)("yt-dlp executable was not found at " + ytDlpPath + ". Internet problem?")); SetResourceUpdateStatus("Required media tools are unavailable.", updating: false); } else if (!File.Exists(ffmpegBinPath)) { BaseListener.ReportDownloaderStatus(available: false); Logger.LogError((object)("ffmpeg executable was not found at " + ffmpegBinPath + " after extraction. Internet problem? Not on Windows problem?")); SetResourceUpdateStatus("Required media tools are unavailable.", updating: false); } else { Logger.LogInfo((object)"Yt-DL initialization complete."); BaseListener.ReportDownloaderStatus(available: true); SetResourceUpdateStatus(string.Empty, updating: false); } } catch (Exception ex) { Logger.LogError((object)("Downloader initialization failed: " + ex.Message)); BaseListener.ReportDownloaderStatus(available: false); SetResourceUpdateStatus("Required media tools are unavailable.", updating: false); } finally { isInitializing = false; } } private static void SetResourceUpdateStatus(string message, bool updating) { resourceUpdateStatus = message; isUpdatingResources = updating; } private static async Task DownloadFileAsync(string url, string destinationPath, int timeout) { using HttpClient client = new HttpClient(); client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("CarretaFuracao", "0.7.9")); client.Timeout = TimeSpan.FromMinutes(timeout); string tempDownloadPath = destinationPath + ".download"; try { byte[] bytes = await client.GetByteArrayAsync(url); Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); if (File.Exists(tempDownloadPath)) { File.Delete(tempDownloadPath); } File.WriteAllBytes(tempDownloadPath, bytes); ReplaceFile(tempDownloadPath, destinationPath); } catch (Exception ex) { Logger.LogError((object)ex.Message); if (File.Exists(tempDownloadPath)) { File.Delete(tempDownloadPath); } throw; } } private static void ReplaceFile(string sourcePath, string destinationPath) { if (File.Exists(destinationPath)) { string text = destinationPath + ".bak"; if (File.Exists(text)) { File.Delete(text); } File.Replace(sourcePath, destinationPath, text, ignoreMetadataErrors: true); if (File.Exists(text)) { File.Delete(text); } } else { File.Move(sourcePath, destinationPath); } } private static async Task DownloadAndExtractArchiveAsync(string url, string installFolder, string executableName, int retries = 3) { string archivePath = Path.Combine(tempFolder, Path.GetFileName(url)); string stagingFolder = Path.Combine(tempFolder, "staging-" + Guid.NewGuid().ToString("N")); try { if (File.Exists(archivePath)) { File.Delete(archivePath); } if (Directory.Exists(stagingFolder)) { Directory.Delete(stagingFolder, recursive: true); } Directory.CreateDirectory(stagingFolder); Logger.LogDebug((object)("Downloading: " + url)); await DownloadFileAsync(url, archivePath, retries); if (!File.Exists(archivePath)) { throw new Exception("Archive download failed."); } Logger.LogDebug((object)"Extracting archive..."); string text = Path.GetExtension(archivePath).ToLowerInvariant(); if (text == ".zip") { ZipFile.ExtractToDirectory(archivePath, stagingFolder); File.Delete(archivePath); if (string.IsNullOrWhiteSpace(Directory.GetFiles(stagingFolder, executableName, SearchOption.AllDirectories).FirstOrDefault())) { throw new Exception(executableName + " not found after extraction."); } if (Directory.Exists(installFolder)) { Directory.Delete(installFolder, recursive: true); } Directory.Move(stagingFolder, installFolder); string result = Directory.GetFiles(installFolder, executableName, SearchOption.AllDirectories).First(); Logger.LogDebug((object)(executableName + " installed successfully.")); return result; } throw new Exception("Unsupported archive type: " + text); } finally { if (File.Exists(archivePath)) { File.Delete(archivePath); } if (Directory.Exists(stagingFolder)) { Directory.Delete(stagingFolder, recursive: true); } } } public static async Task DownloadAudioInfoAsync(string videoUrl) { await InitializeAsync(); return await Task.Run(async delegate { try { SongInfo songInfo = (DownloadHelper.songInfo.ContainsKey(videoUrl) ? DownloadHelper.songInfo[videoUrl] : null); if (songInfo == null || songInfo.IsInvalid()) { songInfo = await GetVideoTitleInternalAsync(videoUrl); if (songInfo == null || string.IsNullOrEmpty(songInfo.title)) { songInfo = new SongInfo(); } } songInfo.title = songInfo.title.Replace("\n", "").Replace("\r", ""); return songInfo; } catch (Exception ex) { Logger.LogError((object)$"Download Error: {ex}"); Logger.LogError((object)("Stack Trace: " + ex.StackTrace)); throw; } }); } public static async Task DownloadAudioAsync(string videoUrl, SongInfo info) { string folder = Path.Combine(tempFolder, Guid.NewGuid().ToString()); Directory.CreateDirectory(folder); Logger.LogDebug((object)("Downloading audio for " + videoUrl + "...")); return await Task.Run(async delegate { _ = 2; try { string text = (Boombox.ApplyQualityToDownloads ? (AudioPlayer.GetQuality() switch { 0 => "32K", 1 => "64K", 2 => "96K", 3 => "128K", _ => "192K", }) : "192K"); string text2 = text; string path = $"audio_{DateTime.Now.Ticks}.%(ext)s"; string text3 = ""; if (jsInstalled) { text3 = text3 + " --js-runtimes node:\"" + jsRuntimePath + "\""; } if (Instance.CookiePassthrough.Value == BoomBoxCartMod.CookieUsage.FILE && !string.IsNullOrWhiteSpace(Instance.CookiePath.Value)) { text3 = text3 + " --cookies \"" + Instance.CookiePath.Value + "\""; Logger.LogDebug((object)"Attempting to use cookies from file."); } else if (Instance.CookiePassthrough.Value == BoomBoxCartMod.CookieUsage.BROWSER) { text3 += $" --cookies-from-browser \"{Instance.Browser.Value}\""; Logger.LogDebug((object)"Attempting to use cookies from browser."); } string arguments = "-x --audio-format mp3 --audio-quality " + text2 + text3 + " --ffmpeg-location \"" + ffmpegBinPath + "\" --output \"" + Path.Combine(folder, path) + "\" " + videoUrl; ProcessStartInfo startInfo = new ProcessStartInfo { FileName = ytDlpPath, Arguments = arguments, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, StandardOutputEncoding = Encoding.UTF8 }; using (Process process = Process.Start(startInfo)) { if (process == null) { throw new Exception("Failed to start yt-dlp process."); } await process.StandardOutput.ReadToEndAsync(); string text4 = await process.StandardError.ReadToEndAsync(); process.WaitForExit(); if (!string.IsNullOrEmpty(text4)) { Logger.LogInfo((object)"An error recorded in yt-dlp download, error is probably not fatal though"); } if (process.ExitCode != 0) { throw new Exception($"yt-dlp download failed. Exit Code: {process.ExitCode}. Error: {text4}"); } } await Task.Delay(1000); string text5 = Directory.GetFiles(folder, "*.mp3").FirstOrDefault(); if (text5 == null) { string[] files = Directory.GetFiles(folder); Logger.LogError((object)$"No MP3 files found. Total files: {files.Length}"); string[] array = files; foreach (string text6 in array) { Logger.LogError((object)("Found file: " + text6)); } throw new Exception("Audio download failed. No MP3 file created."); } return text5; } catch (Exception ex) { Logger.LogError((object)$"Download Error: {ex}"); Logger.LogError((object)("Stack Trace: " + ex.StackTrace)); if (Directory.Exists(folder)) { try { Directory.Delete(folder, recursive: true); } catch (Exception arg) { Logger.LogError((object)$"Failed to clean up temp folder: {arg}"); } } throw; } }); } private static async Task GetVideoTitleInternalAsync(string url) { _ = 2; try { ProcessStartInfo startInfo = new ProcessStartInfo { FileName = ytDlpPath, Arguments = "--skip-download --no-playlist --no-warnings --encoding utf-8 --print \"{\\\"title\\\":\\\"%(title)s\\\",\\\"duration\\\":%(duration)s}\" \"" + url + "\"", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 }; Process process = new Process { StartInfo = startInfo }; try { process.Start(); string json = await process.StandardOutput.ReadToEndAsync(); string error = await process.StandardError.ReadToEndAsync(); Task timeoutTask = Task.Delay(11000); Task task = Task.Run(delegate { process.WaitForExit(); }); if (await Task.WhenAny(new Task[2] { task, timeoutTask }) == timeoutTask) { try { process.Kill(); } catch { } Logger.LogWarning((object)"yt-dlp title fetch timed out"); return new SongInfo(); } if (process.ExitCode != 0) { Logger.LogError((object)$"yt-dlp error code: {process.ExitCode}"); if (!string.IsNullOrWhiteSpace(error)) { Logger.LogWarning((object)("yt-dlp title fetch error: " + error.Trim())); } return new SongInfo(); } SongInfo songInfo; try { songInfo = JsonConvert.DeserializeObject(json); if (songInfo != null && !string.IsNullOrEmpty(songInfo.title)) { _ = songInfo.duration; try { songInfo.title = new string(songInfo.title.Where((char c) => !char.IsControl(c) || c == '\n' || c == '\r' || c == '\t').ToArray()); } catch (Exception ex) { Logger.LogWarning((object)("Error sanitizing title: " + ex.Message)); } } } catch (Exception ex2) { Logger.LogWarning((object)("Error converting audio info: " + ex2.Message)); return new SongInfo(); } return (songInfo == null) ? new SongInfo() : songInfo; } finally { if (process != null) { ((IDisposable)process).Dispose(); } } } catch (Exception ex3) { Logger.LogError((object)("Error getting video title: " + ex3.Message)); return new SongInfo(); } } private static async Task GetLatestGithubReleaseDate(string url) { using HttpClient client = new HttpClient(); client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("CarretaFuracao", "0.7.9")); client.Timeout = TimeSpan.FromSeconds(20.0); try { GitHubRelease gitHubRelease = JsonUtility.FromJson(await client.GetStringAsync(url)); if (gitHubRelease != null && !string.IsNullOrEmpty(gitHubRelease.published_at)) { return DateTime.Parse(gitHubRelease.published_at).ToUniversalTime(); } } catch (Exception ex) { Logger.LogError((object)ex.Message); Logger.LogInfo((object)("Update check failed for url: " + url)); } return null; } public static bool CleanUp() { if (Directory.Exists(tempFolder)) { Directory.Delete(tempFolder, recursive: true); } return false; } public static bool Uninstall() { if (IsUpdatingResources) { Logger.LogWarning((object)"Attempted to uninstall downloader while resource update in progress. Please wait until the update is complete and try again if you need to."); return false; } CleanUp(); try { if (Directory.Exists(baseFolder)) { Directory.Delete(baseFolder, recursive: true); } } catch (Exception ex) { Logger.LogError((object)("Error during uninstallation of dependencies: " + ex.Message)); return false; } Logger.LogInfo((object)"Downloader dependencies uninstalled successfully."); return true; } public static Task Reinstall() { if (!Uninstall()) { return Task.FromResult(result: false); } Logger.LogInfo((object)"Starting downloader dependencies reinstall."); initializeTask = InitializeInternalAsync(); return initializeTask; } } } namespace BoomBoxCartMod.Patches { [HarmonyPatch(typeof(CameraAim))] internal class CamerAimPatch { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; private static ManualLogSource Logger => Instance.logger; [HarmonyPatch("Update")] [HarmonyPrefix] private static bool PatchPlayerAim(CameraAim __instance) { if (BoomboxUI.anyUISHown) { return Instance.modDisabled; } return true; } } [HarmonyPatch(typeof(LevelGenerator))] internal class LevelGeneratorPatch { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; private static ManualLogSource Logger => Instance.logger; [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPatch(LevelGenerator __instance) { if ((Object)(object)((Component)__instance).gameObject == (Object)null) { return; } Boombox.ResetLevelPlaylistSession(); if ((Object)(object)Instance.baseListener == (Object)null) { Instance.baseListener = ((Component)__instance).gameObject.AddComponent(); } if ((Object)(object)Instance.baseListener == (Object)null || !PhotonNetwork.IsConnected || Instance.modDisabled) { return; } Logger.LogDebug((object)"Level started, checking if other players are using the mod."); Instance.baseListener.lastUserAmount = Instance.baseListener.GetAllModUsers().Count; Instance.baseListener.GetAllModUsers().Clear(); if (PhotonNetwork.IsMasterClient && !Instance.modDisabled) { PhotonView photonView = ((MonoBehaviourPun)Instance.baseListener).photonView; if (photonView != null) { photonView.RPC("ModFeedbackCheck", (RpcTarget)1, new object[2] { "0.7.9", PhotonNetwork.LocalPlayer.ActorNumber }); } Instance.baseListener.AddModUser(PhotonNetwork.LocalPlayer.ActorNumber); } } } [HarmonyPatch(typeof(PhysGrabCart))] internal class PhysGrabCartPatch { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; private static ManualLogSource Logger => Instance.logger; [HarmonyPatch("Start")] [HarmonyPostfix] private static void PatchPhysGrabCartStart(PhysGrabCart __instance) { if (!Instance.modDisabled && !RunManager.instance.levelShop.Contains(RunManager.instance.levelCurrent)) { if ((Object)(object)((Component)__instance).GetComponent() == (Object)null) { Boombox boombox = ((Component)__instance).gameObject.AddComponent(); Instance.data.InitializeBoomboxData(boombox); Boombox.RegisterRuntimeCart(boombox); Logger.LogInfo((object)("Boombox component added to " + ((Object)__instance).name)); } if ((Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } } public static class PlayerGrabbingTracker { public static Dictionary playerGrabbingMap = new Dictionary(); public static bool IsLocalPlayerGrabbingCart(GameObject cart) { int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber; if (playerGrabbingMap.ContainsKey(actorNumber)) { return (Object)(object)playerGrabbingMap[actorNumber] == (Object)(object)cart; } return false; } public static void SetLocalPlayerGrabbing(GameObject obj) { int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber; if ((Object)(object)obj != (Object)null) { playerGrabbingMap[actorNumber] = obj; } else if (playerGrabbingMap.ContainsKey(actorNumber)) { playerGrabbingMap.Remove(actorNumber); } } } [HarmonyPatch(typeof(PlayerController))] internal class PlayerControllerPatch { private static BoomBoxCartMod Instance = BoomBoxCartMod.instance; private static ManualLogSource Logger => Instance.logger; [HarmonyPatch("Update")] [HarmonyPostfix] private static void PatchPlayerControllerUpdate(PlayerController __instance) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) if (Instance.modDisabled || (Object)(object)__instance != (Object)(object)PlayerController.instance) { return; } Boombox.TickRuntime(); if ((Object)(object)__instance.physGrabObject != (Object)null) { PlayerGrabbingTracker.SetLocalPlayerGrabbing(__instance.physGrabObject.gameObject); if (Keyboard.current != null && ((ButtonControl)Keyboard.current[Instance.OpenUIKey.Value]).wasPressedThisFrame) { Boombox component = __instance.physGrabObject.GetComponent(); if ((Object)(object)component != (Object)null) { component.ToggleRandomJukeboxLocal(); } } if (Keyboard.current != null && ((ButtonControl)Keyboard.current[Instance.MenuKey.Value]).wasPressedThisFrame) { BoomboxController component2 = __instance.physGrabObject.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.RequestBoomboxControl(); } } } else { PlayerGrabbingTracker.SetLocalPlayerGrabbing(null); } } } }