Decompiled source of CustomElevatorAudio v1.0.0

Custom Elevator Audio.dll

Decompiled 14 hours ago
using 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 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.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)"=== CustomElevatorAudio Loaded ===");
		MusicFolderPath = ((BaseUnityPlugin)this).Config.Bind<string>("General", "MusicFolderPath", "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Lethal Company\\Custom Elevator Audio", "The absolute path to the folder containing your custom .mp3 files.");
		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, "The volume of the custom elevator 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 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;
		CustomMusicClips.Clear();
		CustomMusicClipsByFileName.Clear();
		MineshaftElevatorPatch.ResetStateForReload();
		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[] mp3Files = GetValidMp3Files(folderPath);
		if (mp3Files.Length == 0)
		{
			Debug.LogWarning((object)"[CustomElevatorAudio] 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 = "CustomElevatorSong_" + fileNameNoExt;
					CustomMusicClips.Add(clip);
					if (!CustomMusicClipsByFileName.ContainsKey(fileNameNoExt))
					{
						CustomMusicClipsByFileName.Add(fileNameNoExt, clip);
					}
					else
					{
						Debug.LogWarning((object)("[CustomElevatorAudio] Duplicate file name '" + fileNameNoExt + "' (case-sensitive 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 string[] GetValidMp3Files(string path)
	{
		try
		{
			return Directory.GetFiles(path, "*.mp3");
		}
		catch
		{
			return new string[0];
		}
	}
}
[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 ((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)
		{
			activeSong = null;
			receivedHostSync = false;
			hostSyncedSongName = null;
			hostAlreadySelected = false;
			hostHasBroadcastedSync = false;
			hasSelectedSongThisTrip = false;
			if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsHost)
			{
				ChooseSongLocally();
			}
		}
		else if (!flag && wasPlayingMusic)
		{
			activeSong = null;
			hasSelectedSongThisTrip = false;
			hostHasBroadcastedSync = false;
		}
		wasPlayingMusic = flag;
		if (flag)
		{
			EnforceElevatorMusic();
			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()
	{
		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...");
				}
				return;
			}
			ChooseSongLocally();
		}
		ApplyClipToSources();
	}

	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[CustomElevatorAudio] --- NEW ELEVATOR 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 ChooseSongLocally()
	{
		if (Plugin.CustomMusicClips.Count == 0 || hasSelectedSongThisTrip || (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)"[CustomElevatorAudio] Elevator deck exhausted. Reshuffling playlist with repeat boundary guards!");
					GenerateShuffleDeck(checkBoundaryRepeat: true);
				}
				else
				{
					Debug.Log((object)"[CustomElevatorAudio] Elevator deck exhausted. Standard Shuffle looping back to start of fixed deck.");
					shufflePointer = 0;
				}
			}
			if (shuffleDeck.Count > 0)
			{
				num = shuffleDeck[shufflePointer];
				AudioClip val = Plugin.CustomMusicClips[num];
				if (Plugin.CustomMusicClips.Count >= 2 && (Object)(object)lastPlayedSong != (Object)null && (Object)(object)val == (Object)(object)lastPlayedSong)
				{
					Debug.Log((object)$"[CustomElevatorAudio] Elevator {value} Mode: Track index {num} ({((Object)val).name}) matches last played song. Skipping to next deck position.");
					shufflePointer++;
					if (shufflePointer >= shuffleDeck.Count)
					{
						if (value == PlaybackMode.Reshuffle)
						{
							GenerateShuffleDeck(checkBoundaryRepeat: true);
						}
						else
						{
							shufflePointer = 0;
						}
					}
					num = shuffleDeck[shufflePointer];
					val = Plugin.CustomMusicClips[num];
				}
				activeSong = val;
				shufflePointer++;
				Debug.Log((object)$"[CustomElevatorAudio] Elevator {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)("[CustomElevatorAudio] Elevator 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;
		hasSelectedSongThisTrip = true;
		Debug.Log((object)$"[CustomElevatorAudio] {GetRoleTag()} ELEVATOR CHOSEN SONG ({num + 1} of {Plugin.CustomMusicClips.Count}): {((Object)activeSong).name}");
	}

	[HarmonyPatch("Update")]
	[HarmonyPostfix]
	private static void ApplyClipToJingleSource(MineshaftElevatorController __instance)
	{
		if (!fieldAccessReady || (Object)(object)activeSong == (Object)null)
		{
			return;
		}
		AudioSource val = JingleField.Invoke(__instance);
		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 void ApplyClipToSources(object _dummy = null)
	{
	}

	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();
		}
	}
}