Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CustomElevatorAudio v1.1.0
Custom Elevator Audio.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Custom Elevator Audio")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Custom Elevator Audio")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("bb9da844-988b-4075-bc3b-20396c69ea66")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace CustomElevatorAudio; public enum PlaybackMode { Random, PreventSongRepeat, Shuffle, Reshuffle } [BepInPlugin("com.donobus.CustomElevatorAudio", "Custom Elevator Audio", "1.1.0")] public class Plugin : BaseUnityPlugin { private Harmony harmony; public static ConfigEntry<string> MusicFolderPath; public static List<AudioClip> CustomMusicClips = new List<AudioClip>(); public static Dictionary<string, AudioClip> CustomMusicClipsByFileName = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase); public static ConfigEntry<float> MusicVolume; public static ConfigEntry<bool> EnableDoppler; public static ConfigEntry<PlaybackMode> AudioPlaybackMode; public static ConfigEntry<bool> EnableHostSync; public static ConfigEntry<KeyboardShortcut> ReloadKeybind; private bool isReloading = false; private void Awake() { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Expected O, but got Unknown ((BaseUnityPlugin)this).Logger.LogInfo((object)"=== CustomElevatorAudio Loaded ==="); string text = Path.Combine(Paths.PluginPath, "CustomElevatorAudio", "Music"); MusicFolderPath = ((BaseUnityPlugin)this).Config.Bind<string>("General", "MusicFolderPath", text, "The absolute path to the folder containing your custom .mp3, .wav, or .ogg files."); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } ReloadKeybind = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ReloadKeybind", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Press this key in-game to instantly reload the music folder without restarting the game."); MusicVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio Settings", "MusicVolume", 1f, new ConfigDescription("The volume of the custom elevator music (0.0 to 1.0).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); EnableDoppler = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio Settings", "EnableDoppler", true, "Set to false to disable the pitch-bending doppler effect as you move relative to the Elevator."); AudioPlaybackMode = ((BaseUnityPlugin)this).Config.Bind<PlaybackMode>("Audio Settings", "PlaybackMode", PlaybackMode.PreventSongRepeat, "Choose how songs are selected: Random (pure randomness), PreventSongRepeat (no back-to-back repeats), Shuffle (loops one fixed randomized list), or Reshuffle (creates a completely new random order every loop, ensuring no back-to-back repeats across boundaries)."); EnableHostSync = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio Settings", "EnableHostSync", false, "If true AND you are the host, your chosen song is broadcast to all clients. If true on a client, that client will try to find a local file with the same name as the host's chosen song and play it; if no matching file exists locally, it falls back to that client's own PlaybackMode setting. Has no effect unless the HOST also has this enabled."); ((MonoBehaviour)this).StartCoroutine(LoadAllCustomMusic()); harmony = new Harmony("com.donobus.CustomElevatorAudio"); harmony.PatchAll(); } private void Update() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) MineshaftElevatorPatch.EnsureMessageHandlerRegistered(); KeyboardShortcut value = ReloadKeybind.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !isReloading) { Debug.Log((object)"[CustomElevatorAudio] Reload keybind pressed! Reloading audio files..."); ((MonoBehaviour)this).StartCoroutine(ReloadMusicRoutine()); } } private IEnumerator ReloadMusicRoutine() { isReloading = true; MineshaftElevatorPatch.ResetStateForReload(); foreach (AudioClip clip in CustomMusicClips) { if ((Object)(object)clip != (Object)null) { Object.Destroy((Object)(object)clip); } } CustomMusicClips.Clear(); CustomMusicClipsByFileName.Clear(); yield return ((MonoBehaviour)this).StartCoroutine(LoadAllCustomMusic()); isReloading = false; Debug.Log((object)"[CustomElevatorAudio] Reload complete!"); } private IEnumerator LoadAllCustomMusic() { string folderPath = MusicFolderPath.Value; if (!Directory.Exists(folderPath)) { Debug.LogWarning((object)"[CustomElevatorAudio] Folder path is invalid or does not exist."); yield break; } string[] audioFiles = GetValidAudioFiles(folderPath); if (audioFiles.Length == 0) { Debug.LogWarning((object)"[CustomElevatorAudio] No .mp3, .wav, or .ogg files found in folder."); yield break; } string[] array = audioFiles; foreach (string file in array) { string uri = new Uri(file).AbsoluteUri; string fileNameNoExt = Path.GetFileNameWithoutExtension(file); AudioType audioType = GetAudioType(file); if ((int)audioType == 0) { Debug.LogWarning((object)("[CustomElevatorAudio] Unsupported audio format: " + Path.GetFileName(file))); continue; } UnityWebRequest uwr = UnityWebRequestMultimedia.GetAudioClip(uri, audioType); try { yield return uwr.SendWebRequest(); if ((int)uwr.result == 1) { AudioClip clip = DownloadHandlerAudioClip.GetContent(uwr); ((Object)clip).name = "CustomElevatorSong_" + fileNameNoExt; CustomMusicClips.Add(clip); if (!CustomMusicClipsByFileName.ContainsKey(fileNameNoExt)) { CustomMusicClipsByFileName.Add(fileNameNoExt, clip); } else { Debug.LogWarning((object)("[CustomElevatorAudio] Duplicate file name '" + fileNameNoExt + "' (case-insensitive match). Host Sync matching will only ever use the first one loaded.")); } Debug.Log((object)("[CustomElevatorAudio] Pre-loaded successfully: " + Path.GetFileName(file))); } else { Debug.LogError((object)("[CustomElevatorAudio] Failed to load " + Path.GetFileName(file) + ": " + uwr.error)); } } finally { ((IDisposable)uwr)?.Dispose(); } } Debug.Log((object)$"[CustomElevatorAudio] Total custom songs loaded into rotation: {CustomMusicClips.Count}"); if (CustomMusicClips.Count > 0) { MineshaftElevatorPatch.GenerateShuffleDeck(checkBoundaryRepeat: false); } } private AudioType GetAudioType(string filePath) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_0048: 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, }); } private string[] GetValidAudioFiles(string path) { try { string[] supportedExtensions = new string[3] { ".mp3", ".wav", ".ogg" }; return (from file in Directory.GetFiles(path) where supportedExtensions.Contains<string>(Path.GetExtension(file), StringComparer.OrdinalIgnoreCase) select file).ToArray(); } catch (Exception arg) { Debug.LogError((object)$"[CustomElevatorAudio] Failed to scan music folder: {arg}"); return Array.Empty<string>(); } } } [HarmonyPatch(typeof(MineshaftElevatorController))] public class MineshaftElevatorPatch { private static AudioClip activeSong; private static AudioClip lastPlayedSong; private static bool wasPlayingMusic; private static Random rng; private static string hostSyncedSongName; private static bool receivedHostSync; private static bool hostAlreadySelected; private static bool hostHasBroadcastedSync; private static bool hasSelectedSongThisTrip; private static List<int> shuffleDeck; private static int shufflePointer; public const string ClipNamePrefix = "CustomElevatorSong_"; private static FieldRef<MineshaftElevatorController, AudioSource> JingleField; private static FieldRef<MineshaftElevatorController, bool> PlayMusicField; private static bool fieldAccessReady; private const string SyncMessageName = "CustomElevatorAudio_ElevatorSongSync"; private static NetworkManager registeredOnManager; static MineshaftElevatorPatch() { activeSong = null; lastPlayedSong = null; wasPlayingMusic = false; rng = new Random(); hostSyncedSongName = null; receivedHostSync = false; hostAlreadySelected = false; hostHasBroadcastedSync = false; hasSelectedSongThisTrip = false; shuffleDeck = new List<int>(); shufflePointer = 0; fieldAccessReady = false; registeredOnManager = null; try { JingleField = AccessTools.FieldRefAccess<MineshaftElevatorController, AudioSource>("elevatorJingleMusic"); PlayMusicField = AccessTools.FieldRefAccess<MineshaftElevatorController, bool>("playMusic"); fieldAccessReady = true; } catch (Exception arg) { Debug.LogError((object)$"[CustomElevatorAudio] Failed to bind to MineshaftElevatorController's private fields - elevator music support disabled. {arg}"); fieldAccessReady = false; } } public static void ResetStateForReload() { activeSong = null; lastPlayedSong = null; hostAlreadySelected = false; receivedHostSync = false; hostSyncedSongName = null; hostHasBroadcastedSync = false; wasPlayingMusic = false; hasSelectedSongThisTrip = false; shuffleDeck.Clear(); shufflePointer = 0; if (fieldAccessReady) { MineshaftElevatorController[] array = Object.FindObjectsOfType<MineshaftElevatorController>(); foreach (MineshaftElevatorController val in array) { AudioSource val2 = JingleField.Invoke(val); if ((Object)(object)val2 != (Object)null) { val2.Stop(); val2.clip = null; } } } Debug.Log((object)"[CustomElevatorAudio] Elevator patch variables reset for incoming reload."); } [HarmonyPatch("Update")] [HarmonyPostfix] private static void UpdatePostfix(MineshaftElevatorController __instance) { if (!fieldAccessReady || Plugin.CustomMusicClips.Count == 0) { return; } bool flag = PlayMusicField.Invoke(__instance); if ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.inShipPhase) { return; } if (flag && !wasPlayingMusic) { Debug.Log((object)"[CustomElevatorAudio] Elevator ride started."); activeSong = null; receivedHostSync = false; hostSyncedSongName = null; hostAlreadySelected = false; hostHasBroadcastedSync = false; hasSelectedSongThisTrip = false; if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost) { ChooseSongLocally(); } } if (flag && (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost && !hostHasBroadcastedSync && NetworkManager.Singleton.CustomMessagingManager != null) { if (Plugin.EnableHostSync.Value && (Object)(object)activeSong != (Object)null) { SendSongSyncToClients(GetFileNameForClip(activeSong)); } else if (!Plugin.EnableHostSync.Value) { SendSongSyncToClients("<HOST_SYNC_DISABLED>"); } hostHasBroadcastedSync = true; } if (!flag && wasPlayingMusic) { Debug.Log((object)"[CustomElevatorAudio] Elevator ride ended."); activeSong = null; receivedHostSync = false; hostSyncedSongName = null; hostAlreadySelected = false; hostHasBroadcastedSync = false; hasSelectedSongThisTrip = false; } wasPlayingMusic = flag; if (flag) { EnforceElevatorMusic(__instance); return; } AudioSource val = JingleField.Invoke(__instance); if ((Object)(object)val != (Object)null && (Object)(object)val.clip != (Object)null && ((Object)val.clip).name.ToLower().StartsWith("CustomElevatorSong_".ToLower())) { val.volume = 0f; if (val.isPlaying) { val.Stop(); } } } private static void EnforceElevatorMusic(MineshaftElevatorController elevator) { if (Plugin.CustomMusicClips.Count == 0) { return; } if (!Plugin.EnableHostSync.Value && receivedHostSync) { Debug.Log((object)"[CustomElevatorAudio] EnableHostSync was disabled. Clearing sync state to resume local playback."); receivedHostSync = false; hostSyncedSongName = null; activeSong = null; } if ((Object)(object)activeSong == (Object)null) { if (Plugin.EnableHostSync.Value && (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsClient && !NetworkManager.Singleton.IsHost) { if (receivedHostSync) { Debug.Log((object)"[CustomElevatorAudio] Client has host sync data but no active clip. Reapplying."); ApplyHostSyncedSong(hostSyncedSongName); } else if (!NetworkManager.Singleton.IsConnectedClient) { Debug.Log((object)"[CustomElevatorAudio] Client not fully connected, falling back to local song selection."); ChooseSongLocally(); } else { Debug.Log((object)"[CustomElevatorAudio] Waiting for host song selection..."); } } else { ChooseSongLocally(); } } ApplyActiveSongToJingle(elevator); } private static void ApplyActiveSongToJingle(MineshaftElevatorController elevator) { if (!fieldAccessReady || (Object)(object)activeSong == (Object)null) { return; } AudioSource val = JingleField.Invoke(elevator); if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && ((Behaviour)val).enabled) { if ((Object)(object)val.clip != (Object)(object)activeSong) { val.clip = activeSong; val.time = 0f; } val.loop = true; val.volume = Plugin.MusicVolume.Value; val.dopplerLevel = (Plugin.EnableDoppler.Value ? 1f : 0f); if (!val.isPlaying) { val.Play(); } } } private static int PickIndexExcluding(int excludeIndex, int count) { if (count <= 1 || excludeIndex < 0 || excludeIndex >= count) { return rng.Next(count); } int num = rng.Next(count - 1); if (num >= excludeIndex) { num++; } return num; } public static void GenerateShuffleDeck(bool checkBoundaryRepeat) { if (Plugin.CustomMusicClips.Count == 0) { return; } shuffleDeck.Clear(); for (int i = 0; i < Plugin.CustomMusicClips.Count; i++) { shuffleDeck.Add(i); } int num = shuffleDeck.Count; while (num > 1) { num--; int index = rng.Next(num + 1); int value = shuffleDeck[index]; shuffleDeck[index] = shuffleDeck[num]; shuffleDeck[num] = value; } if (checkBoundaryRepeat && shuffleDeck.Count >= 2 && (Object)(object)lastPlayedSong != (Object)null) { int num2 = Plugin.CustomMusicClips.IndexOf(lastPlayedSong); if (num2 >= 0 && shuffleDeck[0] == num2) { int index2 = rng.Next(1, shuffleDeck.Count); int value2 = shuffleDeck[0]; shuffleDeck[0] = shuffleDeck[index2]; shuffleDeck[index2] = value2; } } shufflePointer = 0; string text = "\n[CustomElevatorAudio] --- NEW ELEVATOR SHUFFLE DECK GENERATED ---"; for (int j = 0; j < shuffleDeck.Count; j++) { text += $"\n {j + 1}. {((Object)Plugin.CustomMusicClips[shuffleDeck[j]]).name}"; } text += "\n---------------------------------------------------------"; Debug.Log((object)text); } private static void ChooseSongLocally() { if (Plugin.CustomMusicClips.Count == 0 || hasSelectedSongThisTrip || (hostAlreadySelected && (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost)) { return; } PlaybackMode value = Plugin.AudioPlaybackMode.Value; int num; if (value == PlaybackMode.Shuffle || value == PlaybackMode.Reshuffle) { if (shuffleDeck.Count != Plugin.CustomMusicClips.Count) { GenerateShuffleDeck(checkBoundaryRepeat: false); } if (shufflePointer >= shuffleDeck.Count) { if (value == PlaybackMode.Reshuffle) { Debug.Log((object)"[CustomElevatorAudio] Elevator deck exhausted. Reshuffling with guaranteed no-repeat boundary."); GenerateShuffleDeck(checkBoundaryRepeat: true); } else { Debug.Log((object)"[CustomElevatorAudio] Elevator deck exhausted. Standard Shuffle looping back to start of fixed deck."); shufflePointer = 0; } } num = shuffleDeck[shufflePointer]; activeSong = Plugin.CustomMusicClips[num]; shufflePointer++; Debug.Log((object)$"[CustomElevatorAudio] Elevator {value} Mode: Playing track index {num} ({shufflePointer}/{shuffleDeck.Count})"); } else if (value == PlaybackMode.PreventSongRepeat && Plugin.CustomMusicClips.Count >= 2 && (Object)(object)lastPlayedSong != (Object)null) { int excludeIndex = Plugin.CustomMusicClips.IndexOf(lastPlayedSong); num = PickIndexExcluding(excludeIndex, Plugin.CustomMusicClips.Count); activeSong = Plugin.CustomMusicClips[num]; Debug.Log((object)("[CustomElevatorAudio] Elevator anti-repeat active. Excluded: " + ((Object)lastPlayedSong).name)); } else { num = rng.Next(Plugin.CustomMusicClips.Count); activeSong = Plugin.CustomMusicClips[num]; } lastPlayedSong = activeSong; hostAlreadySelected = true; hasSelectedSongThisTrip = true; Debug.Log((object)$"[CustomElevatorAudio] {GetRoleTag()} ELEVATOR CHOSEN SONG ({num + 1} of {Plugin.CustomMusicClips.Count}): {((Object)activeSong).name}"); } private static string GetRoleTag() { if ((Object)(object)NetworkManager.Singleton == (Object)null) { return "[NO-NET]"; } if (NetworkManager.Singleton.IsHost) { return "[HOST]"; } if (NetworkManager.Singleton.IsClient) { return "[CLIENT]"; } return "[NO-NET]"; } private static string GetFileNameForClip(AudioClip clip) { if ((Object)(object)clip == (Object)null) { return null; } if (((Object)clip).name.StartsWith("CustomElevatorSong_", StringComparison.Ordinal)) { return ((Object)clip).name.Substring("CustomElevatorSong_".Length); } return ((Object)clip).name; } internal static void EnsureMessageHandlerRegistered() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.CustomMessagingManager != null && !((Object)(object)registeredOnManager == (Object)(object)NetworkManager.Singleton)) { NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("CustomElevatorAudio_ElevatorSongSync", new HandleNamedMessageDelegate(OnSongSyncMessageReceived)); registeredOnManager = NetworkManager.Singleton; Debug.Log((object)("[CustomElevatorAudio] " + GetRoleTag() + " Elevator Host Sync message handler registered.")); } } private unsafe static void SendSongSyncToClients(string songFileName) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(songFileName) && !((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsHost && NetworkManager.Singleton.CustomMessagingManager != null) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(FastBufferWriter.GetWriteSize(songFileName, false), (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe(songFileName, false); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessageToAll("CustomElevatorAudio_ElevatorSongSync", val, (NetworkDelivery)2); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Debug.Log((object)("[CustomElevatorAudio] " + GetRoleTag() + " Elevator Host Sync: broadcast song selection '" + songFileName + "' to all clients.")); } } private static void OnSongSyncMessageReceived(ulong senderClientId, FastBufferReader reader) { string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); Debug.Log((object)("[CustomElevatorAudio] " + GetRoleTag() + " Elevator Host Sync: received broadcast for '" + text + "'.")); if ((Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.IsHost) { return; } if (!Plugin.EnableHostSync.Value) { Debug.Log((object)("[CustomElevatorAudio] " + GetRoleTag() + " Elevator Host Sync: EnableHostSync is off locally, ignoring broadcast.")); } else if (text == "<HOST_SYNC_DISABLED>") { Debug.Log((object)("[CustomElevatorAudio] " + GetRoleTag() + " Elevator: host explicitly broadcast that their sync is disabled. Proceeding locally.")); receivedHostSync = false; hostSyncedSongName = null; if ((Object)(object)activeSong == (Object)null) { ChooseSongLocally(); } } else { ApplyHostSyncedSong(text); } } private static void ApplyHostSyncedSong(string hostFileName) { hostSyncedSongName = hostFileName; receivedHostSync = true; if (!string.IsNullOrEmpty(hostFileName) && Plugin.CustomMusicClipsByFileName.TryGetValue(hostFileName, out var value)) { activeSong = value; lastPlayedSong = value; Debug.Log((object)("[CustomElevatorAudio] " + GetRoleTag() + " Elevator: using host synced song '" + hostFileName + "'.")); } else { Debug.Log((object)("[CustomElevatorAudio] " + GetRoleTag() + " Elevator: missing '" + hostFileName + "', using local selection.")); receivedHostSync = false; activeSong = null; ChooseSongLocally(); } } }