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 CustomDropshipAudio v1.1.3
Custom Dropship Audio.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; 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 Dropship Audio")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Custom Dropship Audio")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("39e6477f-12f9-439e-b179-914cf8dd5841")] [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 CustomDropshipAudio; public enum PlaybackMode { Random, PreventSongRepeat, Shuffle, Reshuffle } [BepInPlugin("com.donobus.CustomDropshipAudio", "Custom Dropship Audio", "1.0.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.Ordinal); 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown ((BaseUnityPlugin)this).Logger.LogInfo((object)"=== CustomDropshipAudio Loaded ==="); MusicFolderPath = ((BaseUnityPlugin)this).Config.Bind<string>("General", "MusicFolderPath", "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Lethal Company\\Custom Dropship Audio", "The absolute path to the folder containing your custom .mp3 files."); ReloadKeybind = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ReloadKeybind", new KeyboardShortcut((KeyCode)286, 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, "The volume of the custom dropship music (0.0 to 1.0)."); 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 Dropship."); 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.CustomDropshipAudio"); 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) ItemDropshipPatch.EnsureMessageHandlerRegistered(); KeyboardShortcut value = ReloadKeybind.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !isReloading) { Debug.Log((object)"[CustomDropshipAudio] Reload keybind pressed! Reloading audio files..."); ((MonoBehaviour)this).StartCoroutine(ReloadMusicRoutine()); } } private IEnumerator ReloadMusicRoutine() { isReloading = true; CustomMusicClips.Clear(); CustomMusicClipsByFileName.Clear(); ItemDropshipPatch.ResetStateForReload(); yield return ((MonoBehaviour)this).StartCoroutine(LoadAllCustomMusic()); isReloading = false; Debug.Log((object)"[CustomDropshipAudio] Reload complete!"); } private IEnumerator LoadAllCustomMusic() { string folderPath = MusicFolderPath.Value; if (!Directory.Exists(folderPath)) { Debug.LogWarning((object)"[CustomDropshipAudio] Folder path is invalid or does not exist."); yield break; } string[] mp3Files = GetValidMp3Files(folderPath); if (mp3Files.Length == 0) { Debug.LogWarning((object)"[CustomDropshipAudio] No .mp3 files found in folder."); yield break; } string[] array = mp3Files; foreach (string file in array) { string uri = "file://" + file.Replace("\\", "/"); string fileNameNoExt = Path.GetFileNameWithoutExtension(file); UnityWebRequest uwr = UnityWebRequestMultimedia.GetAudioClip(uri, (AudioType)13); try { yield return uwr.SendWebRequest(); if ((int)uwr.result == 1) { AudioClip clip = DownloadHandlerAudioClip.GetContent(uwr); ((Object)clip).name = "CustomDropshipSong_" + fileNameNoExt; CustomMusicClips.Add(clip); if (!CustomMusicClipsByFileName.ContainsKey(fileNameNoExt)) { CustomMusicClipsByFileName.Add(fileNameNoExt, clip); } else { Debug.LogWarning((object)("[CustomDropshipAudio] Duplicate file name '" + fileNameNoExt + "' (case-sensitive match). Host Sync matching will only ever use the first one loaded.")); } Debug.Log((object)("[CustomDropshipAudio] Pre-loaded successfully: " + Path.GetFileName(file))); } else { Debug.LogError((object)("[CustomDropshipAudio] Failed to load " + Path.GetFileName(file) + ": " + uwr.error)); } } finally { ((IDisposable)uwr)?.Dispose(); } } Debug.Log((object)$"[CustomDropshipAudio] Total custom songs loaded into rotation: {CustomMusicClips.Count}"); if (CustomMusicClips.Count > 0) { ItemDropshipPatch.GenerateShuffleDeck(checkBoundaryRepeat: false); } } private string[] GetValidMp3Files(string path) { try { return Directory.GetFiles(path, "*.mp3"); } catch { return new string[0]; } } } [HarmonyPatch(typeof(ItemDropship))] public class ItemDropshipPatch { private static AudioClip activeSong = null; private static AudioClip lastPlayedSong = null; private static List<AudioSource> musicSources = new List<AudioSource>(); private static bool hasCachedSources = false; private static bool wasLanded = false; private static Random rng = new Random(); private static string hostSyncedSongName = null; private static bool receivedHostSync = false; private static bool hostAlreadySelected = false; private static bool hostHasBroadcastedSync = false; private static List<int> shuffleDeck = new List<int>(); private static int shufflePointer = 0; public const string ClipNamePrefix = "CustomDropshipSong_"; private const string SyncMessageName = "CustomDropshipAudio_SongSync"; private static NetworkManager registeredOnManager = null; public static void ResetStateForReload() { activeSong = null; lastPlayedSong = null; hostAlreadySelected = false; receivedHostSync = false; hostSyncedSongName = null; hostHasBroadcastedSync = false; foreach (AudioSource musicSource in musicSources) { if ((Object)(object)musicSource != (Object)null) { musicSource.Stop(); musicSource.clip = null; } } musicSources.Clear(); hasCachedSources = false; shuffleDeck.Clear(); shufflePointer = 0; Debug.Log((object)"[CustomDropshipAudio] Patch variables reset for incoming reload."); } [HarmonyPatch("Start")] [HarmonyPrefix] private static void StartPrefix() { musicSources.Clear(); hasCachedSources = false; wasLanded = false; hostAlreadySelected = false; hostHasBroadcastedSync = false; EnsureMessageHandlerRegistered(); if (Plugin.CustomMusicClips.Count > 0 && (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost) { ChooseSongLocally(); } } [HarmonyPatch("ShipLandedAnimationEvent")] [HarmonyPostfix] private static void ShipLandedPostfix(ItemDropship __instance) { EnforceCustomMusic(); } [HarmonyPatch("Update")] [HarmonyPostfix] private static void UpdatePostfix(ItemDropship __instance) { if (Plugin.CustomMusicClips.Count == 0) { return; } if ((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 (!hasCachedSources) { AudioSource[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<AudioSource>(true); AudioSource[] array = componentsInChildren; foreach (AudioSource val in array) { if ((Object)(object)val == (Object)null) { continue; } string text = (((Object)(object)val.clip != (Object)null) ? ((Object)val.clip).name.ToLower() : ""); if (text.Contains("icecream") || text.Contains("dropshipmusic") || text.StartsWith("CustomDropshipSong_".ToLower())) { if (val.isPlaying && text.Contains("icecream")) { val.Stop(); } if ((Object)(object)val.clip != (Object)null && (text.Contains("icecream") || text.Contains("dropshipmusic"))) { val.clip = null; } if (!musicSources.Contains(val)) { musicSources.Add(val); } } } if (musicSources.Count > 0) { hasCachedSources = true; if (__instance.shipLanded) { EnforceCustomMusic(); } } } if (musicSources.Count == 0) { return; } if (wasLanded && !__instance.shipLanded) { foreach (AudioSource musicSource in musicSources) { if ((Object)(object)musicSource != (Object)null && (Object)(object)musicSource.clip == (Object)(object)activeSong && (Object)(object)activeSong != (Object)null) { musicSource.volume = 0f; musicSource.Stop(); } } activeSong = null; receivedHostSync = false; hostSyncedSongName = null; hostAlreadySelected = false; hostHasBroadcastedSync = false; } wasLanded = __instance.shipLanded; if (__instance.shipLanded) { EnforceCustomMusic(); return; } foreach (AudioSource musicSource2 in musicSources) { if (!((Object)(object)musicSource2 == (Object)null) && ((Component)musicSource2).gameObject.activeInHierarchy && ((Behaviour)musicSource2).enabled && (Object)(object)musicSource2.clip != (Object)null && ((Object)musicSource2.clip).name.ToLower().StartsWith("CustomDropshipSong_".ToLower())) { musicSource2.volume = 0f; if (musicSource2.isPlaying) { musicSource2.Stop(); } } } } 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 = 0; bool flag = false; while (!flag && num < 50) { num++; int num2 = shuffleDeck.Count; while (num2 > 1) { num2--; int index = rng.Next(num2 + 1); int value = shuffleDeck[index]; shuffleDeck[index] = shuffleDeck[num2]; shuffleDeck[num2] = value; } flag = true; if (checkBoundaryRepeat && shuffleDeck.Count >= 2 && (Object)(object)lastPlayedSong != (Object)null) { AudioClip val = Plugin.CustomMusicClips[shuffleDeck[0]]; if ((Object)(object)val == (Object)(object)lastPlayedSong) { flag = false; } } } shufflePointer = 0; string text = $"\n[CustomDropshipAudio] --- NEW SHUFFLE DECK GENERATED (Valid: {flag}, Attempts: {num}) ---"; 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 EnforceCustomMusic() { if (Plugin.CustomMusicClips.Count == 0 || musicSources.Count == 0) { return; } if (!Plugin.EnableHostSync.Value && receivedHostSync) { Debug.Log((object)"[CustomDropshipAudio] 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)"Client has host sync data but no active clip. Reapplying."); ApplyHostSyncedSong(hostSyncedSongName); } else { Debug.Log((object)"Waiting for host song selection..."); } return; } ChooseSongLocally(); } ApplyClipToSources(); } private static void ChooseSongLocally() { if (Plugin.CustomMusicClips.Count == 0 || (hostAlreadySelected && (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost)) { return; } int num = 0; PlaybackMode value = Plugin.AudioPlaybackMode.Value; 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)"[CustomDropshipAudio] Deck exhausted. Reshuffling playlist with repeat boundary guards!"); GenerateShuffleDeck(checkBoundaryRepeat: true); } else { Debug.Log((object)"[CustomDropshipAudio] Deck exhausted. Standard Shuffle looping back to start of fixed deck."); shufflePointer = 0; } } if (shuffleDeck.Count > 0) { num = shuffleDeck[shufflePointer]; activeSong = Plugin.CustomMusicClips[num]; shufflePointer++; Debug.Log((object)$"[CustomDropshipAudio] {value} Mode: Playing track index {num} ({shufflePointer}/{shuffleDeck.Count})"); } else { num = rng.Next(Plugin.CustomMusicClips.Count); activeSong = Plugin.CustomMusicClips[num]; } } else if (value == PlaybackMode.PreventSongRepeat && Plugin.CustomMusicClips.Count >= 2 && (Object)(object)lastPlayedSong != (Object)null) { Debug.Log((object)("[CustomDropshipAudio] Anti-repeat active. Making sure we don't play: " + ((Object)lastPlayedSong).name)); do { num = rng.Next(Plugin.CustomMusicClips.Count); activeSong = Plugin.CustomMusicClips[num]; } while ((Object)(object)activeSong == (Object)(object)lastPlayedSong); } else { num = rng.Next(Plugin.CustomMusicClips.Count); activeSong = Plugin.CustomMusicClips[num]; } lastPlayedSong = activeSong; hostAlreadySelected = true; Debug.Log((object)$"[CustomDropshipAudio] {GetRoleTag()} CHOSEN SONG ({num + 1} of {Plugin.CustomMusicClips.Count}): {((Object)activeSong).name}"); } private static void ApplyClipToSources() { if ((Object)(object)activeSong == (Object)null) { return; } foreach (AudioSource musicSource in musicSources) { if (!((Object)(object)musicSource == (Object)null) && ((Component)musicSource).gameObject.activeInHierarchy && ((Behaviour)musicSource).enabled) { if ((Object)(object)musicSource.clip != (Object)(object)activeSong) { musicSource.clip = activeSong; musicSource.time = 0f; } musicSource.loop = true; musicSource.volume = Plugin.MusicVolume.Value; musicSource.dopplerLevel = (Plugin.EnableDoppler.Value ? 1f : 0f); if (!musicSource.isPlaying) { musicSource.Play(); } } } } 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("CustomDropshipSong_", StringComparison.Ordinal)) { return ((Object)clip).name.Substring("CustomDropshipSong_".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("CustomDropshipAudio_SongSync", new HandleNamedMessageDelegate(OnSongSyncMessageReceived)); registeredOnManager = NetworkManager.Singleton; Debug.Log((object)("[CustomDropshipAudio] " + GetRoleTag() + " 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("CustomDropshipAudio_SongSync", val, (NetworkDelivery)2); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } Debug.Log((object)("[CustomDropshipAudio] " + GetRoleTag() + " 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)("[CustomDropshipAudio] " + GetRoleTag() + " Host Sync: received broadcast for '" + text + "'.")); if ((Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.IsHost) { return; } if (!Plugin.EnableHostSync.Value) { Debug.Log((object)("[CustomDropshipAudio] " + GetRoleTag() + " Host Sync: EnableHostSync is off locally, ignoring broadcast.")); } else if (text == "<HOST_SYNC_DISABLED>") { Debug.Log((object)("[CustomDropshipAudio] " + GetRoleTag() + " 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)("[CustomDropshipAudio] " + GetRoleTag() + " Using host synced song '" + hostFileName + "'.")); } else { Debug.Log((object)("[CustomDropshipAudio] " + GetRoleTag() + " Missing '" + hostFileName + "', using local selection.")); receivedHostSync = false; activeSong = null; ChooseSongLocally(); } ApplyClipToSources(); } }