The BepInEx console will not appear when launching like it does for other games on Thunderstore (you can turn it back on in your BepInEx.cfg file). If your PEAK crashes on startup, add -dx12 to your launch parameters.
Decompiled source of sPEAKer v1.9.7
plugins/onlystar.sPEAKer.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using PEAKLib.Core; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; using Zorro.ControllerSupport; using sPEAKer.Scripts; using ytModule; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("onlystar")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Some guy had it aboard. Does it still work?")] [assembly: AssemblyFileVersion("1.9.7.0")] [assembly: AssemblyInformationalVersion("1.9.7+d9f8f843179425a526a3730a27a2052ce081ae48")] [assembly: AssemblyProduct("onlystar.sPEAKer")] [assembly: AssemblyTitle("sPEAKer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.9.7.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace sPEAKer { [BepInPlugin("onlystar.sPEAKer", "sPEAKer", "1.9.7")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private sealed class <DelayedManualMode>d__126 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Controller controller; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedManualMode>d__126(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; if (controller.droneBehaviour.IsFloating && controller.droneBehaviour.CurrentBehaviourMode != DroneBehaviourMode.Manual && controller.droneBehaviour.SwitchBehaviour(DroneBehaviourMode.Manual)) { sPEAKerUI.Instance.ShowTrackName("Drone Mode: Manual"); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <DelayedSpawn>d__131 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private Player <masterClient>5__1; private List<Character>.Enumerator <>s__2; private Character <character>5__3; private Vector3 <spawnPos>5__4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedSpawn>d__131(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <masterClient>5__1 = null; <>s__2 = default(List<Character>.Enumerator); <character>5__3 = null; <>1__state = -2; } private bool MoveNext() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(4f); <>1__state = 1; return true; case 1: <>1__state = -1; <masterClient>5__1 = PhotonNetwork.MasterClient; <>s__2 = Character.AllCharacters.GetEnumerator(); try { while (<>s__2.MoveNext()) { <character>5__3 = <>s__2.Current; Character obj = <character>5__3; if (!object.Equals((obj != null) ? ((MonoBehaviourPun)obj).photonView.Owner : null, <masterClient>5__1)) { continue; } <spawnPos>5__4 = new Vector3(<character>5__3.Center.x + Random.insideUnitSphere.z * 1.5f, <character>5__3.Center.y + 5f, <character>5__3.Center.z + Random.insideUnitSphere.z * 1.5f); PhotonNetwork.InstantiateItemRoom(((Object)_prefab).name, <spawnPos>5__4, Quaternion.identity); break; } } finally { ((IDisposable)<>s__2).Dispose(); } <>s__2 = default(List<Character>.Enumerator); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static GameObject _prefab = null; public static PeakBundle Bundle = null; public static bool ytModuleAvailable = false; private ConfigEntry<string> externalAudioFolderPathConfig = null; private ConfigEntry<bool> loadDemoSongConfig = null; private ConfigEntry<bool> extendedFormatSupportConfig = null; private ConfigEntry<bool> startupLoadingIndicatorConfig = null; private ConfigEntry<bool> fastLoadConfig = null; private ConfigEntry<bool> showTrackDurationConfig = null; private ConfigEntry<bool> displayAuthorInfoConfig = null; private ConfigEntry<bool> shuffleOnStartConfig = null; private ConfigEntry<bool> turnOnAndPlayConfig = null; private ConfigEntry<bool> soundEffectsEnabledConfig = null; private ConfigEntry<bool> useSpatialAudioConfig = null; private ConfigEntry<bool> spawnAtGameStartConfig = null; private ConfigEntry<bool> spawnWithF4Config = null; private ConfigEntry<bool> recallEnabledConfig = null; private ConfigEntry<KeyCode> droneToggleKeyConfig = null; private ConfigEntry<KeyCode> recallCommandKeyConfig = null; private ConfigEntry<KeyCode> danceKeyConfig = null; private ConfigEntry<KeyCode> spawnKeyConfig = null; private ConfigEntry<KeyCode> queueUIKeyConfig = null; private ConfigEntry<KeyCode> ytKeyConfig = null; private ConfigEntry<bool> debugModeConfig = null; internal bool Strange = false; public static Plugin Instance { get; private set; } = null; internal static ManualLogSource Log { get; private set; } = null; public static string DefaultMusicPath { get; private set; } = ""; public static string ExternalAudioFolderPath { get; private set; } = ""; public static bool LoadDemoSong { get; private set; } public static bool ExtendedFormatSupport { get; private set; } public static bool StartupLoadingIndicator { get; private set; } public static bool FastLoad { get; private set; } public static bool ShowTrackDuration { get; private set; } public static bool DisplayAuthorInfo { get; private set; } public static bool ShuffleOnStart { get; private set; } public static bool TurnOnAndPlay { get; private set; } public static bool SoundEffectsEnabled { get; private set; } public static bool UseSpatialAudio { get; private set; } private static bool SpawnAtGameStart { get; set; } private static bool SpawnWithF4 { get; set; } private static bool RecallEnabled { get; set; } public static KeyCode DroneToggleKey { get; private set; } public static KeyCode RecallCommandKey { get; private set; } public static KeyCode DanceKey { get; private set; } private static KeyCode SpawnKey { get; set; } public static KeyCode QueueUIKey { get; private set; } private static KeyCode YouTubeKey { get; set; } public static bool DebugMode { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; SetUserConfigs(); LocalizationFix(); ApplyPatches(); Log.LogInfo((object)"[Plugin] Plugin awakening..."); ModDefinition.GetOrCreate(((BaseUnityPlugin)this).Info.Metadata); AudioMaster.EnsureInitialized(); Netcode.EnsureInitialized(); if (ytModuleCompatibility.enabled) { ytModuleCompatibility.EnsureYouTubePlayerInitialized(); } try { Log.LogInfo((object)"[Plugin] Attempting to load bundle..."); BundleLoader.LoadBundleWithName((BaseUnityPlugin)(object)this, "ps.peakbundle", (Action<PeakBundle>)InitSpeaker); Log.LogInfo((object)"[Plugin] Bundle load request submitted"); } catch (Exception ex) { Log.LogError((object)("[Plugin] Failed to load bundle: " + ex.Message)); } } private void InitSpeaker(PeakBundle bundle) { Log.LogInfo((object)"[Plugin] Initializing sPEAKer with bundle..."); Bundle = bundle; GameObject val = Bundle.LoadAsset<GameObject>("assets/sPEAKer.prefab"); if ((Object)(object)val == (Object)null) { Log.LogError((object)"[Plugin] Failed to load sPEAKer prefab!"); return; } _prefab = val; Log.LogInfo((object)"[Plugin] Successfully loaded sPEAKer prefab."); SetupMaterials(val); SceneManager.sceneLoaded += OnSceneLoaded; val.AddComponent<Controller>(); Bundle.Mod.RegisterContent(); Log.LogInfo((object)"[Plugin] Plugin sPEAKer is loaded!"); } private static void SetupMaterials(GameObject gameObject) { Shader val = Shader.Find("W/Character"); if ((Object)(object)val == (Object)null) { Log.LogWarning((object)"[Plugin] W/Character shader not found, falling back to W/Peak_Standard"); val = Shader.Find("W/Peak_Standard"); } if ((Object)(object)val == (Object)null) { Log.LogError((object)"[Plugin] Neither W/Character nor W/Peak_Standard shader found!"); return; } Renderer[] componentsInChildren = gameObject.GetComponentsInChildren<Renderer>(); Renderer[] array = componentsInChildren; foreach (Renderer val2 in array) { Material[] materials = val2.materials; Material[] array2 = materials; foreach (Material val3 in array2) { if (((Object)val3.shader).name == "Universal Render Pipeline/Lit" || ((Object)val3.shader).name == "Standard") { Texture val4 = val3.GetTexture("_BaseMap") ?? val3.GetTexture("_MainTex"); val3.shader = val; if ((Object)(object)val4 != (Object)null) { val3.SetTexture("_BaseTexture", val4); } Log.LogInfo((object)("[Plugin] Applied W/Character shader to material: " + ((Object)val3).name)); } } val2.materials = materials; } } private void SetUserConfigs() { //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) DefaultMusicPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new InvalidOperationException(), "..\\onlystar-sPEAKer Music"); externalAudioFolderPathConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Audio Loading", "ExternalAudioFolderPath", DefaultMusicPath, "Folder where the mod will first try to load mixtapes and external audio from.\nIt will default to \"/plugins/onlystar-sPEAKer Music\" in case of error."); ExternalAudioFolderPath = externalAudioFolderPathConfig.Value; loadDemoSongConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio Loading", "LoadDemoSong", true, "Include a demo song in your track list."); LoadDemoSong = loadDemoSongConfig.Value; extendedFormatSupportConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio Loading", "ExtendedFormatSupport", true, "Enables loading of .mp3 and .wav files. May impact performance."); ExtendedFormatSupport = extendedFormatSupportConfig.Value; startupLoadingIndicatorConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio Loading", "StartupLoadingIndicator", true, "Shows a loading indicator when audio loading is occuring."); StartupLoadingIndicator = startupLoadingIndicatorConfig.Value; fastLoadConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio Loading", "FastLoad", true, "Shows a loading indicator when audio loading is occuring."); FastLoad = fastLoadConfig.Value; showTrackDurationConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Playback", "ShowTrackDuration", true, "Shows song's length and current time."); ShowTrackDuration = showTrackDurationConfig.Value; displayAuthorInfoConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Playback", "DisplayAuthorInfo", true, "Displays mixtape author under the stamina bar."); DisplayAuthorInfo = displayAuthorInfoConfig.Value; shuffleOnStartConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Playback", "ShuffleOnStart", false, "Start with Shuffle enabled by default. Saves you a toggle if you only want shuffled playback."); ShuffleOnStart = shuffleOnStartConfig.Value; turnOnAndPlayConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Playback", "TurnOnAndPlay", false, "Make sPEAKer start playing immediately after turning it on."); TurnOnAndPlay = turnOnAndPlayConfig.Value; soundEffectsEnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Playback", "SoundEffectsEnabled", true, "Enables JBL sound effects."); SoundEffectsEnabled = soundEffectsEnabledConfig.Value; useSpatialAudioConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Playback", "UseSpatialAudio", true, "Enables proximity-based audio. If disabled, playback will sound globally instead of coming from the sPEAKer."); UseSpatialAudio = useSpatialAudioConfig.Value; spawnAtGameStartConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawning", "SpawnAtGameStart", true, "Automatically spawn a sPEAKer around the crash site when a game starts."); SpawnAtGameStart = spawnAtGameStartConfig.Value; spawnWithF4Config = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawning", "SpawnWithF4", true, "Allows the host to spawn a sPEAKer in their hands by pressing the spawn key (see Keybinds section).\nNotes:\n - Only one sPEAKer may exist at a time (for now).\n - Only the host can spawn a sPEAKer (for now)."); SpawnWithF4 = spawnWithF4Config.Value; recallEnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawning", "RecallEnabled", true, "Enables recalling existing sPEAKer when pressing F4.\nNotes:\n - Only works if SpawnWithF4 is enabled.\n - sPEAKer will only be recalled if it's in the ground and not following any player."); RecallEnabled = recallEnabledConfig.Value; droneToggleKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "DroneToggle", (KeyCode)112, "Drone Mode toggle."); DroneToggleKey = droneToggleKeyConfig.Value; recallCommandKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "RecallCommand", (KeyCode)306, "Hold this while pressing DroneToggle key to make sPEAKer \"recall\" to you instead of looking for people nearby."); RecallCommandKey = recallCommandKeyConfig.Value; danceKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "DanceEmote", (KeyCode)111, "Makes sPEAKer dance when Drone Mode is active."); DanceKey = danceKeyConfig.Value; spawnKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "SpawnKey", (KeyCode)285, "Spawn a sPEAKer if the corresponding spawning config is enabled."); SpawnKey = spawnKeyConfig.Value; queueUIKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "QueueUIKey", (KeyCode)117, "Opens Queue UI."); QueueUIKey = queueUIKeyConfig.Value; ytKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "YouTubeKey", (KeyCode)121, "COMING IN 2.0 - Enter Youtube Radio mode. Hold Ctrl and this key to exit."); YouTubeKey = ytKeyConfig.Value; debugModeConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("zZzZz", "Debug", false, "Enable debug mode AKA verbose logging. If you want to report a bug, this is the way."); DebugMode = debugModeConfig.Value; } private void Update() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Invalid comparison between Unknown and I4 //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Invalid comparison between Unknown and I4 //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(SpawnKey) && PhotonNetwork.IsMasterClient) { if (!SpawnWithF4) { return; } if (AudioMaster.Instance.sPEAKerExists) { if (RecallEnabled) { Controller controller = Object.FindAnyObjectByType<Controller>(); if ((Object)(object)controller != (Object)null && (int)controller.item.itemState == 0 && !controller.droneBehaviour.IsFollowingTarget()) { controller.KillYourself(); Instance.SpawnAtHand(); } } } else { Instance.SpawnAtHand(); } } if (Input.GetKeyDown(DanceKey)) { Controller controller2 = Object.FindAnyObjectByType<Controller>(); if ((Object)(object)controller2 != (Object)null && (int)controller2.item.itemState == 0) { Netcode.Instance.SendRequestDanceEmote(); } else { Log.LogWarning((object)"[Plugin] Could not find controller to initiate Dance."); } } if (Input.GetKeyDown(DroneToggleKey)) { Controller controller3 = Object.FindAnyObjectByType<Controller>(); if ((Object)(object)controller3 != (Object)null && (int)controller3.item.itemState == 0) { bool key = Input.GetKey(RecallCommandKey); bool flag = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307); if (Strange && flag) { if (controller3.droneBehaviour.IsFloating) { controller3.CycleDroneBehaviour(); } else { controller3.ToggleFloatMode(); ((MonoBehaviour)this).StartCoroutine(DelayedManualMode(controller3)); } } else if (key) { controller3.RecallToPlayer(Character.localCharacter); } else if (controller3.droneBehaviour.IsFloating && controller3.droneBehaviour.CurrentBehaviourMode == DroneBehaviourMode.Manual) { controller3.droneBehaviour.SwitchBehaviour(DroneBehaviourMode.Orbiting); controller3.ToggleFloatMode(); sPEAKerUI.Instance.ShowTrackName("Drone Mode: OFF"); } else { controller3.ToggleFloatMode(); } } else { Log.LogWarning((object)"[Plugin] Could not find controller to initiate Drone/Recall mode."); } } if (Input.GetKeyDown(QueueUIKey)) { Controller controller4 = Object.FindAnyObjectByType<Controller>(); if ((Object)(object)controller4 != (Object)null && controller4.IsTurnedOn) { bool flag2 = (Object)(object)controller4.item.lastHolderCharacter == (Object)(object)Character.localCharacter; bool flag3 = (Object)(object)controller4.droneBehaviour != (Object)null && (Object)(object)controller4.droneBehaviour.FollowTarget == (Object)(object)Character.localCharacter; if (flag2 || flag3) { if (DebugMode) { Log.LogInfo((object)"[Plugin] QueueUI key pressed. Toggling Queue UI."); } QueueUI.Instance.ToggleQueue(); } } } if (Input.GetKeyDown(YouTubeKey)) { if (ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleYouTubeKeyPress(); } else { sPEAKerUI.Instance.ShowTrackName("YouTube Radio is not available"); } } YouTubeUI instance = YouTubeUI.Instance; if ((Object)(object)instance != (Object)null && instance.IsVisible() && Input.GetKeyDown((KeyCode)32) && ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleYouTubeSpaceKey(); } } [IteratorStateMachine(typeof(<DelayedManualMode>d__126))] private static IEnumerator DelayedManualMode(Controller controller) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedManualMode>d__126(0) { controller = controller }; } private void OnDestroy() { if (DebugMode) { Log.LogInfo((object)"[Plugin] Plugin destroying..."); } SceneManager.sceneLoaded -= OnSceneLoaded; Instance = null; Log = null; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (DebugMode) { Log.LogInfo((object)("[Plugin] Scene loaded: " + ((Scene)(ref scene)).name)); } if (((Scene)(ref scene)).name == "Airport") { sPEAKerUI.Instance.AttemptFontReload(); if (ytModuleCompatibility.enabled) { ytModuleCompatibility.ReloadYouTubeUIFont(); } AudioMaster.Instance.sPEAKerExists = false; } if (((Scene)(ref scene)).name.StartsWith("Level_") && SpawnAtGameStart && PhotonNetwork.IsConnected && PhotonNetwork.IsMasterClient) { ((MonoBehaviour)this).StartCoroutine(DelayedSpawn()); } } private void LocalizationFix() { LocalizedText.mainTable["NAME_SPEAKER"] = new List<string>(1) { "sPEAKer" }; LocalizedText.mainTable["..."] = new List<string>(1) { "..." }; LocalizedText.mainTable["VOLUME"] = new List<string>(13) { "Volume", "Volume", "Volume", "Lautstärke", "Volumen", "Volumen", "Volume", "Громкость", "Гучність", "音量", "音量", "音量", "볼륨" }; LocalizedText.mainTable["TURN ON"] = new List<string>(13) { "Turn On", "Allumer", "Accendere", "Einschalten", "Encender", "Encender", "Ligar", "Включить", "Увімкнути", "打开", "打開", "オンにする", "켜기" }; LocalizedText.mainTable["PAUSE"] = new List<string>(13) { "Pause", "Pause", "Pausa", "Pause", "Pausa", "Pausa", "Pausar", "Пауза", "Пауза", "暂停", "暫停", "一時停止", "일시정지" }; LocalizedText.mainTable["SHUFFLE"] = new List<string>(13) { "Shuffle", "Aléatoire", "Casuale", "Zufallswiedergabe", "Aleatorio", "Aleatorio", "Aleatório", "Перемешать", "Перемішати", "随机播放", "隨機播放", "シャッフル", "셔플" }; LocalizedText.mainTable["PREVIOUS"] = new List<string>(13) { "Previous", "Précédent", "Precedente", "Vorheriger", "Anterior", "Anterior", "Anterior", "Предыдущий", "Попередній", "上一首", "上一首", "前へ", "이전" }; LocalizedText.mainTable["LOOP"] = new List<string>(13) { "Loop", "Répéter", "Ripeti", "Wiederholen", "Repetir", "Repetir", "Repetir", "Повторить", "Повторити", "循环", "循環", "リピート", "반복" }; LocalizedText.mainTable["START RADIO"] = new List<string>(13) { "Start Radio", "Lancer la Radio", "Avvia Radio", "Radio starten", "Iniciar Radio", "Iniciar Radio", "Iniciar Rádio", "Запустить радио", "Запустити радіо", "播放电台", "播放電台", "ラジオを開始", "라디오 시작" }; LocalizedText.mainTable["BUFFERING"] = new List<string>(13) { "Buffering...", "Chargement...", "Caricamento...", "Wird geladen...", "Cargando...", "Cargando...", "Carregando...", "Загрузка...", "Завантаження...", "缓冲中...", "正在緩衝...", "バッファリング中...", "버퍼링..." }; LocalizedText.mainTable["SHUFFLE_ON"] = new List<string>(13) { "Shuffle is ON", "Lecture aléatoire activée", "Riproduzione casuale ON", "Zufallswiedergabe EIN", "Aleatorio activado", "Aleatorio activado", "Ordem aleatória LIGADA", "Вперемешку ВКЛ", "Перемішування УВІМК", "随机播放已开启", "隨機播放已開啟", "シャッフルオン", "셔플 켜짐" }; LocalizedText.mainTable["SHUFFLE_OFF"] = new List<string>(13) { "Shuffle is OFF", "Lecture aléatoire désactivée", "Riproduzione casuale OFF", "Zufallswiedergabe AUS", "Aleatorio desactivado", "Aleatorio desactivado", "Ordem aleatória DESLIGADA", "Вперемешку ВЫКЛ", "Перемішування ВИМК", "随机播放已关闭", "隨機播放已關閉", "シャッフルオフ", "셔플 꺼짐" }; LocalizedText.mainTable["LOOP_OFF"] = new List<string>(13) { "Loop is OFF", "Répétition désactivée", "Ripetizione OFF", "Wiederholung AUS", "Repetir desactivado", "Repetir desactivado", "Repetição DESLIGADA", "Повтор ВЫКЛ", "Повтор ВИМК", "循环已关闭", "循環已關閉", "ループオフ", "반복 꺼짐" }; LocalizedText.mainTable["LOOPING_ALL"] = new List<string>(13) { "Looping ALL songs", "Répétition de TOUS les titres", "Ripetizione di TUTTI i brani", "Wiederhole ALLE Titel", "Repitiendo TODAS las canciones", "Repitiendo TODAS las canciones", "Repetindo TODAS as músicas", "Повтор ВСЕХ песен", "Повтор УСІХ пісень", "循环播放全部歌曲", "循環播放全部歌曲", "全曲をループ再生", "모든 곡 반복" }; LocalizedText.mainTable["LOOPING_CURRENT"] = new List<string>(13) { "Looping CURRENT song", "Répétition du titre ACTUEL", "Ripetizione del brano ATTUALE", "Wiederhole AKTUELLEN Titel", "Repitiendo canción ACTUAL", "Repitiendo canción ACTUAL", "Repetindo música ATUAL", "Повтор ТЕКУЩЕЙ песни", "Повтор ПОТОЧНОЇ пісні", "单曲循环", "單曲循環", "この曲をループ再生", "현재 곡 반복" }; LocalizedText.mainTable["NO_PREVIOUS_SONGS"] = new List<string>(13) { "No previous songs available", "Aucun titre précédent disponible", "Nessun brano precedente disponibile", "Keine vorherigen Titel verfügbar", "No hay canciones anteriores", "No hay canciones anteriores", "Nenhuma música anterior disponível", "Нет предыдущих песен", "Немає попередніх пісень", "没有上一首歌曲", "沒有上一首歌曲", "前の曲はありません", "이전 곡 없음" }; LocalizedText.mainTable["PREVIOUS_SONG_NOT_CACHED"] = new List<string>(13) { "Previous song no longer cached", "Le titre précédent n'est plus en cache", "Brano precedente non più in cache", "Vorheriger Titel nicht mehr zwischengespeichert", "La canción anterior ya no está en caché", "La canción anterior ya no está en caché", "Música anterior não está mais em cache", "Предыдущая песня удалена из кэша", "Попередню пісню видалено з кешу", "上一首歌曲已不在缓存中", "上一首歌曲已不在快取中", "前の曲はキャッシュされていません", "이전 곡이 더 이상 캐시되지 않음" }; LocalizedText.mainTable["MIXTAPE_BY"] = new List<string>(13) { "Mixtape by", "Mixtape par", "Mixtape di", "Mixtape von", "Mixtape de", "Mixtape de", "Mixtape por", "Микстейп от", "Мікстейп від", "音乐合集,创作者:", "音樂合輯,創作者:", "ミックステープ作成者:", "믹스테이프 작성자:" }; } private void ApplyPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown Harmony val = new Harmony("onlystar.sPEAKer"); val.PatchAll(Assembly.GetExecutingAssembly()); } [IteratorStateMachine(typeof(<DelayedSpawn>d__131))] private static IEnumerator DelayedSpawn() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedSpawn>d__131(0); } public void SpawnAtHand() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) PhotonNetwork.Instantiate("0_Items/" + ((Object)_prefab).name, Character.localCharacter.Center + Vector3.up * 3f, Quaternion.identity, (byte)0, (object[])null).GetComponent<Item>().Interact(Character.localCharacter); } } } namespace sPEAKer.Scripts { public class AudioMaster : MonoBehaviourPun { [CompilerGenerated] private sealed class <MonitorForAutoSkip>d__115 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public AudioMaster <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <MonitorForAutoSkip>d__115(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Auto-skip monitor started."); } <>2__current = (object)new WaitUntil((Func<bool>)(() => (Object)(object)PersistentAudioSource != (Object)null && PersistentAudioSource.isPlaying)); <>1__state = 1; return true; case 1: <>1__state = -1; break; case 2: <>1__state = -1; break; } if (CurrentPlaybackState == PlaybackState.Playing && PersistentAudioSource.isPlaying) { if (IsYouTubeMode) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Auto-skip monitor ended - YouTube mode active."); } return false; } <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 2; return true; } if (CurrentPlaybackState == PlaybackState.Playing && !IsYouTubeMode && PhotonNetwork.IsMasterClient) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Song finished. Automatically playing next track."); } <>4__this.AutoSkipToNext(); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Auto-skip monitor ended."); } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static AudioMaster _instance; private Transform? _targetTransform; private Character? _targetCharacter; public Character? LastHolder; private bool _isFollowing; private const int HASH_ALGORITHM_VERSION = 1; private static FileHashCache _fileHashCache = new FileHashCache(); private static string _cachePath = ""; private static readonly List<string> _processedFilePaths = new List<string>(); private static int _cacheHits = 0; private static int _cacheMisses = 0; private Coroutine _autoSkipCoroutine; public static List<AudioClip> LoadedAudioClips { get; private set; } = new List<AudioClip>(); private static List<MixtapeInfo> AvailableMixtapes { get; set; } = new List<MixtapeInfo>(); public static AudioSource PersistentAudioSource { get; private set; } = null; private static AudioSource EffectsAudioSource { get; set; } = null; public static AudioSource YouTubeAudioSource { get; private set; } = null; public static bool IsInitialized { get; private set; } private static bool IsLoading { get; set; } public static bool IsYouTubeMode { get; private set; } public static Dictionary<string, int> SongHashToIndex { get; private set; } = new Dictionary<string, int>(); public static Dictionary<int, string> IndexToSongHash { get; private set; } = new Dictionary<int, string>(); public static PlaybackState CurrentPlaybackState { get; private set; } = PlaybackState.Stopped; public static int CurrentSongIndex { get; private set; } public static float CurrentPausedTime { get; private set; } private static MixtapeInfo CurrentMixtape { get; set; } public static LoopState CurrentLoopState { get; private set; } = LoopState.None; public static float NetworkVolume { get; private set; } = 0.35f; public static float LocalVolumeMultiplier { get; set; } = 0.5f; public static List<string> SongQueue { get; private set; } = new List<string>(); public bool sPEAKerExists { get; set; } = false; public static AudioMaster Instance { get { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if ((Object)(object)_instance == (Object)null) { _instance = Object.FindAnyObjectByType<AudioMaster>(); if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject("sPEAKerAudioMaster"); _instance = val.AddComponent<AudioMaster>(); Object.DontDestroyOnLoad((Object)(object)val); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Created new instance and marked as DontDestroyOnLoad."); } } } return _instance; } } public static AudioSource ActiveSource { get { if (IsYouTubeMode) { return YouTubeAudioSource; } return PersistentAudioSource; } } public static event Action<string> OnSongChanged; public static event Action OnQueueSynced; public static void EnsureInitialized() { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] EnsureInitialized called."); } AudioMaster instance = Instance; if (!IsInitialized && !IsLoading) { Plugin.Log.LogInfo((object)"[AudioMaster] Not initialized, starting initialization..."); instance.InitializeAsync(); } } private void Awake() { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Awake called."); } if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Duplicate instance detected, destroying."); } Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Instance set and marked as DontDestroyOnLoad."); } InitializeAudioSources(); SceneManager.sceneLoaded += OnSceneLoaded; if (!IsInitialized && !IsLoading) { InitializeAsync(); } } private void InitializeAudioSources() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown AnimationCurve val = new AnimationCurve(); val.AddKey(0f, 1f); val.AddKey(0.25f, 0.85f); val.AddKey(0.6f, 0.2f); val.AddKey(1f, 0f); for (int i = 0; i < val.length; i++) { val.SmoothTangents(i, 0f); } if ((Object)(object)PersistentAudioSource == (Object)null) { PersistentAudioSource = ((Component)this).gameObject.AddComponent<AudioSource>(); PersistentAudioSource.loop = false; PersistentAudioSource.playOnAwake = false; PersistentAudioSource.volume = NetworkVolume * LocalVolumeMultiplier; if (Plugin.UseSpatialAudio) { PersistentAudioSource.minDistance = 2f; PersistentAudioSource.maxDistance = 50f; PersistentAudioSource.spatialBlend = 1f; PersistentAudioSource.dopplerLevel = 0.1f; PersistentAudioSource.spatialize = true; PersistentAudioSource.rolloffMode = (AudioRolloffMode)2; PersistentAudioSource.SetCustomCurve((AudioSourceCurveType)0, val); } else { PersistentAudioSource.spatialBlend = 0f; } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Created persistent audio source."); } } if ((Object)(object)EffectsAudioSource == (Object)null) { EffectsAudioSource = ((Component)this).gameObject.AddComponent<AudioSource>(); EffectsAudioSource.playOnAwake = false; EffectsAudioSource.loop = false; if (Plugin.UseSpatialAudio) { EffectsAudioSource.spatialBlend = 1f; EffectsAudioSource.volume = 1f; EffectsAudioSource.minDistance = 2f; EffectsAudioSource.maxDistance = 25f; EffectsAudioSource.rolloffMode = (AudioRolloffMode)0; } else { EffectsAudioSource.spatialBlend = 0f; EffectsAudioSource.volume = 1f; } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Created effects audio source."); } } if ((Object)(object)YouTubeAudioSource == (Object)null) { YouTubeAudioSource = ((Component)this).gameObject.AddComponent<AudioSource>(); YouTubeAudioSource.playOnAwake = false; YouTubeAudioSource.loop = false; YouTubeAudioSource.volume = NetworkVolume * LocalVolumeMultiplier; if (Plugin.UseSpatialAudio) { YouTubeAudioSource.minDistance = 2f; YouTubeAudioSource.maxDistance = 50f; YouTubeAudioSource.spatialBlend = 1f; YouTubeAudioSource.dopplerLevel = 0.1f; YouTubeAudioSource.spatialize = true; YouTubeAudioSource.rolloffMode = (AudioRolloffMode)2; YouTubeAudioSource.SetCustomCurve((AudioSourceCurveType)0, val); } else { YouTubeAudioSource.spatialBlend = 0f; } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Created YouTube audio source."); } } } private async Task InitializeAsync() { IsLoading = true; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Starting initialization... "); } LoadOrCreateHashCache(); sPEAKerUI.Instance.ShowLoading("Initializing sPEAKer..."); await LoadFolderAudio(); sPEAKerUI.Instance.ShowLoading("Loading bundle audio..."); await LoadBundleAudio(); if (LoadedAudioClips.Count > 0) { SongQueue = IndexToSongHash.Values.ToList(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Initial song queue populated with {SongQueue.Count} tracks."); } if (Plugin.ShuffleOnStart) { Master_ShuffleQueue(broadcast: false); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Shuffled on start."); } } } sPEAKerUI.Instance.ShowLoadingComplete(); SaveChangesToCache(); IsInitialized = true; IsLoading = false; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Initialization complete. Total clips loaded: " + LoadedAudioClips.Count + ", Mixtapes loaded: " + AvailableMixtapes.Count)); } } private async Task LoadBundleAudio() { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Starting bundle audio loading..."); } int waitCount = 0; while (Plugin.Bundle == null && waitCount < 100) { await Task.Delay(100); waitCount++; } if (Plugin.Bundle == null) { Plugin.Log.LogError((object)"[AudioMaster] Bundle not available after 10 seconds - skipping bundle audio loading"); return; } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Bundle is now available, proceeding with loading..."); } string[] allAssets = Plugin.Bundle.GetAllAssetNames(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Total assets in bundle: " + allAssets.Length)); } string[] jblFX = allAssets.Where((string asset) => asset.Contains("jbl_fx") && asset.EndsWith(".ogg")).ToArray(); string[] array = jblFX; foreach (string fx in array) { AudioClip clip = Plugin.Bundle.LoadAsset<AudioClip>(fx); string filename = Path.GetFileNameWithoutExtension(fx); SoundEffects.SetClip(filename, clip); } string[] laserFX = allAssets.Where((string asset) => asset.Contains("laser") && asset.EndsWith(".ogg")).ToArray(); string[] array2 = laserFX; foreach (string fx2 in array2) { AudioClip clip2 = Plugin.Bundle.LoadAsset<AudioClip>(fx2); string filename2 = Path.GetFileNameWithoutExtension(fx2); SoundEffects.SetClip(filename2, clip2); } string whiteNoise = allAssets.FirstOrDefault((string asset) => asset.Contains("whitenoise")); if (!string.IsNullOrEmpty(whiteNoise)) { AudioClip whiteNoiseClip = Plugin.Bundle.LoadAsset<AudioClip>(whiteNoise); SoundEffects.SetClip("whitenoise", whiteNoiseClip); } if (Plugin.LoadDemoSong) { AudioClip demoClip = Plugin.Bundle.LoadAsset<AudioClip>("assets/speaker audio/portal.ogg"); if ((Object)(object)demoClip != (Object)null) { demoClip.LoadAudioData(); while ((int)demoClip.loadState == 1) { await Task.Yield(); } if ((int)demoClip.loadState == 2) { LoadedAudioClips.Add(demoClip); string songHash = "portalAsset"; int index = LoadedAudioClips.Count - 1; SongHashToIndex[songHash] = index; IndexToSongHash[index] = songHash; } else { Plugin.Log.LogError((object)"[AudioMaster] Failed to load demo song audio data"); } } } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Bundle loading complete."); } } private async Task LoadFolderAudio() { List<string> foldersToProcess = new List<string>(); string finalUserPath; try { string configuredPath = Plugin.ExternalAudioFolderPath; if (Directory.Exists(configuredPath) || (!Directory.Exists(configuredPath) && Directory.GetParent(configuredPath).Exists)) { finalUserPath = configuredPath; } else { Plugin.Log.LogWarning((object)("[AudioMaster] Configured path '" + configuredPath + "' is invalid. Falling back to the default path.")); finalUserPath = Plugin.DefaultMusicPath; } } catch (Exception ex) { Exception e = ex; Plugin.Log.LogError((object)("[AudioMaster] Error validating configured path '" + Plugin.ExternalAudioFolderPath + "'. Falling back to default. Error: " + e.Message)); finalUserPath = Plugin.DefaultMusicPath; } try { if (!Directory.Exists(finalUserPath)) { Directory.CreateDirectory(finalUserPath); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Created audio folder: " + finalUserPath)); } } foldersToProcess.Add(finalUserPath); } catch (Exception e2) { Plugin.Log.LogError((object)("[AudioMaster] Failed to create or access the final user path '" + finalUserPath + "'. Error: " + e2.Message)); } string pluginsPath = Paths.PluginPath; if (Directory.Exists(pluginsPath)) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Scanning for mixtape mods in: " + pluginsPath)); } string[] subDirectories = Directory.GetDirectories(pluginsPath, "*", SearchOption.AllDirectories); string[] array = subDirectories; foreach (string subDir in array) { if (File.Exists(Path.Combine(subDir, "sPEAKer.json")) && !foldersToProcess.Contains(subDir)) { foldersToProcess.Add(subDir); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Found mixtape mod folder: " + subDir)); } } } } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Total unique folders to process: " + foldersToProcess.Count)); } foreach (string folderPath in foldersToProcess) { await ProcessFolderForAudio(folderPath); } } private async Task ProcessAudioFolder(string folderPath) { string folderPath2 = folderPath; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Processing loose audio files in: " + folderPath2)); } List<string> searchPatterns = new List<string> { "*.ogg" }; if (Plugin.ExtendedFormatSupport) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Extended format support is ON. Searching for .mp3 and .wav files."); } searchPatterns.Add("*.mp3"); searchPatterns.Add("*.wav"); } List<string> audioFiles = searchPatterns.SelectMany((string pattern) => Directory.GetFiles(folderPath2, pattern, SearchOption.TopDirectoryOnly)).ToList(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Found " + audioFiles.Count + " loose audio files in " + folderPath2)); } if (audioFiles.Count == 0) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] No loose audio files found, skipping: " + folderPath2)); } return; } _processedFilePaths.AddRange(audioFiles.Select(NormalizePath)); int successCount = 0; int failCount = 0; foreach (string file in audioFiles) { string fileName = Path.GetFileName(file); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Loading file: " + fileName)); } string url = "file://" + file.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); AudioType audioType = GetAudioTypeFromExtension(file); if ((int)audioType == 0) { if (Plugin.DebugMode) { Plugin.Log.LogWarning((object)("[AudioMaster] Skipping unsupported file type: " + fileName)); } continue; } UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, audioType); try { ((DownloadHandlerAudioClip)www.downloadHandler).streamAudio = Plugin.FastLoad; await (AsyncOperation)(object)www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { Plugin.Log.LogError((object)("[AudioMaster] Failed to load audio: " + fileName + " - Error: " + www.error)); failCount++; } else { AudioClip clip = DownloadHandlerAudioClip.GetContent(www); if ((Object)(object)clip != (Object)null) { ((Object)clip).name = Path.GetFileNameWithoutExtension(file); string songHash = await GenerateFileHashAsync(file); if (songHash == null) { continue; } clip.LoadAudioData(); while ((int)clip.loadState == 1) { await Task.Yield(); } if ((int)clip.loadState == 2) { LoadedAudioClips.Add(clip); int index = LoadedAudioClips.Count - 1; SongHashToIndex[songHash] = index; IndexToSongHash[index] = songHash; successCount++; } else { Plugin.Log.LogError((object)("[AudioMaster] Failed to preload audio data for: " + file)); } } } } finally { ((IDisposable)www)?.Dispose(); } await Task.Yield(); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Loose file processing complete for " + folderPath2 + ". Success: " + successCount + ", Failed: " + failCount)); } } private async Task ProcessFolderForAudio(string folderPath) { string folderPath2 = folderPath; List<string> searchPatterns = new List<string> { "*.ogg" }; if (Plugin.ExtendedFormatSupport) { searchPatterns.Add("*.mp3"); searchPatterns.Add("*.wav"); } List<string> looseAudioFiles = searchPatterns.SelectMany((string pattern) => Directory.GetFiles(folderPath2, pattern, SearchOption.TopDirectoryOnly)).ToList(); string[] subDirectories = Directory.GetDirectories(folderPath2); bool hasSpeakerJson = File.Exists(Path.Combine(folderPath2, "sPEAKer.json")); bool hasLooseAudio = looseAudioFiles.Count > 0; if (hasSpeakerJson && hasLooseAudio) { await LoadLooseFilesAsMixtape(folderPath2, looseAudioFiles); } else if (hasLooseAudio) { await ProcessAudioFolder(folderPath2); } string[] array = subDirectories; foreach (string mixtapeDir in array) { await LoadMixtapeAsync(mixtapeDir); } } private async Task LoadLooseFilesAsMixtape(string folderPath, List<string> audioFiles) { string folderName = new DirectoryInfo(folderPath).Name; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Loading loose files as mixtape: " + folderName)); } MixtapeInfo mixtapeInfo = new MixtapeInfo { Name = folderName, Author = "Unknown", FirstSongIndex = LoadedAudioClips.Count, Path = folderPath }; string metadataPath = Path.Combine(folderPath, "mixtape.json"); if (File.Exists(metadataPath)) { try { string jsonContent = File.ReadAllText(metadataPath); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Raw JSON content: " + jsonContent)); } MixtapeMetadata metadata = JsonUtility.FromJson<MixtapeMetadata>(jsonContent); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Parsed metadata - name: '" + metadata?.name + "', author: '" + metadata?.author + "'")); } if (metadata != null) { if (!string.IsNullOrEmpty(metadata.name)) { mixtapeInfo.Name = metadata.name; sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + mixtapeInfo.Name + "..."); } else { sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + folderName + "..."); } if (!string.IsNullOrEmpty(metadata.author)) { mixtapeInfo.Author = metadata.author; } } else if (Plugin.DebugMode) { Plugin.Log.LogWarning((object)"[AudioMaster] JsonUtility.FromJson returned null for mixtape metadata"); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Final mixtape info - Name: " + mixtapeInfo.Name + ", Author: " + mixtapeInfo.Author)); } } catch (Exception ex) { Exception e2 = ex; Plugin.Log.LogWarning((object)("[AudioMaster] Failed to parse mixtape metadata for " + folderName + ": " + e2.Message)); string[] parts2 = folderName.Split('-', 2); if (parts2.Length == 2) { mixtapeInfo.Author = parts2[0].Trim(); mixtapeInfo.Name = parts2[1].Trim(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Used fallback parsing - Author: " + mixtapeInfo.Author + ", Name: " + mixtapeInfo.Name)); } } } } else { string[] parts = folderName.Split('-', 2); if (parts.Length == 2) { mixtapeInfo.Author = parts[0].Trim(); mixtapeInfo.Name = parts[1].Trim(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Parsed folder name - Author: " + mixtapeInfo.Author + ", Name: " + mixtapeInfo.Name)); } } sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + mixtapeInfo.Name + "..."); } string iconPath = Path.Combine(folderPath, "icon.png"); if (File.Exists(iconPath)) { try { string iconUrl = "file://" + iconPath.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); UnityWebRequest www2 = UnityWebRequest.Get(iconUrl); try { await (AsyncOperation)(object)www2.SendWebRequest(); if ((int)www2.result == 2 || (int)www2.result == 3) { Plugin.Log.LogError((object)("[AudioMaster] Failed to load icon for mixtape " + folderName + ": " + www2.error)); } else { byte[] iconBytes = www2.downloadHandler.data; Texture2D iconTexture = new Texture2D(2, 2); if (ImageConversion.LoadImage(iconTexture, iconBytes)) { mixtapeInfo.IconTexture = iconTexture; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Successfully loaded icon for loose files mixtape: " + folderName)); } } } } finally { ((IDisposable)www2)?.Dispose(); } } catch (Exception ex) { Exception e = ex; Plugin.Log.LogError((object)("[AudioMaster] Exception loading icon for loose files mixtape " + folderName + ": " + e.Message)); } } _processedFilePaths.AddRange(audioFiles.Select(NormalizePath)); int songSuccessCount = 0; float totalDuration = 0f; foreach (string audioFile in audioFiles) { string fileName = Path.GetFileName(audioFile); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Loading loose file as mixtape song: " + fileName)); } string url = "file://" + audioFile.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); AudioType audioType = GetAudioTypeFromExtension(audioFile); if ((int)audioType == 0) { if (Plugin.DebugMode) { Plugin.Log.LogWarning((object)("[AudioMaster] Skipping unsupported file type: " + fileName)); } continue; } UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, audioType); try { ((DownloadHandlerAudioClip)www.downloadHandler).streamAudio = Plugin.FastLoad; await (AsyncOperation)(object)www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { Plugin.Log.LogError((object)("[AudioMaster] Failed to load loose audio: " + fileName + " - Error: " + www.error)); } else { AudioClip clip = DownloadHandlerAudioClip.GetContent(www); if ((Object)(object)clip != (Object)null) { ((Object)clip).name = Path.GetFileNameWithoutExtension(audioFile); string songHash = await GenerateFileHashAsync(audioFile); if (songHash == null) { continue; } clip.LoadAudioData(); while ((int)clip.loadState == 1) { await Task.Yield(); } if ((int)clip.loadState == 2) { LoadedAudioClips.Add(clip); int index = LoadedAudioClips.Count - 1; SongHashToIndex[songHash] = index; IndexToSongHash[index] = songHash; totalDuration += clip.length; songSuccessCount++; } else { Plugin.Log.LogError((object)("[AudioMaster] Failed to preload audio data for loose file: " + audioFile)); } } } } finally { ((IDisposable)www)?.Dispose(); } await Task.Yield(); } mixtapeInfo.SongCount = songSuccessCount; mixtapeInfo.LastSongIndex = mixtapeInfo.FirstSongIndex + songSuccessCount - 1; mixtapeInfo.TotalLength = FormatDuration(totalDuration); if (songSuccessCount > 0) { AvailableMixtapes.Add(mixtapeInfo); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Successfully loaded loose files as mixtape '" + mixtapeInfo.Name + "' by " + mixtapeInfo.Author + " - " + songSuccessCount + " songs, " + mixtapeInfo.TotalLength + " total")); } } else { Plugin.Log.LogWarning((object)("[AudioMaster] Loose files in '" + folderName + "' could not be loaded as mixtape.")); } } private async Task<bool> LoadMixtapeAsync(string mixtapePath) { string mixtapePath2 = mixtapePath; string mixtapeName = new DirectoryInfo(mixtapePath2).Name; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Loading mixtape: " + mixtapeName)); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] From path: " + mixtapePath2)); } MixtapeInfo mixtapeInfo = new MixtapeInfo { Name = mixtapeName, Author = "Unknown", FirstSongIndex = LoadedAudioClips.Count, Path = mixtapePath2 }; string metadataPath = Path.Combine(mixtapePath2, "mixtape.json"); if (File.Exists(metadataPath)) { try { string jsonContent = File.ReadAllText(metadataPath); MixtapeMetadata metadata = JsonUtility.FromJson<MixtapeMetadata>(jsonContent); if (!string.IsNullOrEmpty(metadata.name)) { mixtapeInfo.Name = metadata.name; sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + mixtapeInfo.Name + "..."); } else { sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + mixtapeName + "..."); } if (!string.IsNullOrEmpty(metadata.author)) { mixtapeInfo.Author = metadata.author; } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Loaded mixtape metadata - Name: " + mixtapeInfo.Name + ", Author: " + mixtapeInfo.Author)); } } catch (Exception ex) { Exception e2 = ex; Plugin.Log.LogWarning((object)("[AudioMaster] Failed to parse mixtape metadata for " + mixtapeName + ": " + e2.Message)); } } string iconPath = Path.Combine(mixtapePath2, "..", "icon.png"); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Checking for icon at path: " + iconPath)); } string normalizedIconPath = Path.GetFullPath(iconPath); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Normalized icon path: " + normalizedIconPath)); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Icon file exists: {File.Exists(normalizedIconPath)}"); } if (File.Exists(normalizedIconPath)) { try { FileInfo fileInfo = new FileInfo(normalizedIconPath); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Icon file size: {fileInfo.Length} bytes"); } string iconUrl = "file://" + normalizedIconPath.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Converted icon URL: " + iconUrl)); } UnityWebRequest www2 = UnityWebRequest.Get(iconUrl); try { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Starting web request for icon: " + mixtapeName)); } await (AsyncOperation)(object)www2.SendWebRequest(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Web request result: {www2.result}"); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Response code: {www2.responseCode}"); } if ((int)www2.result == 2 || (int)www2.result == 3) { Plugin.Log.LogError((object)("[AudioMaster] Failed to load icon for mixtape " + mixtapeName + ": " + www2.error)); } else { byte[] iconBytes = www2.downloadHandler.data; Texture2D iconTexture = new Texture2D(2, 2); if (ImageConversion.LoadImage(iconTexture, iconBytes)) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Icon texture dimensions: {((Texture)iconTexture).width}x{((Texture)iconTexture).height}"); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Icon texture is readable: {((Texture)iconTexture).isReadable}"); } mixtapeInfo.IconTexture = iconTexture; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Successfully loaded and assigned icon for mixtape: " + mixtapeName)); } } else { Plugin.Log.LogError((object)("[AudioMaster] Failed to load image data from bytes for: " + mixtapeName)); } } } finally { ((IDisposable)www2)?.Dispose(); } } catch (Exception ex) { Exception e = ex; Plugin.Log.LogError((object)("[AudioMaster] Exception loading icon for mixtape " + mixtapeName + ": " + e.Message)); } } List<string> searchPatterns = new List<string> { "*.ogg" }; if (Plugin.ExtendedFormatSupport) { searchPatterns.Add("*.mp3"); searchPatterns.Add("*.wav"); } List<string> audioFiles = searchPatterns.SelectMany((string pattern) => Directory.GetFiles(mixtapePath2, pattern, SearchOption.TopDirectoryOnly)).ToList(); _processedFilePaths.AddRange(audioFiles.Select(NormalizePath)); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Found " + audioFiles.Count + " audio files in mixtape.")); } int songSuccessCount = 0; float totalDuration = 0f; foreach (string audioFile in audioFiles) { string fileName = Path.GetFileName(audioFile); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Loading mixtape song: " + fileName)); } string url = "file://" + audioFile.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); AudioType audioType = GetAudioTypeFromExtension(audioFile); if ((int)audioType == 0) { if (Plugin.DebugMode) { Plugin.Log.LogWarning((object)("[AudioMaster] Skipping unsupported file type in mixtape: " + fileName)); } continue; } UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, audioType); try { ((DownloadHandlerAudioClip)www.downloadHandler).streamAudio = Plugin.FastLoad; await (AsyncOperation)(object)www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { Plugin.Log.LogError((object)("[AudioMaster] Failed to load mixtape audio: " + fileName + " - Error: " + www.error)); } else { AudioClip clip = DownloadHandlerAudioClip.GetContent(www); if ((Object)(object)clip != (Object)null) { ((Object)clip).name = Path.GetFileNameWithoutExtension(audioFile); string songHash = await GenerateFileHashAsync(audioFile); if (songHash == null) { continue; } clip.LoadAudioData(); while ((int)clip.loadState == 1) { await Task.Yield(); } if ((int)clip.loadState == 2) { LoadedAudioClips.Add(clip); int index = LoadedAudioClips.Count - 1; SongHashToIndex[songHash] = index; IndexToSongHash[index] = songHash; totalDuration += clip.length; songSuccessCount++; } else { Plugin.Log.LogError((object)("[AudioMaster] Failed to preload audio data for mixtape song: " + audioFile)); } } } } finally { ((IDisposable)www)?.Dispose(); } await Task.Yield(); } mixtapeInfo.SongCount = songSuccessCount; mixtapeInfo.LastSongIndex = mixtapeInfo.FirstSongIndex + songSuccessCount - 1; mixtapeInfo.TotalLength = FormatDuration(totalDuration); if (songSuccessCount > 0) { AvailableMixtapes.Add(mixtapeInfo); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Successfully loaded mixtape '" + mixtapeInfo.Name + "' by " + mixtapeInfo.Author + " - " + songSuccessCount + " songs, " + mixtapeInfo.TotalLength + " total")); } return true; } Plugin.Log.LogWarning((object)("[AudioMaster] Mixtape folder '" + mixtapeInfo.Name + "' contains no valid audio files.")); return false; } public void LoadAudioClipsFromBundle() { //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Invalid comparison between Unknown and I4 //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_031e: Invalid comparison between Unknown and I4 if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Starting bundle audio loading..."); } string[] allAssetNames = Plugin.Bundle.GetAllAssetNames(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Total assets in bundle: " + allAssetNames.Length)); Plugin.Log.LogInfo((object)"--- All Assets in Bundle ---"); string[] array = allAssetNames; foreach (string text in array) { Plugin.Log.LogInfo((object)(" -> " + text)); } Plugin.Log.LogInfo((object)"----------------------------"); } string[] array2 = allAssetNames.Where((string asset) => asset.Contains("jbl_fx") && asset.EndsWith(".ogg")).ToArray(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Found {array2.Length} JBL_FX assets:"); string[] array3 = array2; foreach (string text2 in array3) { Plugin.Log.LogInfo((object)(" -> " + text2)); } } string[] array4 = array2; foreach (string text3 in array4) { AudioClip clip2 = Plugin.Bundle.LoadAsset<AudioClip>(text3); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text3); SoundEffects.SetClip(fileNameWithoutExtension, clip2); } string text4 = allAssetNames.FirstOrDefault((string asset) => asset.Contains("whitenoise")); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Found whitenoise asset: " + (text4 ?? "NULL"))); } AudioClip clip3 = Plugin.Bundle.LoadAsset<AudioClip>(text4); SoundEffects.SetClip("whitenoise", clip3); string[] array5 = allAssetNames.Where((string asset) => asset.Contains("speaker audio") && asset.EndsWith(".ogg")).ToArray(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Found " + array5.Length + " demo songs in bundle.")); string[] array6 = array5; foreach (string text5 in array6) { Plugin.Log.LogInfo((object)(" -> " + text5)); } } int num = 0; int num2 = 0; string[] array7 = array5; foreach (string text6 in array7) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Loading bundle audio: " + text6)); } try { AudioClip clip = Plugin.Bundle.LoadAsset<AudioClip>(text6); if ((Object)(object)clip != (Object)null) { if ((int)clip.loadState == 2) { goto IL_0354; } clip.LoadAudioData(); if ((int)clip.loadState != 3) { goto IL_0354; } Plugin.Log.LogError((object)("[AudioMaster] Failed to preload audio data for bundle clip: " + ((Object)clip).name)); num2++; } else { Plugin.Log.LogError((object)("[AudioMaster] Failed to load audio clip from bundle: " + text6)); num2++; } goto end_IL_02b9; IL_0354: if (LoadedAudioClips.All((AudioClip c) => ((Object)c).name != ((Object)clip).name)) { LoadedAudioClips.Add(clip); string text7 = "portalAsset"; int num3 = LoadedAudioClips.Count - 1; SongHashToIndex[text7] = num3; IndexToSongHash[num3] = text7; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Successfully loaded bundle audio: " + ((Object)clip).name + " (Length: " + clip.length + "s)")); } num++; } else if (Plugin.DebugMode) { Plugin.Log.LogWarning((object)("[AudioMaster] Audio clip already loaded: " + ((Object)clip).name)); } end_IL_02b9:; } catch (Exception ex) { Plugin.Log.LogError((object)("[AudioMaster] Exception loading " + text6 + ": " + ex.Message)); num2++; } } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Bundle loading complete. Success: " + num + ", Failed: " + num2 + ", Total clips now: " + LoadedAudioClips.Count)); } if (LoadedAudioClips.Count > 0 && Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] All loaded clips:"); for (int n = 0; n < LoadedAudioClips.Count; n++) { Plugin.Log.LogInfo((object)(" [" + (n + 1) + "] " + ((Object)LoadedAudioClips[n]).name)); } } } public static void UpdatePlaybackState(PlaybackState newState, int songIndex = -1, float pausedTime = 0f) { CurrentPlaybackState = newState; if (songIndex >= 0) { CurrentSongIndex = songIndex; } CurrentPausedTime = pausedTime; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Playback state updated - State: {newState}, Song: {CurrentSongIndex}, PausedTime: {pausedTime}"); } } private static void ResetPlaybackState() { CurrentPlaybackState = PlaybackState.Stopped; CurrentSongIndex = 0; CurrentPausedTime = 0f; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Playback state reset to defaults"); } } private string FormatDuration(float totalSeconds) { int num = Mathf.FloorToInt(totalSeconds / 60f); int num2 = Mathf.FloorToInt(totalSeconds % 60f); return $"{num}:{num2:00}"; } private string NormalizePath(string path) { return path.Replace('\\', '/'); } private async Task<string> GenerateFileHashAsync(string filePath) { string normalizedPath = NormalizePath(filePath); try { FileInfo fileInfo = new FileInfo(filePath); long size = fileInfo.Length; DateTime lastModified = fileInfo.LastWriteTimeUtc; if (_fileHashCache.Hashes.TryGetValue(normalizedPath, out CacheEntry cachedEntry) && cachedEntry.Size == size && cachedEntry.LastModified == lastModified) { if (Plugin.DebugMode) { _cacheHits++; } return cachedEntry.Hash; } if (Plugin.DebugMode) { _cacheMisses++; } string fileName = Path.GetFileName(filePath); string uniqueId = $"{fileName}|{size}"; using SHA256 sha256 = SHA256.Create(); byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(uniqueId)); string newHash = Convert.ToBase64String(hashBytes).Substring(0, 12); _fileHashCache.Hashes[normalizedPath] = new CacheEntry { Hash = newHash, LastModified = lastModified, Size = size }; return await Task.FromResult(newHash); } catch (Exception e) { Plugin.Log.LogError((object)("[AudioMaster] Failed to generate metadata hash for " + filePath + ": " + e.Message)); return await Task.FromResult<string>(null); } } public static MixtapeInfo GetMixtapeForSongIndex(int songIndex) { return AvailableMixtapes.FirstOrDefault((MixtapeInfo m) => m.ContainsSongIndex(songIndex)) ?? new MixtapeInfo { Author = "unknown" }; } private void LoadOrCreateHashCache() { try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string text = Path.Combine(directoryName, "data"); _cachePath = Path.Combine(text, "file_hashes.json"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[Cache] Created cache data folder at: " + text)); } } if (File.Exists(_cachePath)) { string text2 = File.ReadAllText(_cachePath); FileHashCache fileHashCache = JsonConvert.DeserializeObject<FileHashCache>(text2); if (fileHashCache != null && fileHashCache.Version == 1 && fileHashCache.AlgorithmVersion == 1) { _fileHashCache = fileHashCache; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[Cache] Successfully loaded {_fileHashCache.Hashes.Count} hashes from cache (Algo v{1})."); } return; } if (Plugin.DebugMode) { Plugin.Log.LogWarning((object)"[Cache] Cache is from an incompatible algorithm version. Regenerating."); } _fileHashCache = new FileHashCache { AlgorithmVersion = 1 }; } else { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[Cache] No cache file found. A new one will be created."); } _fileHashCache = new FileHashCache { AlgorithmVersion = 1 }; } } catch (Exception ex) { Plugin.Log.LogError((object)("[Cache] Error loading hash cache, regenerating. Error: " + ex.Message)); _fileHashCache = new FileHashCache { AlgorithmVersion = 1 }; } } private void SaveChangesToCache() { try { List<string> list = _fileHashCache.Hashes.Keys.Except(_processedFilePaths).ToList(); foreach (string item in list) { _fileHashCache.Hashes.Remove(item); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[Cache] Cleanup removed {list.Count} stale entries."); Plugin.Log.LogInfo((object)$"[Cache] Stats: {_cacheHits} hits, {_cacheMisses} misses."); } string contents = JsonConvert.SerializeObject((object)_fileHashCache, (Formatting)1); string text = _cachePath + ".tmp"; File.WriteAllText(text, contents); if (File.Exists(_cachePath)) { File.Replace(text, _cachePath, null); } else { File.Move(text, _cachePath); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[Cache] Saved {_fileHashCache.Hashes.Count} hashes to cache at {_cachePath}"); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Cache] Failed to save hash cache: " + ex.Message)); } } [IteratorStateMachine(typeof(<MonitorForAutoSkip>d__115))] private IEnumerator MonitorForAutoSkip() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <MonitorForAutoSkip>d__115(0) { <>4__this = this }; } private void AutoSkipToNext() { List<AudioClip> loadedAudioClips = LoadedAudioClips; if (loadedAudioClips != null && loadedAudioClips.Count <= 0) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Not enough songs to auto-skip"); } return; } int num2; switch (CurrentLoopState) { case LoopState.One: num2 = CurrentSongIndex; break; case LoopState.All: num2 = GetNextSongIndex(CurrentSongIndex); break; default: { string item = IndexToSongHash[CurrentSongIndex]; int num = SongQueue.IndexOf(item); if (num == SongQueue.Count - 1) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Reached end of queue. Stopping."); } Netcode.Instance.SendStopPlayback(); return; } num2 = GetNextSongIndex(CurrentSongIndex); break; } } string songHash = IndexToSongHash[num2]; string name = ((Object)LoadedAudioClips[num2]).name; Netcode.Instance.SendNextSong(songHash, name); } public void StartAutoSkipMonitoring() { if (_autoSkipCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_autoSkipCoroutine); } _autoSkipCoroutine = ((MonoBehaviour)this).StartCoroutine(MonitorForAutoSkip()); } public void StopAutoSkipMonitoring() { if (_autoSkipCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_autoSkipCoroutine); _autoSkipCoroutine = null; } } public void HandleNextSong(string songHash, string songName) { if (CurrentPlaybackState == PlaybackState.Stopped) { return; } if (SongHashToIndex.TryGetValue(songHash, out var value)) { CurrentSongIndex = value; PlaySongAtIndex(value); } else { if (Plugin.DebugMode) { Plugin.Log.LogWarning((object)"[AudioMaster] Song not available for next song, playing white noise"); } PersistentAudioSource.clip = SoundEffects.WhiteNoise; PersistentAudioSource.Play(); sPEAKerUI.Instance.ShowTrackName(songName); } UpdatePlaybackState(PlaybackState.Playing, CurrentSongIndex); if (PhotonNetwork.IsMasterClient && !IsYouTubeMode) { StartAutoSkipMonitoring(); } } private void PlaySongAtIndex(int index) { if (index < 0 || index >= LoadedAudioClips.Count || (Object)(object)PersistentAudioSource == (Object)null) { if (Plugin.DebugMode) { Plugin.Log.LogError((object)$"[AudioMaster] PlaySongAtIndex called with invalid index: {index}. Aborting."); } return; } string text = IndexToSongHash[index]; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] PlaySongAtIndex: Playing index {index} with hash '{text}'."); } AudioMaster.OnSongChanged?.Invoke(text); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] OnSongChanged event has been invoked for hash '" + text + "'.")); } AudioClip val = LoadedAudioClips[index]; PersistentAudioSource.clip = val; PersistentAudioSource.Play(); CurrentMixtape = GetMixtapeForSongIndex(index); if (!string.IsNullOrEmpty(CurrentMixtape.Name)) { sPEAKerUI.Instance.ShowTrackName(CurrentMixtape.Name + " - " + ((Object)val).name); sPEAKerUI.Instance.ShowAuthorInfo(CurrentMixtape.Author); } else { sPEAKerUI.Instance.HideAuthorInfo(); sPEAKerUI.Instance.ShowTrackName(((Object)val).name); } sPEAKerUI.Instance.ShowDuration(val.length); } public Texture2D GetIconForSongHash(string songHash) { if (SongHashToIndex.TryGetValue(songHash, out var songIndex)) { MixtapeInfo mixtapeInfo = AvailableMixtapes.FirstOrDefault((MixtapeInfo m) => m.ContainsSongIndex(songIndex)); if (mixtapeInfo != null) { return mixtapeInfo.IconTexture; } } return null; } public static Texture2D GetCurrentMixtapeIcon() { if (LoadedAudioClips.Count == 0) { return null; } return GetMixtapeForSongIndex(CurrentSongIndex)?.IconTexture; } private AudioType GetAudioTypeFromExtension(string filePath) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) return (AudioType)(Path.GetExtension(filePath).ToLowerInvariant() switch { ".mp3" => 13, ".wav" => 20, ".ogg" => 14, _ => 0, }); } public static void PlayFX(AudioClip clip) { if (Plugin.SoundEffectsEnabled && !((Object)(object)EffectsAudioSource == (Object)null) && !((Object)(object)clip == (Object)null)) { EffectsAudioSource.clip = clip; EffectsAudioSource.Play(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Playing sound effect: " + ((Object)clip).name)); } } } public static void PlayLaserFX() { if (Plugin.SoundEffectsEnabled && !((Object)(object)EffectsAudioSource == (Object)null)) { List<AudioClip> list = new List<AudioClip>(3) { SoundEffects.Laser1, SoundEffects.Laser2, SoundEffects.Laser3 }; int index = Random.Range(0, 2); EffectsAudioSource.clip = list[index]; EffectsAudioSource.Play(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Playing laser sound effect: " + ((Object)EffectsAudioSource.clip).name)); } } } public void SetVolume(float newVolume) { float num = Mathf.Clamp(newVolume, 0f, 1f); if (num >= 1f && NetworkVolume < 1f) { PlayFX(SoundEffects.VolMax); } NetworkVolume = num; ApplyVolumeMultiplier(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[AudioMaster] Network volume set to " + NetworkVolume + " with local multiplier " + LocalVolumeMultiplier + " = final volume " + NetworkVolume * LocalVolumeMultiplier)); } } public void SetFollow(Transform? target) { _targetTransform = target; _targetCharacter = null; _isFollowing = (Object)(object)target != (Object)null; if (Plugin.DebugMode) { if (_isFollowing) { Plugin.Log.LogInfo((object)("[AudioMaster] Set to follow transform: " + ((Object)target).name)); } else { Plugin.Log.LogInfo((object)"[AudioMaster] Stopped following."); } } } public void SetFollow(Character? target) { _targetCharacter = target; LastHolder = target; _targetTransform = null; _isFollowing = (Object)(object)target != (Object)null; if (Plugin.DebugMode) { if (_isFollowing) { Plugin.Log.LogInfo((object)("[AudioMaster] Set to follow character: " + ((target != null) ? ((Object)target).name : null))); } else { Plugin.Log.LogInfo((object)"[AudioMaster] Stopped following."); } } } public static void SetYouTubeMode(bool enabled) { IsYouTubeMode = enabled; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] YouTube mode set to {enabled}"); } } public static void PauseLocalAudio() { if ((Object)(object)PersistentAudioSource != (Object)null && PersistentAudioSource.isPlaying) { PersistentAudioSource.Pause(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Local audio paused for YouTube mode"); } } } public static void ResumeLocalAudio() { if ((Object)(object)PersistentAudioSource != (Object)null && (Object)(object)PersistentAudioSource.clip != (Object)null) { PersistentAudioSource.UnPause(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Local audio resumed from YouTube mode"); } } } public static void ToggleLoop() { CurrentLoopState = (LoopState)((int)(CurrentLoopState + 1) % 3); switch (CurrentLoopState) { case LoopState.None: sPEAKerUI.Instance.ShowTrackName("Loop is OFF"); break; case LoopState.All: sPEAKerUI.Instance.ShowTrackName("Looping ALL songs"); break; case LoopState.One: sPEAKerUI.Instance.ShowTrackName("Looping CURRENT song"); break; } if (PhotonNetwork.IsMasterClient) { Netcode.Instance.SendLoopStateChange((int)CurrentLoopState); } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Loop state toggled: {CurrentLoopState}"); } } public void HandleNetworkLoopStateChange(int loopState) { CurrentLoopState = (LoopState)loopState; if (!PhotonNetwork.IsMasterClient) { switch (CurrentLoopState) { case LoopState.None: sPEAKerUI.Instance.ShowTrackName("Loop is OFF"); break; case LoopState.All: sPEAKerUI.Instance.ShowTrackName("Looping ALL songs"); break; case LoopState.One: sPEAKerUI.Instance.ShowTrackName("Looping CURRENT song"); break; } } if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Network updated loop state: {CurrentLoopState}"); } } public void Master_PlaySongByHash(string songHash) { if (PhotonNetwork.IsMasterClient && SongHashToIndex.TryGetValue(songHash, out var value) && value >= 0 && value < LoadedAudioClips.Count) { string name = ((Object)LoadedAudioClips[value]).name; Netcode.Instance.SendNextSong(songHash, name); } } public void Master_UpdateQueue(List<string> newQueueOrder, bool broadcast = true) { if (PhotonNetwork.IsMasterClient) { SongQueue = new List<string>(newQueueOrder); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Master updated queue. New count: {SongQueue.Count}"); } if (broadcast) { Netcode.Instance.BroadcastSongQueue(SongQueue.ToArray()); } } } public void Master_ShuffleQueue(bool broadcast = true) { if (PhotonNetwork.IsMasterClient) { Random random = new Random(); SongQueue = SongQueue.OrderBy((string x) => random.Next()).ToList(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Master shuffled the queue."); } sPEAKerUI.Instance.ShowTrackName("Queue has been shuffled!"); if (broadcast) { Netcode.Instance.BroadcastSongQueue(SongQueue.ToArray()); } } } public void HandleNetworkQueueSync(string[] newQueue) { SongQueue = newQueue.ToList(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Synced queue from network. {SongQueue.Count} tracks."); } AudioMaster.OnQueueSynced?.Invoke(); } public static int GetNextSongIndex(int currentIndex) { if (SongQueue.Count < 2) { return currentIndex; } string valueOrDefault = IndexToSongHash.GetValueOrDefault(currentIndex, ""); if (string.IsNullOrEmpty(valueOrDefault)) { return (currentIndex + 1) % LoadedAudioClips.Count; } int num = SongQueue.IndexOf(valueOrDefault); if (num == -1) { return (currentIndex + 1) % LoadedAudioClips.Count; } int index = (num + 1) % SongQueue.Count; string key = SongQueue[index]; return SongHashToIndex.GetValueOrDefault(key, 0); } public static int GetPreviousSongIndex(int currentIndex) { if (SongQueue.Count < 2) { return currentIndex; } string valueOrDefault = IndexToSongHash.GetValueOrDefault(currentIndex, ""); if (string.IsNullOrEmpty(valueOrDefault)) { return (currentIndex - 1 + LoadedAudioClips.Count) % LoadedAudioClips.Count; } int num = SongQueue.IndexOf(valueOrDefault); if (num == -1) { return (currentIndex - 1 + LoadedAudioClips.Count) % LoadedAudioClips.Count; } int index = (num - 1 + SongQueue.Count) % SongQueue.Count; string key = SongQueue[index]; return SongHashToIndex.GetValueOrDefault(key, 0); } public static void SetLocalVolumeMultiplier(float volume) { LocalVolumeMultiplier = Mathf.Clamp01(volume); ApplyVolumeMultiplier(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[AudioMaster] Local volume multiplier set to {LocalVolumeMultiplier}"); } } private static void ApplyVolumeMultiplier() { if ((Object)(object)PersistentAudioSource != (Object)null) { PersistentAudioSource.volume = NetworkVolume * LocalVolumeMultiplier; } if ((Object)(object)YouTubeAudioSource != (Object)null) { YouTubeAudioSource.volume = NetworkVolume * LocalVolumeMultiplier; } } private void LateUpdate() { //IL_0013: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) if (_isFollowing) { Vector3 val = Vector3.zero; if ((Object)(object)_targetTransform != (Object)null) { val = _targetTransform.position; } else if ((Object)(object)_targetCharacter != (Object)null) { val = _targetCharacter.Center; } if ((Object)(object)PersistentAudioSource != (Object)null && ((Component)PersistentAudioSource).transform.position != val) { ((Component)PersistentAudioSource).transform.position = val; } if ((Object)(object)EffectsAudioSource != (Object)null && ((Component)EffectsAudioSource).transform.position != val) { ((Component)EffectsAudioSource).transform.position = val; } if ((Object)(object)YouTubeAudioSource != (Object)null && ((Component)YouTubeAudioSource).transform.position != val) { ((Component)YouTubeAudioSource).transform.position = val; } } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (((Scene)(ref scene)).name == "Airport") { StopAllPlayback(); sPEAKerUI.Instance.HideDuration(); sPEAKerUI.Instance.HideAuthorInfo(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] Airport scene detected - stopped audio playback"); } } } private void OnDestroy() { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[AudioMaster] OnDestroy called."); } SceneManager.sceneLoaded -= OnSceneLoaded; if ((Object)(object)_instance == (Object)(object)this) { IsInitialized = false; IsLoading = false; } } private void OnGUI() { if (!((Object)(object)GUIManager.instance != (Object)null)) { return; } if (GUIManager.instance.pauseMenu.activeSelf) { if (CurrentPlaybackState != 0) { sPEAKerUI.Instance.HideDuration(); sPEAKerUI.Instance.HideAuthorInfo(); } } else if (CurrentPlaybackState != 0 && (Object)(object)ActiveSource != (Object)null && (Object)(object)ActiveSource.clip != (Object)null) { sPEAKerUI.Instance.ShowDuration(ActiveSource.clip.length); MixtapeInfo mixtapeForSongIndex = GetMixtapeForSongIndex(CurrentSongIndex); if (!string.IsNullOrEmpty(mixtapeForSongIndex.Name)) { sPEAKerUI.Instance.ShowAuthorInfo(mixtapeForSongIndex.Author); } else { sPEAKerUI.Instance.HideAuthorInfo(); } } } public void StopAllPlayback() { ResetPlaybackState(); if ((Object)(object)PersistentAudioSource != (Object)null && PersistentAudioSource.isPlaying) { PersistentAudioSource.Stop(); } if ((Object)(object)YouTubeAudioSource != (Object)null && YouTubeAudioSource.isPlaying) { YouTubeAudioSource.Stop(); } } } public class FileHashCache { public const int CurrentVersion = 1; public int Version { get; set; } = 1; public int AlgorithmVersion { get; set; } public Dictionary<string, CacheEntry> Hashes { get; set; } = new Dictionary<string, CacheEntry>(); } public class CacheEntry { [JsonProperty("hash")] public string Hash { get; set; } [JsonProperty("lastModified")] public DateTime LastModified { get; set; } [JsonProperty("size")] public long Size { get; set; } } [Serializable] public class MixtapeMetadata { public string name; public string author; } public class MixtapeInfo { public string Name; public string Author; public string TotalLength; public int FirstSongIndex; public int LastSongIndex; public Texture2D IconTexture; public int SongCount; public string Path; public bool ContainsSongIndex(int index) { return index >= FirstSongIndex && index <= LastSongIndex; } } public enum PlaybackState { Stopped, Playing, Paused } public enum LoopState { None, All, One } public static class SoundEffects { public static AudioClip Paired { get; private set; } public static AudioClip Pairing { get; private set; } public static AudioClip Party { get; private set; } public static AudioClip TurnOn { get; private set; } public static AudioClip VolMax { get; private set; } public static AudioClip WhiteNoise { get; private set; } public static AudioClip Laser1 { get; private set; } public static AudioClip Laser2 { get; private set; } public static AudioClip Laser3 { get; private set; } public static void SetClip(string filename, AudioClip clip) { switch (filename.ToLower().Replace(" ", "")) { case "connected": Paired = clip; break; case "connecting": Pairing = clip; break; case "party": Party = clip; break; case "turnon": TurnOn = clip; break; case "volmax": VolMax = clip; break; case "whitenoise": WhiteNoise = clip; break; case "laser1": Laser1 = clip; break; case "laser2": Laser2 = clip; break; case "laser3": Laser3 = clip; break; } } } public static class TaskExtensions { public static TaskAwaiter GetAwaiter(this AsyncOperation asyncOp) { TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); asyncOp.completed += delegate { tcs.TrySetResult(null); }; return ((Task)tcs.Task).GetAwaiter(); } } public class Controller : MonoBehaviour { [CompilerGenerated] private sealed class <DelayedUIUpdate>d__51 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Controller <>4__this; private PlaybackState <currentState>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedUIUpdate>d__51(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; if (AudioMaster.IsYouTubeMode) { <currentState>5__1 = YouTubePlayer.Instance.CurrentPlaybackState; } else { <currentState>5__1 = AudioMaster.CurrentPlaybackState; } <>4__this.UpdateMainPrompt(<currentState>5__1); <>4__this._currentSongIndex = AudioMaster.CurrentSongIndex; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)$"[Controller] Delayed UI update complete. Final state: {<>4__this._playbackState}"); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <InitializeWhenReady>d__17 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Controller <>4__this; private int <waitCount>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <InitializeWhenReady>d__17(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[Controller] Waiting for AudioMaster to initialize..."); } <waitCount>5__1 = 0; break; case 1: <>1__state = -1; <waitCount>5__1++; break; } if (!AudioMaster.IsInitialized && <waitCount>5__1 < 50) { <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 1; return true; } if (!AudioMaster.IsInitialized) { Plugin.Log.LogError((object)"[Controller] AudioMaster failed to initialize after 5 seconds!"); return false; } Item item = <>4__this.item; item.OnScrolled = (Action<float>)Delegate.Combine(item.OnScrolled, new Action<float>(<>4__this.HandleScroll)); <>4__this._currentAudioClips = AudioMaster.LoadedAudioClips.ToArray(); if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)("[Controller] Loaded " + <>4__this._currentAudioClips.Length + " audio clips")); } AudioMaster.Instance.SetFollow(((Component)<>4__this).transform); <>4__this.SyncWithPersistentAudio(); <>4__this.droneBehaviour.CreateSearchRadiusIndicator(); <>4__this._isInitialized = true; AudioMaster.Instance.sPEAKerExists = true; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <TurnOnSequence>d__25 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Controller <>4__this; private float <waitTime>5__1; private float <syncTimeout>5__2; private float <syncWaitTime>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <TurnOnSequence>d__25(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>4__this.item.UIData.mainInteractPrompt = "..."; if (!PhotonNetwork.IsMasterClient) { if (Plugin.DebugMode) { Plugin.Log.LogInfo((object)"[Controller] Non-host turning on sPEAKer. Requesting sync first to prevent state contamination."); } <>4__this._waitingForSyncBeforeTurnOn = true; Netcode.Instance.RequestResyncFromHost(); <syncTimeout>5__2 = 5f; <syncWaitTime>5__3 = 0f; goto IL_00ce;