Decompiled source of LethalMusicSync v1.0.2

LethalMusicSync.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Networking;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace LethalMusicSync;

[BepInPlugin("com.foxwood.lethalmusicsync", "LethalMusicSync", "1.0.2")]
public class LethalMusicSyncPlugin : BaseUnityPlugin
{
	public const string MOD_GUID = "com.foxwood.lethalmusicsync";

	public const string MOD_NAME = "LethalMusicSync";

	public const string MOD_VERSION = "1.0.2";

	internal static ManualLogSource LoggerInstance;

	private static LethalMusicSyncPlugin Instance;

	public static AudioSource CustomShipSpeaker;

	public static AudioSource CustomShipMusic;

	public static string CurrentPlayingUrl = "";

	public static bool IsCustomBoomboxPlaying = false;

	public static AudioClip[] OriginalBoomboxClips;

	public static List<string> SpeakerQueue = new List<string>();

	public static List<string> SpeakerQueueTitles = new List<string>();

	public static int SpeakerQueueIndex = -1;

	public static string SpeakerLoopMode = "off";

	public static List<string> MusicQueue = new List<string>();

	public static List<string> MusicQueueTitles = new List<string>();

	public static int MusicQueueIndex = -1;

	public static string MusicLoopMode = "off";

	public static List<string> BoomboxQueue = new List<string>();

	public static List<string> BoomboxQueueTitles = new List<string>();

	public static int BoomboxQueueIndex = -1;

	public static string BoomboxLoopMode = "off";

	public static bool IsProgrammaticActivation = false;

	public static bool IsSpeakerPaused = false;

	public static bool IsMusicPaused = false;

	public static bool IsBoomboxPaused = false;

	public static float SpeakerVolumeSetting = 0.8f;

	public static float MusicVolumeSetting = 0.6f;

	public static ConfigEntry<string> HudHotkey;

	public static ConfigEntry<string> MusicControlPermission;

	public static bool showBoomboxGui = false;

	private string guiUrlInput = "";

	private float lastVolumeBroadcastTime = 0f;

	private int lastBroadcastedVolumePercent = -1;

	public static readonly Queue<string> pendingChatBroadcasts = new Queue<string>();

	private void Awake()
	{
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Expected O, but got Unknown
		Instance = this;
		LoggerInstance = ((BaseUnityPlugin)this).Logger;
		LoggerInstance.LogInfo((object)"LethalMusicSync is loading...");
		HudHotkey = ((BaseUnityPlugin)this).Config.Bind<string>("General", "HUDHotkey", "F8", "The hotkey used to toggle the Boombox HUD Console dashboard. Can be F8, F9, F10, F11, Tab, etc.");
		MusicControlPermission = ((BaseUnityPlugin)this).Config.Bind<string>("General", "MusicControlPermission", "everyone", "Who can control the music (speaker, music, boombox). Options: everyone, host.");
		Harmony val = new Harmony("com.foxwood.lethalmusicsync");
		val.PatchAll();
		LoggerInstance.LogInfo((object)"LethalMusicSync patches applied successfully!");
	}

	public static Key ParseKey(string keyStr)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//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_003e: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			return (Key)Enum.Parse(typeof(Key), keyStr, ignoreCase: true);
		}
		catch
		{
			LoggerInstance.LogWarning((object)("[LMS] Invalid HUDHotkey config value: '" + keyStr + "'. Falling back to F8."));
			return (Key)101;
		}
	}

	public static bool CanLocalPlayerControl()
	{
		if (MusicControlPermission == null)
		{
			return true;
		}
		string text = MusicControlPermission.Value.ToLower();
		if (text == "host" || text == "hostonly")
		{
			bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
			if (!flag && (Object)(object)StartOfRound.Instance != (Object)null)
			{
				flag = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
			}
			return flag;
		}
		return true;
	}

	private void Update()
	{
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		while (pendingChatBroadcasts.Count > 0)
		{
			string text = pendingChatBroadcasts.Dequeue();
			if ((Object)(object)HUDManager.Instance != (Object)null)
			{
				try
				{
					HUDManager.Instance.AddTextToChatOnServer(text, -1);
					Log("[LMS] Safely sent GUI command: " + text);
				}
				catch (Exception ex)
				{
					LoggerInstance.LogError((object)("[LMS] Failed to send GUI command: " + ex.Message));
				}
			}
		}
		PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
		if ((Object)(object)val != (Object)null && val.currentlyHeldObjectServer is BoomboxItem)
		{
			bool flag = false;
			try
			{
				string keyStr = ((HudHotkey != null && !string.IsNullOrEmpty(HudHotkey.Value)) ? HudHotkey.Value : "F8");
				Key val2 = ParseKey(keyStr);
				if (Keyboard.current != null && ((ButtonControl)Keyboard.current[val2]).wasPressedThisFrame)
				{
					flag = true;
				}
			}
			catch (Exception ex)
			{
				LoggerInstance.LogError((object)("Error reading InputSystem keyboard: " + ex.Message));
			}
			if (flag)
			{
				showBoomboxGui = !showBoomboxGui;
				if (showBoomboxGui)
				{
					Cursor.visible = true;
					Cursor.lockState = (CursorLockMode)0;
					val.disableLookInput = true;
				}
				else
				{
					Cursor.visible = false;
					Cursor.lockState = (CursorLockMode)1;
					val.disableLookInput = false;
				}
			}
		}
		else if (showBoomboxGui)
		{
			showBoomboxGui = false;
			Cursor.visible = false;
			Cursor.lockState = (CursorLockMode)1;
			if ((Object)(object)val != (Object)null)
			{
				val.disableLookInput = false;
			}
		}
		if (!((Object)(object)val != (Object)null))
		{
			return;
		}
		bool flag2 = false;
		if ((!val.isPlayerDead || !((Object)(object)val.spectatedPlayerScript != (Object)null)) ? val.isInsideFactory : val.spectatedPlayerScript.isInsideFactory)
		{
			if ((Object)(object)CustomShipSpeaker != (Object)null && CustomShipSpeaker.volume > 0f)
			{
				CustomShipSpeaker.volume = 0f;
			}
			if ((Object)(object)CustomShipMusic != (Object)null && CustomShipMusic.volume > 0f)
			{
				CustomShipMusic.volume = 0f;
			}
		}
		else
		{
			if ((Object)(object)CustomShipSpeaker != (Object)null && CustomShipSpeaker.volume != SpeakerVolumeSetting)
			{
				CustomShipSpeaker.volume = SpeakerVolumeSetting;
			}
			if ((Object)(object)CustomShipMusic != (Object)null && CustomShipMusic.volume != MusicVolumeSetting)
			{
				CustomShipMusic.volume = MusicVolumeSetting;
			}
		}
	}

	private void OnGUI()
	{
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Expected O, but got Unknown
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Expected O, but got Unknown
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0250: Unknown result type (might be due to invalid IL or missing references)
		//IL_0257: Expected O, but got Unknown
		//IL_0267: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_0345: Unknown result type (might be due to invalid IL or missing references)
		//IL_034c: Expected O, but got Unknown
		//IL_035c: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0452: Unknown result type (might be due to invalid IL or missing references)
		//IL_0493: Unknown result type (might be due to invalid IL or missing references)
		//IL_06ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_0504: Unknown result type (might be due to invalid IL or missing references)
		//IL_055a: Unknown result type (might be due to invalid IL or missing references)
		//IL_06eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_079e: Unknown result type (might be due to invalid IL or missing references)
		//IL_07d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0808: Unknown result type (might be due to invalid IL or missing references)
		//IL_062c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0632: Invalid comparison between Unknown and I4
		//IL_08cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_099f: Unknown result type (might be due to invalid IL or missing references)
		PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		GrabbableObject currentlyHeldObjectServer = val.currentlyHeldObjectServer;
		BoomboxItem val2 = (BoomboxItem)(object)((currentlyHeldObjectServer is BoomboxItem) ? currentlyHeldObjectServer : null);
		if ((Object)(object)val2 == (Object)null)
		{
			return;
		}
		GUIStyle val3 = new GUIStyle(GUI.skin.label);
		val3.normal.textColor = Color.cyan;
		val3.fontSize = 13;
		val3.fontStyle = (FontStyle)1;
		GUI.backgroundColor = new Color(0f, 0.1f, 0.15f, 0.85f);
		GUI.Box(new Rect(15f, 15f, 250f, 40f), "");
		string text = ((HudHotkey != null && !string.IsNullOrEmpty(HudHotkey.Value)) ? HudHotkey.Value : "F8");
		GUI.Label(new Rect(25f, 23f, 230f, 25f), "\ud83d\udcfb [LMS HUD] Press " + text + " to Toggle Dashboard", val3);
		if (!showBoomboxGui)
		{
			return;
		}
		Rect val4 = default(Rect);
		((Rect)(ref val4))..ctor((float)Screen.width / 2f - 200f, (float)Screen.height / 2f - 190f, 400f, 380f);
		GUI.backgroundColor = new Color(0.08f, 0.12f, 0.08f, 0.98f);
		GUI.Box(val4, "");
		GUIStyle val5 = new GUIStyle(GUI.skin.label);
		val5.alignment = (TextAnchor)4;
		val5.fontSize = 15;
		val5.fontStyle = (FontStyle)1;
		val5.normal.textColor = Color.green;
		GUI.Label(new Rect(((Rect)(ref val4)).x, ((Rect)(ref val4)).y + 12f, ((Rect)(ref val4)).width, 25f), "=== \ud83d\udcfb LMS BOOMBOX CONSOLE ===", val5);
		string text2 = "Idle (Queue Empty)";
		if (BoomboxQueueIndex >= 0 && BoomboxQueueIndex < BoomboxQueueTitles.Count)
		{
			text2 = BoomboxQueueTitles[BoomboxQueueIndex];
		}
		GUIStyle val6 = new GUIStyle(GUI.skin.label);
		val6.alignment = (TextAnchor)4;
		val6.normal.textColor = Color.white;
		val6.fontSize = 12;
		GUI.Label(new Rect(((Rect)(ref val4)).x + 20f, ((Rect)(ref val4)).y + 45f, ((Rect)(ref val4)).width - 40f, 20f), "Playing: " + text2, val6);
		GUI.Label(new Rect(((Rect)(ref val4)).x + 20f, ((Rect)(ref val4)).y + 65f, ((Rect)(ref val4)).width - 40f, 20f), "Loop Mode: " + BoomboxLoopMode.ToUpper(), val6);
		float num = ((Rect)(ref val4)).y + 95f;
		float num2 = 110f;
		float num3 = 30f;
		bool flag = CanLocalPlayerControl();
		if (!flag)
		{
			GUIStyle val7 = new GUIStyle(GUI.skin.label);
			val7.alignment = (TextAnchor)4;
			val7.normal.textColor = Color.red;
			val7.fontSize = 11;
			val7.fontStyle = (FontStyle)1;
			GUI.Label(new Rect(((Rect)(ref val4)).x + 20f, ((Rect)(ref val4)).y + 35f, ((Rect)(ref val4)).width - 40f, 15f), "⚠\ufe0f LOBBY OWNER ONLY PERMISSIONS ENABLED", val7);
		}
		GUI.enabled = flag;
		string text3 = (val2.isPlayingMusic ? "PAUSE" : "RESUME");
		if (GUI.Button(new Rect(((Rect)(ref val4)).x + 20f, num, num2, num3), text3))
		{
			if (val2.isPlayingMusic)
			{
				string item = "[LMS_SYNC] BOOMBOX PAUSE";
				pendingChatBroadcasts.Enqueue(item);
			}
			else
			{
				string item = "[LMS_SYNC] BOOMBOX RESUME";
				pendingChatBroadcasts.Enqueue(item);
			}
		}
		if (GUI.Button(new Rect(((Rect)(ref val4)).x + 145f, num, num2, num3), "SKIP"))
		{
			string item = "[LMS_SYNC] BOOMBOX SKIP";
			pendingChatBroadcasts.Enqueue(item);
		}
		if (GUI.Button(new Rect(((Rect)(ref val4)).x + 270f, num, num2, num3), "CLEAR"))
		{
			string item = "[LMS_SYNC] BOOMBOX CLEAR";
			pendingChatBroadcasts.Enqueue(item);
		}
		GUI.enabled = true;
		float num4 = num + 45f;
		if ((Object)(object)val2.boomboxAudio != (Object)null)
		{
			GUI.Label(new Rect(((Rect)(ref val4)).x + 20f, num4, 120f, 20f), "Volume: " + Mathf.RoundToInt(val2.boomboxAudio.volume * 100f) + "%", val6);
			float num5 = GUI.HorizontalSlider(new Rect(((Rect)(ref val4)).x + 140f, num4 + 5f, 240f, 20f), val2.boomboxAudio.volume, 0f, 1f);
			int num6 = Mathf.RoundToInt(num5 * 100f);
			if (Mathf.Abs(num5 - val2.boomboxAudio.volume) > 0.01f)
			{
				val2.boomboxAudio.volume = num5;
				if (flag && num6 != lastBroadcastedVolumePercent && Time.time - lastVolumeBroadcastTime > 0.25f)
				{
					lastVolumeBroadcastTime = Time.time;
					lastBroadcastedVolumePercent = num6;
					string item = "[LMS_SYNC] BOOMBOX VOLUME " + num6;
					pendingChatBroadcasts.Enqueue(item);
				}
			}
			if (Event.current != null && (int)Event.current.type == 1 && flag && num6 != lastBroadcastedVolumePercent)
			{
				lastVolumeBroadcastTime = Time.time;
				lastBroadcastedVolumePercent = num6;
				string item = "[LMS_SYNC] BOOMBOX VOLUME " + num6;
				pendingChatBroadcasts.Enqueue(item);
			}
		}
		else
		{
			GUI.Label(new Rect(((Rect)(ref val4)).x + 20f, num4, 360f, 20f), "Volume: [Audio Source Missing]", val6);
		}
		GUI.enabled = flag;
		float num7 = num4 + 30f;
		if (GUI.Button(new Rect(((Rect)(ref val4)).x + 20f, num7, 360f, 30f), "CYCLE LOOP (Current: " + BoomboxLoopMode.ToUpper() + ")"))
		{
			string text4 = "off";
			if (BoomboxLoopMode == "off")
			{
				text4 = "track";
			}
			else if (BoomboxLoopMode == "track")
			{
				text4 = "queue";
			}
			string item = "[LMS_SYNC] BOOMBOX LOOP " + text4;
			pendingChatBroadcasts.Enqueue(item);
		}
		float num8 = num7 + 45f;
		GUI.Label(new Rect(((Rect)(ref val4)).x + 20f, num8, 360f, 20f), "Paste Link or type YouTube Search query:", val6);
		guiUrlInput = GUI.TextField(new Rect(((Rect)(ref val4)).x + 20f, num8 + 20f, 360f, 25f), guiUrlInput);
		float num9 = num8 + 55f;
		if (GUI.Button(new Rect(((Rect)(ref val4)).x + 20f, num9, 170f, 35f), "PLAY NOW") && !string.IsNullOrEmpty(guiUrlInput))
		{
			string text5 = guiUrlInput.Trim();
			if (!text5.StartsWith("http://") && !text5.StartsWith("https://"))
			{
				SearchAndQueueYoutube(text5, "BOOMBOX", "PLAY", viaGui: true);
			}
			else
			{
				string item = "[LMS_SYNC] BOOMBOX PLAY " + text5;
				pendingChatBroadcasts.Enqueue(item);
			}
			guiUrlInput = "";
		}
		if (GUI.Button(new Rect(((Rect)(ref val4)).x + 210f, num9, 170f, 35f), "ADD TO QUEUE") && !string.IsNullOrEmpty(guiUrlInput))
		{
			string text5 = guiUrlInput.Trim();
			if (!text5.StartsWith("http://") && !text5.StartsWith("https://"))
			{
				SearchAndQueueYoutube(text5, "BOOMBOX", "QUEUE", viaGui: true);
			}
			else
			{
				string item = "[LMS_SYNC] BOOMBOX QUEUE " + text5;
				pendingChatBroadcasts.Enqueue(item);
			}
			guiUrlInput = "";
		}
		GUI.enabled = true;
		float num10 = num9 + 45f;
		GUI.Label(new Rect(((Rect)(ref val4)).x + 20f, num10, 360f, 20f), "Total Tracks in Queue: " + BoomboxQueue.Count, val6);
	}

	public static void Log(string message)
	{
		LoggerInstance.LogInfo((object)message);
	}

	public static string ResolveSunoUrl(string inputUrl)
	{
		if (string.IsNullOrEmpty(inputUrl))
		{
			return "";
		}
		if (inputUrl.Contains("cdn1.suno.ai") && inputUrl.EndsWith(".mp3"))
		{
			return inputUrl;
		}
		Match match = Regex.Match(inputUrl, "([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})");
		if (match.Success)
		{
			string value = match.Groups[1].Value;
			string text = "https://cdn1.suno.ai/" + value + ".mp3";
			Log("Resolved Suno AI URL: " + inputUrl + " -> " + text);
			return text;
		}
		return inputUrl;
	}

	public static AudioSource GetOrCreateShipAudioSource(string device)
	{
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Expected O, but got Unknown
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_030a: Expected O, but got Unknown
		//IL_0324: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = GameObject.Find("HangarShip");
		if ((Object)(object)val == (Object)null)
		{
			val = GameObject.Find("HangarShip(Clone)");
		}
		if ((Object)(object)val == (Object)null && (Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.shipAnimatorObject != (Object)null)
		{
			val = ((Component)StartOfRound.Instance.shipAnimatorObject).gameObject;
		}
		if ((Object)(object)val == (Object)null)
		{
			Log("HangarShip not found in scene. Cannot spawn audio source.");
			return null;
		}
		PlayerControllerB val2 = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
		bool flag = false;
		if ((Object)(object)val2 != (Object)null)
		{
			flag = ((!val2.isPlayerDead || !((Object)(object)val2.spectatedPlayerScript != (Object)null)) ? val2.isInsideFactory : val2.spectatedPlayerScript.isInsideFactory);
		}
		if (device == "SPEAKER")
		{
			if ((Object)(object)CustomShipSpeaker != (Object)null)
			{
				return CustomShipSpeaker;
			}
			GameObject val3 = new GameObject("LethalMusicSyncSpeaker3D");
			val3.transform.SetParent(val.transform);
			val3.transform.localPosition = new Vector3(0f, 2.2f, -1f);
			AudioSource val4 = val3.AddComponent<AudioSource>();
			val4.playOnAwake = false;
			val4.spatialBlend = 1f;
			val4.rolloffMode = (AudioRolloffMode)0;
			val4.minDistance = 3f;
			val4.maxDistance = 18f;
			val4.volume = (flag ? 0f : SpeakerVolumeSetting);
			AudioSource componentInChildren = val.GetComponentInChildren<AudioSource>();
			if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.outputAudioMixerGroup != (Object)null)
			{
				val4.outputAudioMixerGroup = componentInChildren.outputAudioMixerGroup;
				Log("[LMS] Routed Speaker through Ship AudioMixerGroup: " + ((Object)componentInChildren.outputAudioMixerGroup).name);
			}
			else
			{
				AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
				AudioSource[] array2 = array;
				foreach (AudioSource val5 in array2)
				{
					if ((Object)(object)val5 != (Object)null && (Object)(object)val5.outputAudioMixerGroup != (Object)null)
					{
						val4.outputAudioMixerGroup = val5.outputAudioMixerGroup;
						Log("[LMS] Routed Speaker through Fallback AudioMixerGroup: " + ((Object)val5.outputAudioMixerGroup).name);
						break;
					}
				}
			}
			CustomShipSpeaker = val4;
			Log("Custom 3D Spatial Hangar Ship Speaker spawned successfully!");
			return CustomShipSpeaker;
		}
		if (device == "MUSIC")
		{
			if ((Object)(object)CustomShipMusic != (Object)null)
			{
				return CustomShipMusic;
			}
			GameObject val6 = new GameObject("LethalMusicSyncMusic2D");
			val6.transform.SetParent(val.transform);
			val6.transform.localPosition = Vector3.zero;
			AudioSource val4 = val6.AddComponent<AudioSource>();
			val4.playOnAwake = false;
			val4.spatialBlend = 0f;
			val4.volume = (flag ? 0f : MusicVolumeSetting);
			AudioSource componentInChildren = val.GetComponentInChildren<AudioSource>();
			if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.outputAudioMixerGroup != (Object)null)
			{
				val4.outputAudioMixerGroup = componentInChildren.outputAudioMixerGroup;
				Log("[LMS] Routed Music through Ship AudioMixerGroup: " + ((Object)componentInChildren.outputAudioMixerGroup).name);
			}
			else
			{
				AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
				AudioSource[] array2 = array;
				foreach (AudioSource val5 in array2)
				{
					if ((Object)(object)val5 != (Object)null && (Object)(object)val5.outputAudioMixerGroup != (Object)null)
					{
						val4.outputAudioMixerGroup = val5.outputAudioMixerGroup;
						Log("[LMS] Routed Music through Fallback AudioMixerGroup: " + ((Object)val5.outputAudioMixerGroup).name);
						break;
					}
				}
			}
			CustomShipMusic = val4;
			Log("Custom 2D Global Ship Background Music spawned successfully!");
			return CustomShipMusic;
		}
		return null;
	}

	public static void PlayAudioFromUrl(AudioSource source, string url, string device, Action<AudioClip> onSuccess = null)
	{
		if (!((Object)(object)source == (Object)null) && !string.IsNullOrEmpty(url))
		{
			((MonoBehaviour)Instance).StartCoroutine(StreamAudioCoroutine(source, url, device, onSuccess));
		}
	}

	private static IEnumerator StreamAudioCoroutine(AudioSource source, string url, string device, Action<AudioClip> onSuccess)
	{
		switch (device)
		{
		case "SPEAKER":
			IsSpeakerPaused = false;
			break;
		case "MUSIC":
			IsMusicPaused = false;
			break;
		case "BOOMBOX":
			IsBoomboxPaused = false;
			break;
		}
		string targetUrl = url;
		if (url.Contains("youtube.com") || url.Contains("youtu.be"))
		{
			Log("[LMS] Resolving YouTube URL via Cobalt API...");
			string requestJson = "{\"url\":\"" + url + "\",\"downloadMode\":\"audio\"}";
			byte[] requestBytes = Encoding.UTF8.GetBytes(requestJson);
			string[] cobaltEndpoints = new string[2] { "https://apicobalt.mgytr.top/", "https://api.cobalt.tools/" };
			bool resolvedSuccessfully = false;
			try
			{
				string[] array = cobaltEndpoints;
				foreach (string endpoint in array)
				{
					Log("[LMS] Sending request to Cobalt endpoint: " + endpoint);
					UnityWebRequest resolveRequest = new UnityWebRequest(endpoint, "POST");
					try
					{
						resolveRequest.uploadHandler = (UploadHandler)new UploadHandlerRaw(requestBytes);
						resolveRequest.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
						resolveRequest.SetRequestHeader("Content-Type", "application/json");
						resolveRequest.SetRequestHeader("Accept", "application/json");
						yield return resolveRequest.SendWebRequest();
						if ((int)resolveRequest.result == 2 || (int)resolveRequest.result == 3)
						{
							Log("[LMS] Cobalt endpoint error (" + endpoint + "): " + resolveRequest.error);
							if (resolveRequest.downloadHandler != null && !string.IsNullOrEmpty(resolveRequest.downloadHandler.text))
							{
								Log("[LMS] Server response details: " + resolveRequest.downloadHandler.text);
							}
							continue;
						}
						string responseText = resolveRequest.downloadHandler.text;
						if (responseText.Contains("error.api.auth"))
						{
							Log("[LMS] Cobalt endpoint (" + endpoint + ") failed due to auth lock. Response: " + responseText);
							continue;
						}
						Match match = Regex.Match(responseText, "\"url\":\"(.*?)\"");
						if (match.Success)
						{
							targetUrl = match.Groups[1].Value;
							Log("[LMS] Successfully resolved YouTube direct stream URL via " + endpoint);
							Match match2 = Regex.Match(responseText, "\"title\":\"(.*?)\"");
							if (match2.Success)
							{
								string value = match2.Groups[1].Value;
								UpdateQueueTitle(url, value, device);
							}
							resolvedSuccessfully = true;
							break;
						}
						Log("[LMS] Cobalt response parsing failed for endpoint " + endpoint + ". Response: " + responseText);
					}
					finally
					{
						((IDisposable)resolveRequest)?.Dispose();
					}
				}
			}
			finally
			{
			}
			if (!resolvedSuccessfully)
			{
				Log("[LMS] Failed to resolve YouTube URL through any Cobalt endpoints. Aborting stream.");
				yield break;
			}
		}
		else if (url.Contains("suno.com/playlist") || url.Contains("suno.com/p/"))
		{
			Log("[LMS] Suno Playlist URL detected. Fetching playlist page to extract tracks...");
			string playlistId = "";
			Match idMatch = Regex.Match(url, "/(?:playlist|p)/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})");
			if (idMatch.Success)
			{
				playlistId = idMatch.Groups[1].Value.ToLower();
			}
			UnityWebRequest pageRequest = UnityWebRequest.Get(url);
			try
			{
				pageRequest.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36");
				pageRequest.SetRequestHeader("Referer", "https://suno.com/");
				yield return pageRequest.SendWebRequest();
				if ((int)pageRequest.result == 2 || (int)pageRequest.result == 3)
				{
					Log("[LMS] Failed to fetch Suno playlist page: " + pageRequest.error);
				}
				else
				{
					string text = pageRequest.downloadHandler.text;
					string text2 = "";
					try
					{
						int num = text.IndexOf("playlist_clips");
						if (num != -1)
						{
							int num2 = text.LastIndexOf("<script>", num);
							int num3 = text.IndexOf("</script>", num);
							if (num2 != -1 && num3 != -1)
							{
								string input = text.Substring(num2, num3 - num2);
								Match match3 = Regex.Match(input, "self\\.__next_f\\.push\\(\\[1,\\s*\"(.*?)\"\\s*\\]\\)", RegexOptions.Singleline);
								if (match3.Success)
								{
									string value2 = match3.Groups[1].Value;
									string text3 = JsonConvert.DeserializeObject<string>("\"" + value2 + "\"");
									int num4 = text3.IndexOf("[");
									if (num4 != -1)
									{
										JToken token = JToken.Parse(text3.Substring(num4));
										JToken val = FindPlaylistClips(token);
										if (val != null && (int)val.Type == 2 && val.HasValues)
										{
											JToken val2 = val[(object)0][(object)"clip"];
											if (val2 != null)
											{
												JToken val3 = val2[(object)"id"];
												if (val3 != null)
												{
													text2 = ((object)val3).ToString();
												}
											}
										}
									}
								}
							}
						}
					}
					catch
					{
					}
					if (string.IsNullOrEmpty(text2))
					{
						MatchCollection matchCollection = Regex.Matches(text, "([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})");
						foreach (Match item in matchCollection)
						{
							string text4 = item.Value.ToLower();
							if (text4 != playlistId && text4 != "019c23e4-1dd3-7d05-8d4f-341d10bb7c55")
							{
								text2 = text4;
								break;
							}
						}
					}
					if (!string.IsNullOrEmpty(text2))
					{
						targetUrl = "https://cdn1.suno.ai/" + text2 + ".mp3";
						Log("[LMS] Successfully resolved Suno Playlist to track direct URL: " + targetUrl);
					}
					else
					{
						Log("[LMS] Could not find any song UUIDs in Suno playlist page.");
					}
				}
			}
			finally
			{
				((IDisposable)pageRequest)?.Dispose();
			}
		}
		else if (url.Contains("suno.com/song") || url.Contains("suno.com/track"))
		{
			Match match5 = Regex.Match(url, "([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})");
			if (match5.Success)
			{
				string value3 = match5.Groups[1].Value;
				targetUrl = "https://cdn1.suno.ai/" + value3 + ".mp3";
				Log("[LMS] Resolved Suno song to direct URL: " + targetUrl);
			}
		}
		else
		{
			targetUrl = ResolveSunoUrl(url);
		}
		Log("[LMS] Starting stream of URL: " + targetUrl);
		UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(targetUrl, (AudioType)13);
		try
		{
			if (targetUrl.Contains("suno.ai") || targetUrl.Contains("suno.com"))
			{
				www.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36");
				www.SetRequestHeader("Referer", "https://suno.com/");
			}
			yield return www.SendWebRequest();
			if ((int)www.result == 2 || (int)www.result == 3)
			{
				Log("[LMS] Stream Error: " + www.error);
				yield break;
			}
			AudioClip content = DownloadHandlerAudioClip.GetContent(www);
			if ((Object)(object)content != (Object)null)
			{
				source.clip = content;
				source.loop = false;
				source.Play();
				Log("[LMS] Playing audio track successfully!");
				bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
				if (!flag && (Object)(object)StartOfRound.Instance != (Object)null)
				{
					flag = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
				}
				if (flag && (Object)(object)HUDManager.Instance != (Object)null)
				{
					string text5 = "Audio Stream";
					if (device == "SPEAKER" && SpeakerQueueIndex >= 0 && SpeakerQueueIndex < SpeakerQueueTitles.Count)
					{
						text5 = SpeakerQueueTitles[SpeakerQueueIndex];
					}
					else if (device == "MUSIC" && MusicQueueIndex >= 0 && MusicQueueIndex < MusicQueueTitles.Count)
					{
						text5 = MusicQueueTitles[MusicQueueIndex];
					}
					else if (device == "BOOMBOX" && BoomboxQueueIndex >= 0 && BoomboxQueueIndex < BoomboxQueueTitles.Count)
					{
						text5 = BoomboxQueueTitles[BoomboxQueueIndex];
					}
					HUDManager.Instance.AddTextToChatOnServer("<color=cyan>[LMS] ▶ Now Playing: " + text5 + " (" + device + ")</color>", -1);
				}
				((MonoBehaviour)Instance).StartCoroutine(TrackEndTrigger(source, content, device));
				onSuccess?.Invoke(content);
			}
			else
			{
				Log("[LMS] Download succeeded, but clip is null.");
			}
		}
		finally
		{
			((IDisposable)www)?.Dispose();
		}
	}

	private static JToken FindPlaylistClips(JToken token)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Invalid comparison between Unknown and I4
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Invalid comparison between Unknown and I4
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		if (token == null)
		{
			return null;
		}
		if ((int)token.Type == 1)
		{
			JObject val = (JObject)token;
			if (val.ContainsKey("playlist_clips"))
			{
				return val["playlist_clips"];
			}
			foreach (KeyValuePair<string, JToken> item in val)
			{
				JToken val2 = FindPlaylistClips(item.Value);
				if (val2 != null)
				{
					return val2;
				}
			}
		}
		else if ((int)token.Type == 2)
		{
			foreach (JToken item2 in (IEnumerable<JToken>)token)
			{
				JToken val2 = FindPlaylistClips(item2);
				if (val2 != null)
				{
					return val2;
				}
			}
		}
		return null;
	}

	public static void SearchAndQueueYoutube(string query, string device, string action, bool viaGui)
	{
		((MonoBehaviour)Instance).StartCoroutine(SearchYoutubeCoroutine(device, action, query, viaGui));
	}

	private static IEnumerator SearchYoutubeCoroutine(string device, string action, string query, bool viaGui)
	{
		string escapedQuery = Uri.EscapeDataString(query);
		string resolvedUrl = null;
		string resolvedTitle = null;
		string ytSearchUrl = "https://www.youtube.com/results?search_query=" + escapedQuery;
		Log("[LMS] Searching YouTube directly via: " + ytSearchUrl);
		UnityWebRequest ytRequest = UnityWebRequest.Get(ytSearchUrl);
		try
		{
			ytRequest.timeout = 6;
			ytRequest.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
			yield return ytRequest.SendWebRequest();
			if ((int)ytRequest.result == 1)
			{
				string text = ytRequest.downloadHandler.text;
				Match match = Regex.Match(text, "\\\"videoRenderer\\\":\\{\\\"videoId\\\":\\\"([a-zA-Z0-9_-]{11})\\\"");
				if (match.Success)
				{
					resolvedUrl = "https://www.youtube.com/watch?v=" + match.Groups[1].Value;
					Match match2 = Regex.Match(text, "\\\"videoRenderer\\\":\\{\\\"videoId\\\":\\\"([a-zA-Z0-9_-]{11})\\\".*?\\\"title\\\":\\{\\\"runs\\\":\\[\\{\\\"text\\\":\\\"([^\\\"]+)\\\"");
					if (match2.Success && match2.Groups[1].Value == match.Groups[1].Value)
					{
						resolvedTitle = match2.Groups[2].Value;
						try
						{
							resolvedTitle = Regex.Unescape(resolvedTitle);
						}
						catch
						{
						}
					}
					else
					{
						resolvedTitle = query;
					}
					Log("[LMS] Direct YouTube search succeeded!");
				}
			}
		}
		finally
		{
			((IDisposable)ytRequest)?.Dispose();
		}
		if (string.IsNullOrEmpty(resolvedUrl))
		{
			string[] instances = new string[6] { "https://inv.nadeko.net", "https://invidious.nerdvpn.de", "https://iv.melmac.space", "https://invidious.no-logs.com", "https://invidious.flokinet.to", "https://yewtu.be" };
			try
			{
				string[] array = instances;
				foreach (string instance in array)
				{
					string searchUrl = instance + "/api/v1/search?q=" + escapedQuery;
					Log("[LMS] Direct search failed. Trying Invidious fallback: " + searchUrl);
					UnityWebRequest request = UnityWebRequest.Get(searchUrl);
					try
					{
						request.timeout = 5;
						yield return request.SendWebRequest();
						if ((int)request.result == 1)
						{
							try
							{
								string text2 = request.downloadHandler.text;
								JArray val = JArray.Parse(text2);
								foreach (JToken item in val)
								{
									if (item[(object)"type"] != null && ((object)item[(object)"type"]).ToString() == "video")
									{
										string text3 = ((object)item[(object)"videoId"]).ToString();
										resolvedTitle = ((item[(object)"title"] != null) ? ((object)item[(object)"title"]).ToString() : query);
										resolvedUrl = "https://www.youtube.com/watch?v=" + text3;
										break;
									}
								}
							}
							catch (Exception ex)
							{
								Log("[LMS] Error parsing search JSON: " + ex.Message);
							}
						}
					}
					finally
					{
						((IDisposable)request)?.Dispose();
					}
					if (!string.IsNullOrEmpty(resolvedUrl))
					{
						break;
					}
				}
			}
			finally
			{
			}
		}
		if (!string.IsNullOrEmpty(resolvedUrl))
		{
			Log("[LMS] Search resolved to: " + resolvedUrl + " (" + resolvedTitle + ")");
			if ((Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer("<color=cyan>[LMS] \ud83d\udd0d Found: " + resolvedTitle + " (" + device.ToUpper() + ")</color>", -1);
			}
			string text4 = "[LMS_SYNC] " + device.ToUpper() + " " + action.ToUpper() + " " + resolvedUrl;
			if (viaGui)
			{
				pendingChatBroadcasts.Enqueue(text4);
			}
			else if ((Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer(text4, -1);
			}
		}
		else
		{
			string text5 = "YouTube search failed for: \"" + query + "\"";
			Log("[LMS] " + text5);
			if ((Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer("[LMS] Search failed. No video found for search query.", -1);
			}
		}
	}

	public static void ResolveAndQueueSunoPlaylist(string url, string device, bool clearQueue)
	{
		((MonoBehaviour)Instance).StartCoroutine(ResolveSunoPlaylistCoroutine(url, device, clearQueue));
	}

	private static IEnumerator ResolveSunoPlaylistCoroutine(string url, string device, bool clearQueue)
	{
		Log("[LMS] Scraping Suno Playlist tracks: " + url);
		string playlistId = "";
		Match idMatch = Regex.Match(url, "/(?:playlist|p)/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})");
		if (idMatch.Success)
		{
			playlistId = idMatch.Groups[1].Value.ToLower();
		}
		UnityWebRequest pageRequest = UnityWebRequest.Get(url);
		try
		{
			pageRequest.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36");
			pageRequest.SetRequestHeader("Referer", "https://suno.com/");
			yield return pageRequest.SendWebRequest();
			if ((int)pageRequest.result == 2 || (int)pageRequest.result == 3)
			{
				Log("[LMS] Failed to scrape playlist: " + pageRequest.error);
				yield break;
			}
			string text = pageRequest.downloadHandler.text;
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			try
			{
				int num = text.IndexOf("playlist_clips");
				if (num != -1)
				{
					int num2 = text.LastIndexOf("<script>", num);
					int num3 = text.IndexOf("</script>", num);
					if (num2 != -1 && num3 != -1)
					{
						string input = text.Substring(num2, num3 - num2);
						Match match = Regex.Match(input, "self\\.__next_f\\.push\\(\\[1,\\s*\"(.*?)\"\\s*\\]\\)", RegexOptions.Singleline);
						if (match.Success)
						{
							string value = match.Groups[1].Value;
							string text2 = JsonConvert.DeserializeObject<string>("\"" + value + "\"");
							int num4 = text2.IndexOf("[");
							if (num4 != -1)
							{
								JToken token = JToken.Parse(text2.Substring(num4));
								JToken val = FindPlaylistClips(token);
								if (val != null && (int)val.Type == 2)
								{
									JArray val2 = (JArray)val;
									foreach (JToken item2 in val2)
									{
										JToken val3 = item2[(object)"clip"];
										if (val3 == null)
										{
											continue;
										}
										string text3 = ((object)val3[(object)"id"])?.ToString();
										string text4 = ((object)val3[(object)"title"])?.ToString();
										if (!string.IsNullOrEmpty(text3))
										{
											string item = "https://cdn1.suno.ai/" + text3 + ".mp3";
											if (string.IsNullOrEmpty(text4))
											{
												text4 = "Suno Track " + text3.Substring(0, 8);
											}
											if (!list.Contains(item))
											{
												list.Add(item);
												list2.Add(text4);
											}
										}
									}
									Log("[LMS] Scraped " + list.Count + " tracks via Strategy 1 (RSC Parser) with real titles!");
								}
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				Log("[LMS] Strategy 1 (RSC Parser) error: " + ex.Message + ". Falling back to Strategy 2...");
			}
			if (list.Count == 0)
			{
				Log("[LMS] Strategy 1 failed or found no clips. Running Strategy 2 broad UUID scan...");
				MatchCollection matchCollection = Regex.Matches(text, "([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})");
				foreach (Match item3 in matchCollection)
				{
					string text5 = item3.Value.ToLower();
					if (text5 != playlistId && text5 != "019c23e4-1dd3-7d05-8d4f-341d10bb7c55")
					{
						string item = "https://cdn1.suno.ai/" + text5 + ".mp3";
						if (!list.Contains(item))
						{
							list.Add(item);
							list2.Add("Suno Track " + text5.Substring(0, 8));
						}
					}
				}
				Log("[LMS] Scraped " + list.Count + " tracks via Strategy 2 (Broad UUID Scan).");
			}
			if (list.Count <= 0)
			{
				yield break;
			}
			switch (device)
			{
			case "SPEAKER":
			{
				if (clearQueue)
				{
					SpeakerQueue.Clear();
					SpeakerQueueTitles.Clear();
					SpeakerQueueIndex = 0;
				}
				int count = SpeakerQueue.Count;
				for (int i = 0; i < list.Count; i++)
				{
					SpeakerQueue.Add(list[i]);
					SpeakerQueueTitles.Add(list2[i]);
				}
				Log("[LMS] Queued " + list.Count + " tracks for SPEAKER.");
				bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
				if (!flag && (Object)(object)StartOfRound.Instance != (Object)null)
				{
					flag = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
				}
				if (flag && (Object)(object)HUDManager.Instance != (Object)null)
				{
					HUDManager.Instance.AddTextToChatOnServer("<color=yellow>[LMS] ➕ Queued Playlist: Scraped " + list.Count + " Suno tracks (SPEAKER)</color>", -1);
				}
				if (clearQueue || SpeakerQueueIndex == -1)
				{
					SpeakerQueueIndex = count;
					PlayAudioFromUrl(GetOrCreateShipAudioSource("SPEAKER"), SpeakerQueue[SpeakerQueueIndex], "SPEAKER");
				}
				break;
			}
			case "MUSIC":
			{
				if (clearQueue)
				{
					MusicQueue.Clear();
					MusicQueueTitles.Clear();
					MusicQueueIndex = 0;
				}
				int count = MusicQueue.Count;
				for (int i = 0; i < list.Count; i++)
				{
					MusicQueue.Add(list[i]);
					MusicQueueTitles.Add(list2[i]);
				}
				Log("[LMS] Queued " + list.Count + " tracks for MUSIC.");
				bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
				if (!flag && (Object)(object)StartOfRound.Instance != (Object)null)
				{
					flag = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
				}
				if (flag && (Object)(object)HUDManager.Instance != (Object)null)
				{
					HUDManager.Instance.AddTextToChatOnServer("<color=yellow>[LMS] ➕ Queued Playlist: Scraped " + list.Count + " Suno tracks (MUSIC)</color>", -1);
				}
				if (clearQueue || MusicQueueIndex == -1)
				{
					MusicQueueIndex = count;
					PlayAudioFromUrl(GetOrCreateShipAudioSource("MUSIC"), MusicQueue[MusicQueueIndex], "MUSIC");
				}
				break;
			}
			case "BOOMBOX":
			{
				BoomboxItem nearest = ChatSyncPatch.GetNearestBoombox();
				if (!((Object)(object)nearest != (Object)null))
				{
					break;
				}
				if (clearQueue)
				{
					BoomboxQueue.Clear();
					BoomboxQueueTitles.Clear();
					BoomboxQueueIndex = 0;
				}
				int count = BoomboxQueue.Count;
				for (int i = 0; i < list.Count; i++)
				{
					BoomboxQueue.Add(list[i]);
					BoomboxQueueTitles.Add(list2[i]);
				}
				Log("[LMS] Queued " + list.Count + " tracks for BOOMBOX.");
				bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
				if (!flag && (Object)(object)StartOfRound.Instance != (Object)null)
				{
					flag = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
				}
				if (flag && (Object)(object)HUDManager.Instance != (Object)null)
				{
					HUDManager.Instance.AddTextToChatOnServer("<color=yellow>[LMS] ➕ Queued Playlist: Scraped " + list.Count + " Suno tracks (BOOMBOX)</color>", -1);
				}
				if (!clearQueue && BoomboxQueueIndex != -1)
				{
					break;
				}
				BoomboxQueueIndex = count;
				PlayAudioFromUrl(nearest.boomboxAudio, BoomboxQueue[BoomboxQueueIndex], "BOOMBOX", delegate(AudioClip clip)
				{
					nearest.musicAudios = (AudioClip[])(object)new AudioClip[1] { clip };
					((GrabbableObject)nearest).isBeingUsed = true;
					nearest.isPlayingMusic = true;
					IsProgrammaticActivation = true;
					try
					{
						((GrabbableObject)nearest).ItemActivate(true, true);
					}
					finally
					{
						IsProgrammaticActivation = false;
					}
				});
				break;
			}
			}
		}
		finally
		{
			((IDisposable)pageRequest)?.Dispose();
		}
	}

	public static void QueueSingleTrack(string url, string device, bool clearQueue)
	{
		string text = url;
		if (url.Contains("youtube.com") || url.Contains("youtu.be"))
		{
			text = "YouTube Video";
		}
		else if (url.Contains("suno.ai") || url.Contains("suno.com"))
		{
			Match match = Regex.Match(url, "([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})");
			text = ((!match.Success) ? "Suno Track" : ("Suno Song " + match.Groups[1].Value.Substring(0, 8)));
		}
		else
		{
			text = "Audio Stream";
		}
		switch (device)
		{
		case "SPEAKER":
		{
			if (clearQueue)
			{
				SpeakerQueue.Clear();
				SpeakerQueueTitles.Clear();
			}
			SpeakerQueue.Add(url);
			SpeakerQueueTitles.Add(text);
			if (clearQueue || SpeakerQueueIndex == -1)
			{
				SpeakerQueueIndex = SpeakerQueue.Count - 1;
				PlayAudioFromUrl(GetOrCreateShipAudioSource("SPEAKER"), url, "SPEAKER");
				break;
			}
			bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
			if (!flag && (Object)(object)StartOfRound.Instance != (Object)null)
			{
				flag = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
			}
			if (flag && (Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer("<color=yellow>[LMS] ➕ Queued: " + text + " (SPEAKER)</color>", -1);
			}
			break;
		}
		case "MUSIC":
		{
			if (clearQueue)
			{
				MusicQueue.Clear();
				MusicQueueTitles.Clear();
			}
			MusicQueue.Add(url);
			MusicQueueTitles.Add(text);
			if (clearQueue || MusicQueueIndex == -1)
			{
				MusicQueueIndex = MusicQueue.Count - 1;
				PlayAudioFromUrl(GetOrCreateShipAudioSource("MUSIC"), url, "MUSIC");
				break;
			}
			bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
			if (!flag && (Object)(object)StartOfRound.Instance != (Object)null)
			{
				flag = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
			}
			if (flag && (Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer("<color=yellow>[LMS] ➕ Queued: " + text + " (MUSIC)</color>", -1);
			}
			break;
		}
		case "BOOMBOX":
		{
			BoomboxItem nearest = ChatSyncPatch.GetNearestBoombox();
			if (!((Object)(object)nearest != (Object)null))
			{
				break;
			}
			if (clearQueue)
			{
				BoomboxQueue.Clear();
				BoomboxQueueTitles.Clear();
			}
			BoomboxQueue.Add(url);
			BoomboxQueueTitles.Add(text);
			if (clearQueue || BoomboxQueueIndex == -1)
			{
				BoomboxQueueIndex = BoomboxQueue.Count - 1;
				PlayAudioFromUrl(nearest.boomboxAudio, url, "BOOMBOX", delegate(AudioClip clip)
				{
					nearest.musicAudios = (AudioClip[])(object)new AudioClip[1] { clip };
					((GrabbableObject)nearest).isBeingUsed = true;
					nearest.isPlayingMusic = true;
					IsProgrammaticActivation = true;
					try
					{
						((GrabbableObject)nearest).ItemActivate(true, true);
					}
					finally
					{
						IsProgrammaticActivation = false;
					}
				});
			}
			else
			{
				bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
				if (!flag && (Object)(object)StartOfRound.Instance != (Object)null)
				{
					flag = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
				}
				if (flag && (Object)(object)HUDManager.Instance != (Object)null)
				{
					HUDManager.Instance.AddTextToChatOnServer("<color=yellow>[LMS] ➕ Queued: " + text + " (BOOMBOX)</color>", -1);
				}
			}
			break;
		}
		}
	}

	private static void UpdateQueueTitle(string url, string newTitle, string device)
	{
		switch (device)
		{
		case "SPEAKER":
		{
			for (int i = 0; i < SpeakerQueue.Count; i++)
			{
				if (SpeakerQueue[i] == url)
				{
					SpeakerQueueTitles[i] = newTitle;
				}
			}
			break;
		}
		case "MUSIC":
		{
			for (int i = 0; i < MusicQueue.Count; i++)
			{
				if (MusicQueue[i] == url)
				{
					MusicQueueTitles[i] = newTitle;
				}
			}
			break;
		}
		case "BOOMBOX":
		{
			for (int i = 0; i < BoomboxQueue.Count; i++)
			{
				if (BoomboxQueue[i] == url)
				{
					BoomboxQueueTitles[i] = newTitle;
				}
			}
			break;
		}
		}
	}

	private static IEnumerator TrackEndTrigger(AudioSource source, AudioClip clip, string device)
	{
		float startWaitTime = 0f;
		bool isPaused = false;
		while ((Object)(object)source != (Object)null && (Object)(object)source.clip == (Object)(object)clip && !source.isPlaying && startWaitTime < 3f)
		{
			switch (device)
			{
			case "SPEAKER":
				isPaused = IsSpeakerPaused;
				break;
			case "MUSIC":
				isPaused = IsMusicPaused;
				break;
			case "BOOMBOX":
				isPaused = IsBoomboxPaused;
				break;
			}
			if (isPaused)
			{
				break;
			}
			startWaitTime += 0.2f;
			yield return (object)new WaitForSeconds(0.2f);
		}
		while ((Object)(object)source != (Object)null && (Object)(object)source.clip == (Object)(object)clip)
		{
			switch (device)
			{
			case "SPEAKER":
				isPaused = IsSpeakerPaused;
				break;
			case "MUSIC":
				isPaused = IsMusicPaused;
				break;
			case "BOOMBOX":
				isPaused = IsBoomboxPaused;
				break;
			}
			if (source.isPlaying || isPaused)
			{
				yield return (object)new WaitForSeconds(1f);
				continue;
			}
			break;
		}
		if (!((Object)(object)source != (Object)null) || !((Object)(object)source.clip == (Object)(object)clip))
		{
			yield break;
		}
		switch (device)
		{
		case "SPEAKER":
			isPaused = IsSpeakerPaused;
			break;
		case "MUSIC":
			isPaused = IsMusicPaused;
			break;
		case "BOOMBOX":
			isPaused = IsBoomboxPaused;
			break;
		}
		if (source.isPlaying || isPaused)
		{
			yield break;
		}
		if (device == "SPEAKER" && SpeakerQueueIndex != -1)
		{
			if (SpeakerLoopMode == "track")
			{
				source.Play();
				((MonoBehaviour)Instance).StartCoroutine(TrackEndTrigger(source, clip, device));
			}
			else
			{
				Log("[LMS] Speaker track finished naturally. Advancing queue...");
				PlayNextSpeakerTrack();
			}
		}
		else if (device == "MUSIC" && MusicQueueIndex != -1)
		{
			if (MusicLoopMode == "track")
			{
				source.Play();
				((MonoBehaviour)Instance).StartCoroutine(TrackEndTrigger(source, clip, device));
			}
			else
			{
				Log("[LMS] Music track finished naturally. Advancing queue...");
				PlayNextMusicTrack();
			}
		}
		else if (device == "BOOMBOX" && BoomboxQueueIndex != -1)
		{
			if (BoomboxLoopMode == "track")
			{
				source.Play();
				((MonoBehaviour)Instance).StartCoroutine(TrackEndTrigger(source, clip, device));
			}
			else
			{
				Log("[LMS] Boombox track finished naturally. Advancing queue...");
				PlayNextBoomboxTrack();
			}
		}
	}

	public static void PlayNextSpeakerTrack()
	{
		if (SpeakerQueue.Count == 0)
		{
			return;
		}
		AudioSource orCreateShipAudioSource = GetOrCreateShipAudioSource("SPEAKER");
		if ((Object)(object)orCreateShipAudioSource != (Object)null)
		{
			orCreateShipAudioSource.Stop();
			orCreateShipAudioSource.clip = null;
		}
		SpeakerQueueIndex++;
		if (SpeakerQueueIndex >= SpeakerQueue.Count)
		{
			if (!(SpeakerLoopMode == "queue"))
			{
				SpeakerQueueIndex = -1;
				Log("[LMS] Speaker reached end of queue.");
				return;
			}
			SpeakerQueueIndex = 0;
		}
		string text = SpeakerQueue[SpeakerQueueIndex];
		Log("[LMS] Playing next speaker track in queue (Index " + SpeakerQueueIndex + "): " + text);
		PlayAudioFromUrl(orCreateShipAudioSource, text, "SPEAKER");
	}

	public static void PlayNextMusicTrack()
	{
		if (MusicQueue.Count == 0)
		{
			return;
		}
		AudioSource orCreateShipAudioSource = GetOrCreateShipAudioSource("MUSIC");
		if ((Object)(object)orCreateShipAudioSource != (Object)null)
		{
			orCreateShipAudioSource.Stop();
			orCreateShipAudioSource.clip = null;
		}
		MusicQueueIndex++;
		if (MusicQueueIndex >= MusicQueue.Count)
		{
			if (!(MusicLoopMode == "queue"))
			{
				MusicQueueIndex = -1;
				Log("[LMS] Global Music reached end of queue.");
				return;
			}
			MusicQueueIndex = 0;
		}
		string text = MusicQueue[MusicQueueIndex];
		Log("[LMS] Playing next music track in queue (Index " + MusicQueueIndex + "): " + text);
		PlayAudioFromUrl(orCreateShipAudioSource, text, "MUSIC");
	}

	public static void PlayNextBoomboxTrack()
	{
		BoomboxItem nearest = ChatSyncPatch.GetNearestBoombox();
		if ((Object)(object)nearest == (Object)null || BoomboxQueue.Count == 0)
		{
			return;
		}
		if ((Object)(object)nearest.boomboxAudio != (Object)null)
		{
			nearest.boomboxAudio.Stop();
			nearest.boomboxAudio.clip = null;
		}
		BoomboxQueueIndex++;
		if (BoomboxQueueIndex >= BoomboxQueue.Count)
		{
			if (!(BoomboxLoopMode == "queue"))
			{
				BoomboxQueueIndex = -1;
				((GrabbableObject)nearest).isBeingUsed = false;
				nearest.isPlayingMusic = false;
				if (OriginalBoomboxClips != null)
				{
					nearest.musicAudios = OriginalBoomboxClips;
				}
				Log("[LMS] Boombox reached end of queue.");
				return;
			}
			BoomboxQueueIndex = 0;
		}
		string text = BoomboxQueue[BoomboxQueueIndex];
		Log("[LMS] Playing next boombox track in queue (Index " + BoomboxQueueIndex + "): " + text);
		PlayAudioFromUrl(nearest.boomboxAudio, text, "BOOMBOX", delegate(AudioClip clip)
		{
			nearest.musicAudios = (AudioClip[])(object)new AudioClip[1] { clip };
			((GrabbableObject)nearest).isBeingUsed = true;
			nearest.isPlayingMusic = true;
			IsProgrammaticActivation = true;
			try
			{
				((GrabbableObject)nearest).ItemActivate(true, true);
			}
			finally
			{
				IsProgrammaticActivation = false;
			}
		});
	}

	public static string GetQueueDisplayString(string device)
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("\n=========================================");
		stringBuilder.AppendLine("         LETHAL MUSIC SYNC QUEUE         ");
		stringBuilder.AppendLine("=========================================\n");
		List<string> list = ((device == "SPEAKER") ? SpeakerQueue : ((device == "MUSIC") ? MusicQueue : BoomboxQueue));
		List<string> list2 = ((device == "SPEAKER") ? SpeakerQueueTitles : ((device == "MUSIC") ? MusicQueueTitles : BoomboxQueueTitles));
		int num = ((device == "SPEAKER") ? SpeakerQueueIndex : ((device == "MUSIC") ? MusicQueueIndex : BoomboxQueueIndex));
		string text = ((device == "SPEAKER") ? SpeakerLoopMode : ((device == "MUSIC") ? MusicLoopMode : BoomboxLoopMode));
		stringBuilder.AppendLine("DEVICE:    " + device);
		stringBuilder.AppendLine("LOOP MODE: " + text.ToUpper());
		stringBuilder.AppendLine("TRACKS:    " + list.Count + "\n");
		if (list.Count == 0)
		{
			stringBuilder.AppendLine("[Queue is currently empty.]");
			stringBuilder.AppendLine("Use: play [URL] or queue [URL] to add songs!");
		}
		else
		{
			for (int i = 0; i < list.Count; i++)
			{
				string text2 = "  ";
				string text3 = "";
				if (i == num)
				{
					text2 = "> ";
					text3 = " [PLAYING]";
				}
				stringBuilder.AppendLine(text2 + (i + 1) + ". " + list2[i] + text3);
			}
		}
		stringBuilder.AppendLine("\n=========================================\n");
		return stringBuilder.ToString();
	}
}
[HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")]
public static class TerminalPatch
{
	[HarmonyPrefix]
	public static bool Prefix(Terminal __instance, ref TerminalNode __result)
	{
		string text = "";
		try
		{
			if ((Object)(object)__instance.screenText != (Object)null && !string.IsNullOrEmpty(__instance.screenText.text))
			{
				string text2 = __instance.screenText.text;
				text = ((text2.Length < __instance.textAdded) ? text2.Trim() : text2.Substring(text2.Length - __instance.textAdded).Trim());
				if (text.StartsWith(">"))
				{
					text = text.Substring(1).Trim();
				}
			}
		}
		catch (Exception ex)
		{
			LethalMusicSyncPlugin.Log("Error getting terminal text: " + ex.Message);
		}
		if (string.IsNullOrEmpty(text))
		{
			return true;
		}
		string text3 = text.Trim().ToLower();
		if (text3.StartsWith("boombox ") || text3.StartsWith("speaker ") || text3.StartsWith("music "))
		{
			__result = HandleTerminalCommand(__instance, text3);
			return false;
		}
		return true;
	}

	private static TerminalNode HandleTerminalCommand(Terminal terminal, string command)
	{
		TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
		val.clearPreviousText = true;
		string[] array = command.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length < 2)
		{
			val.displayText = "LethalMusicSync - Invalid Command Format.\nUse: boombox/speaker/music play [URL], queue [URL], skip, loop, list, clear, stop.\n\n";
			return val;
		}
		string text = array[0];
		string text2 = array[1];
		switch (text2)
		{
		case "queue":
			if (array.Length < 3)
			{
				break;
			}
			goto default;
		default:
			if (!LethalMusicSyncPlugin.CanLocalPlayerControl())
			{
				val.displayText = "\n=========================================\n         LETHAL MUSIC SYNC (v1.0)        \n=========================================\n\nDEVICE:  " + text.ToUpper() + "\n[ERROR] ONLY THE HOST IS PERMITTED TO CONTROL MUSIC IN THIS LOBBY.\n=========================================\n\n";
				return val;
			}
			break;
		case "list":
		case "qlist":
			break;
		}
		int result;
		if (text2 == "play" || text2 == "queue" || text2 == "q")
		{
			if (array.Length < 3)
			{
				val.displayText = "Usage: " + text + " " + text2 + " [URL/Search Query]\n\n";
				return val;
			}
			string text3 = command.Substring(command.IndexOf(text2) + text2.Length).Trim();
			if (!text3.StartsWith("http://") && !text3.StartsWith("https://"))
			{
				LethalMusicSyncPlugin.SearchAndQueueYoutube(text3, text, text2, viaGui: false);
				if ((Object)(object)HUDManager.Instance != (Object)null)
				{
					HUDManager.Instance.AddTextToChatOnServer("<color=orange>[LMS] \ud83d\udd0d Searching YouTube for: \"" + text3 + "\" (" + text.ToUpper() + ")...</color>", -1);
				}
				val.displayText = "\n=========================================\n         LETHAL MUSIC SYNC (v1.0)        \n=========================================\n\nDEVICE:  " + text.ToUpper() + "\nACTION:  " + text2.ToUpper() + "\nSEARCH:  " + text3 + "\n\n[Searching YouTube asynchronously...]\n=========================================\n\n";
			}
			else
			{
				string text4 = "[LMS_SYNC] " + text.ToUpper() + " " + text2.ToUpper() + " " + text3;
				HUDManager.Instance.AddTextToChatOnServer(text4, -1);
				string text5 = ((text2 == "play") ? "Initiating Stream..." : "Queueing Track...");
				val.displayText = "\n=========================================\n         LETHAL MUSIC SYNC (v1.0)        \n=========================================\n\nDEVICE:  " + text.ToUpper() + "\nACTION:  " + text5 + "\nURL:     " + text3 + "\n\n[Broadcasting network sync...]\n=========================================\n\n";
			}
		}
		else if (text2 == "stop")
		{
			string text4 = "[LMS_SYNC] " + text.ToUpper() + " STOP";
			HUDManager.Instance.AddTextToChatOnServer(text4, -1);
			val.displayText = "Stopping stream...\n\n";
		}
		else if (text2 == "skip" || text2 == "s")
		{
			if (array.Length >= 3 && array[2] == "to")
			{
				if (array.Length < 4)
				{
					val.displayText = "Usage: " + text + " skip to [index]\n\n";
					return val;
				}
				string text6 = array[3];
				if (!int.TryParse(text6, out result))
				{
					val.displayText = "[ERROR] Index '" + text6 + "' must be a valid integer.\n\n";
					return val;
				}
				int num = ((text == "speaker") ? LethalMusicSyncPlugin.SpeakerQueue.Count : ((text == "music") ? LethalMusicSyncPlugin.MusicQueue.Count : LethalMusicSyncPlugin.BoomboxQueue.Count));
				if (result < 1 || result > num)
				{
					val.displayText = "[ERROR] Index " + text6 + " is out of bounds. The " + text.ToUpper() + " queue has " + num + " track(s).\n\n";
					return val;
				}
				string text4 = "[LMS_SYNC] " + text.ToUpper() + " SKIPTO " + text6;
				HUDManager.Instance.AddTextToChatOnServer(text4, -1);
				val.displayText = "Skipping to track index " + text6 + "...\n\n";
			}
			else if (array.Length >= 3)
			{
				string text6 = array[2];
				if (int.TryParse(text6, out result))
				{
					int num = ((text == "speaker") ? LethalMusicSyncPlugin.SpeakerQueue.Count : ((text == "music") ? LethalMusicSyncPlugin.MusicQueue.Count : LethalMusicSyncPlugin.BoomboxQueue.Count));
					if (result < 1 || result > num)
					{
						val.displayText = "[ERROR] Index " + text6 + " is out of bounds. The " + text.ToUpper() + " queue has " + num + " track(s).\n\n";
						return val;
					}
					string text4 = "[LMS_SYNC] " + text.ToUpper() + " SKIPTO " + text6;
					HUDManager.Instance.AddTextToChatOnServer(text4, -1);
					val.displayText = "Skipping to track index " + text6 + "...\n\n";
				}
				else
				{
					string text4 = "[LMS_SYNC] " + text.ToUpper() + " SKIP";
					HUDManager.Instance.AddTextToChatOnServer(text4, -1);
					val.displayText = "Skipping current track...\n\n";
				}
			}
			else
			{
				string text4 = "[LMS_SYNC] " + text.ToUpper() + " SKIP";
				HUDManager.Instance.AddTextToChatOnServer(text4, -1);
				val.displayText = "Skipping current track...\n\n";
			}
		}
		else if (text2 == "skipto" || text2 == "goto")
		{
			if (array.Length < 3)
			{
				val.displayText = "Usage: " + text + " skipto [index]\n\n";
				return val;
			}
			string text6 = array[2];
			if (!int.TryParse(text6, out result))
			{
				val.displayText = "[ERROR] Index '" + text6 + "' must be a valid integer.\n\n";
				return val;
			}
			int num = ((text == "speaker") ? LethalMusicSyncPlugin.SpeakerQueue.Count : ((text == "music") ? LethalMusicSyncPlugin.MusicQueue.Count : LethalMusicSyncPlugin.BoomboxQueue.Count));
			if (result < 1 || result > num)
			{
				val.displayText = "[ERROR] Index " + text6 + " is out of bounds. The " + text.ToUpper() + " queue has " + num + " track(s).\n\n";
				return val;
			}
			string text4 = "[LMS_SYNC] " + text.ToUpper() + " SKIPTO " + text6;
			HUDManager.Instance.AddTextToChatOnServer(text4, -1);
			val.displayText = "Skipping to track index " + text6 + "...\n\n";
		}
		else
		{
			switch (text2)
			{
			case "loop":
			{
				string text8 = "off";
				if (array.Length >= 3)
				{
					text8 = array[2].ToLower();
				}
				else
				{
					string text9 = "off";
					text9 = ((text == "speaker") ? LethalMusicSyncPlugin.SpeakerLoopMode : ((!(text == "music")) ? LethalMusicSyncPlugin.BoomboxLoopMode : LethalMusicSyncPlugin.MusicLoopMode));
					text8 = ((text9 == "off") ? "track" : ((!(text9 == "track")) ? "off" : "queue"));
				}
				if (text8 != "off" && text8 != "track" && text8 != "queue")
				{
					val.displayText = "Invalid loop mode. Choose: track, queue, off.\n\n";
					return val;
				}
				string text4 = "[LMS_SYNC] " + text.ToUpper() + " LOOP " + text8;
				HUDManager.Instance.AddTextToChatOnServer(text4, -1);
				val.displayText = "Setting loop mode to '" + text8.ToUpper() + "'...\n\n";
				break;
			}
			case "clear":
			{
				string text4 = "[LMS_SYNC] " + text.ToUpper() + " CLEAR";
				HUDManager.Instance.AddTextToChatOnServer(text4, -1);
				val.displayText = "Clearing current queue...\n\n";
				break;
			}
			default:
				if (!(text2 == "queue"))
				{
					if (text2 == "volume" || text2 == "vol")
					{
						if (array.Length < 3)
						{
							val.displayText = "Usage: " + text + " volume [0-100]\n\n";
							return val;
						}
						string text7 = array[2];
						string text4 = "[LMS_SYNC] " + text.ToUpper() + " VOLUME " + text7;
						HUDManager.Instance.AddTextToChatOnServer(text4, -1);
						val.displayText = "Adjusting volume to " + text7 + "%\n\n";
					}
					else
					{
						val.displayText = "Unknown command. Options: play, queue, list, skip, skipto, loop, clear, stop, volume.\n\n";
					}
					break;
				}
				goto case "list";
			case "list":
			case "qlist":
				val.displayText = LethalMusicSyncPlugin.GetQueueDisplayString(text.ToUpper());
				break;
			}
		}
		return val;
	}
}
[HarmonyPatch(typeof(HUDManager), "AddTextToChatOnServer")]
public static class ChatSyncPatch
{
	[HarmonyPrefix]
	public static bool Prefix(ref string chatMessage, int playerId)
	{
		if (string.IsNullOrEmpty(chatMessage))
		{
			return true;
		}
		if (chatMessage.StartsWith("[LMS_SYNC]"))
		{
			if (LethalMusicSyncPlugin.MusicControlPermission != null && LethalMusicSyncPlugin.MusicControlPermission.Value.ToLower() == "host" && playerId != 0 && playerId != -1)
			{
				LethalMusicSyncPlugin.Log("[LMS] Ignored sync command from non-host player (ID: " + playerId + "). Permissions locked to Host.");
				return false;
			}
			try
			{
				string cmd = chatMessage.Substring(10).Trim();
				ExecuteSyncCommand(cmd);
			}
			catch (Exception ex)
			{
				LethalMusicSyncPlugin.Log("Error executing sync command: " + ex.Message);
			}
			return false;
		}
		return true;
	}

	private static void ExecuteSyncCommand(string cmd)
	{
		string[] array = cmd.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length < 2)
		{
			return;
		}
		string text = array[0];
		string text2 = array[1];
		if (text2 == "PLAY" || text2 == "QUEUE")
		{
			if (array.Length < 3)
			{
				return;
			}
			string text3 = cmd.Substring(cmd.IndexOf(text2) + text2.Length).Trim();
			bool clearQueue = text2 == "PLAY";
			if (text == "BOOMBOX")
			{
				LethalMusicSyncPlugin.IsCustomBoomboxPlaying = true;
				BoomboxItem nearestBoombox = GetNearestBoombox();
				if ((Object)(object)nearestBoombox != (Object)null && LethalMusicSyncPlugin.OriginalBoomboxClips == null)
				{
					LethalMusicSyncPlugin.OriginalBoomboxClips = nearestBoombox.musicAudios;
				}
			}
			if (text3.Contains("suno.com/playlist") || text3.Contains("suno.com/p/"))
			{
				LethalMusicSyncPlugin.ResolveAndQueueSunoPlaylist(text3, text, clearQueue);
			}
			else
			{
				LethalMusicSyncPlugin.QueueSingleTrack(text3, text, clearQueue);
			}
			return;
		}
		switch (text2)
		{
		case "STOP":
		{
			bool flag2 = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
			if (!flag2 && (Object)(object)StartOfRound.Instance != (Object)null)
			{
				flag2 = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
			}
			if (flag2 && (Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer("<color=red>[LMS] \ud83d\uded1 Stopping playback (" + text + ")...</color>", -1);
			}
			switch (text)
			{
			case "SPEAKER":
			{
				AudioSource orCreateShipAudioSource = LethalMusicSyncPlugin.GetOrCreateShipAudioSource("SPEAKER");
				if ((Object)(object)orCreateShipAudioSource != (Object)null)
				{
					orCreateShipAudioSource.Stop();
				}
				LethalMusicSyncPlugin.SpeakerQueueIndex = -1;
				break;
			}
			case "MUSIC":
			{
				AudioSource orCreateShipAudioSource2 = LethalMusicSyncPlugin.GetOrCreateShipAudioSource("MUSIC");
				if ((Object)(object)orCreateShipAudioSource2 != (Object)null)
				{
					orCreateShipAudioSource2.Stop();
				}
				LethalMusicSyncPlugin.MusicQueueIndex = -1;
				break;
			}
			case "BOOMBOX":
			{
				LethalMusicSyncPlugin.IsCustomBoomboxPlaying = false;
				LethalMusicSyncPlugin.IsBoomboxPaused = false;
				LethalMusicSyncPlugin.BoomboxQueueIndex = -1;
				BoomboxItem nearestBoombox = GetNearestBoombox();
				if ((Object)(object)nearestBoombox != (Object)null)
				{
					((GrabbableObject)nearestBoombox).isBeingUsed = false;
					nearestBoombox.isPlayingMusic = false;
					if ((Object)(object)nearestBoombox.boomboxAudio != (Object)null)
					{
						nearestBoombox.boomboxAudio.Stop();
					}
					LethalMusicSyncPlugin.IsProgrammaticActivation = true;
					try
					{
						((GrabbableObject)nearestBoombox).ItemActivate(false, false);
					}
					finally
					{
						LethalMusicSyncPlugin.IsProgrammaticActivation = false;
					}
					if (LethalMusicSyncPlugin.OriginalBoomboxClips != null)
					{
						nearestBoombox.musicAudios = LethalMusicSyncPlugin.OriginalBoomboxClips;
					}
				}
				break;
			}
			}
			break;
		}
		case "PAUSE":
			if (text == "BOOMBOX")
			{
				BoomboxItem nearestBoombox = GetNearestBoombox();
				if ((Object)(object)nearestBoombox != (Object)null)
				{
					nearestBoombox.isPlayingMusic = false;
					nearestBoombox.boomboxAudio.Pause();
					LethalMusicSyncPlugin.IsCustomBoomboxPlaying = false;
					LethalMusicSyncPlugin.IsBoomboxPaused = true;
				}
			}
			break;
		case "RESUME":
		{
			if (!(text == "BOOMBOX"))
			{
				break;
			}
			BoomboxItem nearestBoombox = GetNearestBoombox();
			if ((Object)(object)nearestBoombox != (Object)null)
			{
				nearestBoombox.isPlayingMusic = true;
				nearestBoombox.boomboxAudio.UnPause();
				LethalMusicSyncPlugin.IsCustomBoomboxPlaying = true;
				LethalMusicSyncPlugin.IsBoomboxPaused = false;
				LethalMusicSyncPlugin.IsProgrammaticActivation = true;
				try
				{
					((GrabbableObject)nearestBoombox).ItemActivate(true, true);
					break;
				}
				finally
				{
					LethalMusicSyncPlugin.IsProgrammaticActivation = false;
				}
			}
			break;
		}
		case "SKIP":
		{
			bool flag2 = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
			if (!flag2 && (Object)(object)StartOfRound.Instance != (Object)null)
			{
				flag2 = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
			}
			if (flag2 && (Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer("<color=cyan>[LMS] ⏭\ufe0f Skipping current track (" + text + ")...</color>", -1);
			}
			switch (text)
			{
			case "SPEAKER":
				LethalMusicSyncPlugin.PlayNextSpeakerTrack();
				break;
			case "MUSIC":
				LethalMusicSyncPlugin.PlayNextMusicTrack();
				break;
			case "BOOMBOX":
				LethalMusicSyncPlugin.PlayNextBoomboxTrack();
				break;
			}
			break;
		}
		case "SKIPTO":
		{
			if (array.Length < 3 || !int.TryParse(array[2], out var result2))
			{
				break;
			}
			bool flag2 = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
			if (!flag2 && (Object)(object)StartOfRound.Instance != (Object)null)
			{
				flag2 = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
			}
			if (flag2 && (Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer("<color=cyan>[LMS] ⏭\ufe0f Skipping to track #" + result2 + " (" + text + ")...</color>", -1);
			}
			result2--;
			switch (text)
			{
			case "SPEAKER":
				if (result2 >= 0 && result2 < LethalMusicSyncPlugin.SpeakerQueue.Count)
				{
					LethalMusicSyncPlugin.Log("[LMS] Execute SKIPTO for SPEAKER index: " + (result2 + 1));
					LethalMusicSyncPlugin.SpeakerQueueIndex = result2;
					AudioSource orCreateShipAudioSource3 = LethalMusicSyncPlugin.GetOrCreateShipAudioSource("SPEAKER");
					if ((Object)(object)orCreateShipAudioSource3 != (Object)null)
					{
						orCreateShipAudioSource3.Stop();
						orCreateShipAudioSource3.clip = null;
					}
					LethalMusicSyncPlugin.PlayAudioFromUrl(orCreateShipAudioSource3, LethalMusicSyncPlugin.SpeakerQueue[LethalMusicSyncPlugin.SpeakerQueueIndex], "SPEAKER");
				}
				else
				{
					LethalMusicSyncPlugin.Log("[LMS] SKIPTO SPEAKER failed: Index " + (result2 + 1) + " out of bounds (Count: " + LethalMusicSyncPlugin.SpeakerQueue.Count + ")");
				}
				break;
			case "MUSIC":
				if (result2 >= 0 && result2 < LethalMusicSyncPlugin.MusicQueue.Count)
				{
					LethalMusicSyncPlugin.Log("[LMS] Execute SKIPTO for MUSIC index: " + (result2 + 1));
					LethalMusicSyncPlugin.MusicQueueIndex = result2;
					AudioSource orCreateShipAudioSource3 = LethalMusicSyncPlugin.GetOrCreateShipAudioSource("MUSIC");
					if ((Object)(object)orCreateShipAudioSource3 != (Object)null)
					{
						orCreateShipAudioSource3.Stop();
						orCreateShipAudioSource3.clip = null;
					}
					LethalMusicSyncPlugin.PlayAudioFromUrl(orCreateShipAudioSource3, LethalMusicSyncPlugin.MusicQueue[LethalMusicSyncPlugin.MusicQueueIndex], "MUSIC");
				}
				else
				{
					LethalMusicSyncPlugin.Log("[LMS] SKIPTO MUSIC failed: Index " + (result2 + 1) + " out of bounds (Count: " + LethalMusicSyncPlugin.MusicQueue.Count + ")");
				}
				break;
			case "BOOMBOX":
				if (result2 >= 0 && result2 < LethalMusicSyncPlugin.BoomboxQueue.Count)
				{
					LethalMusicSyncPlugin.Log("[LMS] Execute SKIPTO for BOOMBOX index: " + (result2 + 1));
					LethalMusicSyncPlugin.BoomboxQueueIndex = result2;
					BoomboxItem nearest = GetNearestBoombox();
					if ((Object)(object)nearest != (Object)null)
					{
						if ((Object)(object)nearest.boomboxAudio != (Object)null)
						{
							nearest.boomboxAudio.Stop();
							nearest.boomboxAudio.clip = null;
						}
						LethalMusicSyncPlugin.PlayAudioFromUrl(nearest.boomboxAudio, LethalMusicSyncPlugin.BoomboxQueue[LethalMusicSyncPlugin.BoomboxQueueIndex], "BOOMBOX", delegate(AudioClip clip)
						{
							nearest.musicAudios = (AudioClip[])(object)new AudioClip[1] { clip };
							((GrabbableObject)nearest).isBeingUsed = true;
							nearest.isPlayingMusic = true;
							LethalMusicSyncPlugin.IsProgrammaticActivation = true;
							try
							{
								((GrabbableObject)nearest).ItemActivate(true, true);
							}
							finally
							{
								LethalMusicSyncPlugin.IsProgrammaticActivation = false;
							}
						});
					}
					else
					{
						LethalMusicSyncPlugin.Log("[LMS] SKIPTO BOOMBOX failed: No boombox nearby found!");
					}
				}
				else
				{
					LethalMusicSyncPlugin.Log("[LMS] SKIPTO BOOMBOX failed: Index " + (result2 + 1) + " out of bounds (Count: " + LethalMusicSyncPlugin.BoomboxQueue.Count + ")");
				}
				break;
			}
			break;
		}
		case "LOOP":
		{
			if (array.Length < 3)
			{
				break;
			}
			string text4 = array[2].ToLower();
			bool flag2 = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
			if (!flag2 && (Object)(object)StartOfRound.Instance != (Object)null)
			{
				flag2 = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
			}
			if (flag2 && (Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer("<color=pink>[LMS] \ud83d\udd01 Loop mode set to: " + text4.ToUpper() + " (" + text + ")</color>", -1);
			}
			switch (text)
			{
			case "SPEAKER":
				LethalMusicSyncPlugin.SpeakerLoopMode = text4;
				if ((Object)(object)LethalMusicSyncPlugin.CustomShipSpeaker != (Object)null)
				{
					LethalMusicSyncPlugin.CustomShipSpeaker.loop = text4 == "track";
				}
				break;
			case "MUSIC":
				LethalMusicSyncPlugin.MusicLoopMode = text4;
				if ((Object)(object)LethalMusicSyncPlugin.CustomShipMusic != (Object)null)
				{
					LethalMusicSyncPlugin.CustomShipMusic.loop = text4 == "track";
				}
				break;
			case "BOOMBOX":
			{
				LethalMusicSyncPlugin.BoomboxLoopMode = text4;
				BoomboxItem nearestBoombox = GetNearestBoombox();
				if ((Object)(object)nearestBoombox != (Object)null && (Object)(object)nearestBoombox.boomboxAudio != (Object)null)
				{
					nearestBoombox.boomboxAudio.loop = text4 == "track";
				}
				break;
			}
			}
			break;
		}
		case "CLEAR":
		{
			bool flag2 = (Object)(object)GameNetworkManager.Instance != (Object)null && GameNetworkManager.Instance.isHostingGame;
			if (!flag2 && (Object)(object)StartOfRound.Instance != (Object)null)
			{
				flag2 = ((NetworkBehaviour)StartOfRound.Instance).IsServer;
			}
			if (flag2 && (Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.AddTextToChatOnServer("<color=red>[LMS] \ud83e\uddf9 Queue cleared (" + text + ")...</color>", -1);
			}
			switch (text)
			{
			case "SPEAKER":
				LethalMusicSyncPlugin.SpeakerQueue.Clear();
				LethalMusicSyncPlugin.SpeakerQueueTitles.Clear();
				LethalMusicSyncPlugin.SpeakerQueueIndex = -1;
				if ((Object)(object)LethalMusicSyncPlugin.CustomShipSpeaker != (Object)null)
				{
					LethalMusicSyncPlugin.CustomShipSpeaker.Stop();
				}
				break;
			case "MUSIC":
				LethalMusicSyncPlugin.MusicQueue.Clear();
				LethalMusicSyncPlugin.MusicQueueTitles.Clear();
				LethalMusicSyncPlugin.MusicQueueIndex = -1;
				if ((Object)(object)LethalMusicSyncPlugin.CustomShipMusic != (Object)null)
				{
					LethalMusicSyncPlugin.CustomShipMusic.Stop();
				}
				break;
			case "BOOMBOX":
			{
				LethalMusicSyncPlugin.BoomboxQueue.Clear();
				LethalMusicSyncPlugin.BoomboxQueueTitles.Clear();
				LethalMusicSyncPlugin.IsBoomboxPaused = false;
				LethalMusicSyncPlugin.BoomboxQueueIndex = -1;
				BoomboxItem nearestBoombox = GetNearestBoombox();
				if ((Object)(object)nearestBoombox != (Object)null)
				{
					((GrabbableObject)nearestBoombox).isBeingUsed = false;
					nearestBoombox.isPlayingMusic = false;
					if ((Object)(object)nearestBoombox.boomboxAudio != (Object)null)
					{
						nearestBoombox.boomboxAudio.Stop();
					}
					LethalMusicSyncPlugin.IsProgrammaticActivation = true;
					try
					{
						((GrabbableObject)nearestBoombox).ItemActivate(false, false);
					}
					finally
					{
						LethalMusicSyncPlugin.IsProgrammaticActivation = false;
					}
					if (LethalMusicSyncPlugin.OriginalBoomboxClips != null)
					{
						nearestBoombox.musicAudios = LethalMusicSyncPlugin.OriginalBoomboxClips;
					}
				}
				break;
			}
			}
			break;
		}
		case "VOLUME":
		{
			if (array.Length < 3 || !float.TryParse(array[2], out var result))
			{
				break;
			}
			switch (text)
			{
			case "SPEAKER":
			{
				LethalMusicSyncPlugin.SpeakerVolumeSetting = result / 100f;
				AudioSource orCreateShipAudioSource = LethalMusicSyncPlugin.GetOrCreateShipAudioSource("SPEAKER");
				PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
				bool flag = false;
				if ((Object)(object)val != (Object)null)
				{
					flag = ((!val.isPlayerDead || !((Object)(object)val.spectatedPlayerScript != (Object)null)) ? val.isInsideFactory : val.spectatedPlayerScript.isInsideFactory);
				}
				if ((Object)(object)orCreateShipAudioSource != (Object)null && !flag)
				{
					orCreateShipAudioSource.volume = LethalMusicSyncPlugin.SpeakerVolumeSetting;
				}
				break;
			}
			case "MUSIC":
			{
				LethalMusicSyncPlugin.MusicVolumeSetting = result / 100f;
				AudioSource orCreateShipAudioSource2 = LethalMusicSyncPlugin.GetOrCreateShipAudioSource("MUSIC");
				PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
				bool flag = false;
				if ((Object)(object)val != (Object)null)
				{
					flag = ((!val.isPlayerDead || !((Object)(object)val.spectatedPlayerScript != (Object)null)) ? val.isInsideFactory : val.spectatedPlayerScript.isInsideFactory);
				}
				if ((Object)(object)orCreateShipAudioSource2 != (Object)null && !flag)
				{
					orCreateShipAudioSource2.volume = LethalMusicSyncPlugin.MusicVolumeSetting;
				}
				break;
			}
			case "BOOMBOX":
			{
				BoomboxItem nearestBoombox = GetNearestBoombox();
				if ((Object)(object)nearestBoombox != (Object)null)
				{
					nearestBoombox.boomboxAudio.volume = result / 100f;
				}
				break;
			}
			}
			break;
		}
		}
	}

	public static BoomboxItem GetNearestBoombox()
	{
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		BoomboxItem[] array = Object.FindObjectsOfType<BoomboxItem>();
		if (array == null || array.Length == 0)
		{
			return null;
		}
		PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
		BoomboxItem[] array2;
		if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).transform != (Object)null)
		{
			GrabbableObject currentlyHeldObjectServer = val.currentlyHeldObjectServer;
			BoomboxItem val2 = (BoomboxItem)(object)((currentlyHeldObjectServer is BoomboxItem) ? currentlyHeldObjectServer : null);
			if ((Object)(object)val2 != (Object)null && (Object)(object)((Component)val2).transform != (Object)null)
			{
				return val2;
			}
			BoomboxItem result = null;
			float num = float.MaxValue;
			array2 = array;
			foreach (BoomboxItem val3 in array2)
			{
				if (!((Object)(object)val3 == (Object)null) && !((Object)(object)((Component)val3).gameObject == (Object)null) && !((Object)(object)((Component)val3).transform == (Object)null))
				{
					float num2 = Vector3.Distance(((Component)val).transform.position, ((Component)val3).transform.position);
					if (num2 < num)
					{
						num = num2;
						result = val3;
					}
				}
			}
			return result;
		}
		array2 = array;
		foreach (BoomboxItem val3 in array2)
		{
			if ((Object)(object)val3 != (Object)null && (Object)(object)((Component)val3).gameObject != (Object)null && (Object)(object)((Component)val3).transform != (Object)null)
			{
				return val3;
			}
		}
		return null;
	}
}
public static class BoomboxPocketHelper
{
	public static void ProcessPocket(GrabbableObject item)
	{
		BoomboxItem val = (BoomboxItem)(object)((item is BoomboxItem) ? item : null);
		if ((Object)(object)val != (Object)null && LethalMusicSyncPlugin.BoomboxQueueIndex != -1)
		{
			LethalMusicSyncPlugin.Log("[LMS] Intercepted Boombox PocketItem (" + ((object)item).GetType().Name + "). Pausing stream to preserve track time...");
			((GrabbableObject)val).isPocketed = true;
			((GrabbableObject)val).EnableItemMeshes(false);
			if ((Object)(object)((GrabbableObject)val).playerHeldBy != (Object)null)
			{
				((GrabbableObject)val).parentObject = ((GrabbableObject)val).playerHeldBy.localItemHolder;
			}
			((GrabbableObject)val).isBeingUsed = false;
			val.isPlayingMusic = false;
			val.boomboxAudio.Pause();
			LethalMusicSyncPlugin.IsCustomBoomboxPlaying = false;
			LethalMusicSyncPlugin.IsBoomboxPaused = true;
			PlayerControllerB val2 = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if ((Object)(object)val2 != (Object)null && (Object)(object)((GrabbableObject)val).playerHeldBy == (Object)(object)val2)
			{
				LethalMusicSyncPlugin.pendingChatBroadcasts.Enqueue("[LMS_SYNC] BOOMBOX PAUSE");
			}
		}
	}
}
[HarmonyPatch(typeof(GrabbableObject), "PocketItem")]
public static class GrabbablePocketPatch
{
	[HarmonyPrefix]
	public static bool Prefix(GrabbableObject __instance)
	{
		if (__instance is BoomboxItem)
		{
			BoomboxPocketHelper.ProcessPocket(__instance);
			return false;
		}
		return true;
	}
}
[HarmonyPatch(typeof(GrabbableObject), "DiscardItem")]
public static class BoomboxDiscardPatch
{
	[HarmonyPrefix]
	public static void Prefix(GrabbableObject __instance)
	{
		BoomboxItem val = (BoomboxItem)(object)((__instance is BoomboxItem) ? __instance : null);
		if ((Object)(object)val != (Object)null && LethalMusicSyncPlugin.BoomboxQueueIndex != -1)
		{
			LethalMusicSyncPlugin.IsProgrammaticActivation = true;
			LethalMusicSyncPlugin.Log("[LMS] Boombox dropped — blocking discard-triggered ItemActivate.");
		}
	}

	[HarmonyPostfix]
	public static void Postfix(GrabbableObject __instance)
	{
		BoomboxItem val = (BoomboxItem)(object)((__instance is BoomboxItem) ? __instance : null);
		if (!((Object)(object)val != (Object)null) || LethalMusicSyncPlugin.BoomboxQueueIndex == -1)
		{
			return;
		}
		LethalMusicSyncPlugin.IsProgrammaticActivation = false;
		if (LethalMusicSyncPlugin.IsBoomboxPaused)
		{
			((GrabbableObject)val).isBeingUsed = false;
			val.isPlayingMusic = false;
			if ((Object)(object)val.boomboxAudio != (Object)null)
			{
				val.boomboxAudio.Pause();
			}
			LethalMusicSyncPlugin.Log("[LMS] Boombox dropped while paused — ensuring it remains paused.");
		}
		else
		{
			((GrabbableObject)val).isBeingUsed = true;
			val.isPlayingMusic = true;
			if ((Object)(object)val.boomboxAudio != (Object)null && !val.boomboxAudio.isPlaying)
			{
				val.boomboxAudio.Play();
			}
			LethalMusicSyncPlugin.Log("[LMS] Boombox dropped while playing — ensuring it keeps playing.");
		}
	}
}
[HarmonyPatch(typeof(BoomboxItem), "ItemActivate")]
public static class BoomboxOverridePatch
{
	[HarmonyPrefix]
	public static bool Prefix(BoomboxItem __instance, bool used, bool buttonDown)
	{
		if (LethalMusicSyncPlugin.showBoomboxGui)
		{
			return false;
		}
		if (LethalMusicSyncPlugin.BoomboxQueueIndex != -1)
		{
			if (!LethalMusicSyncPlugin.IsProgrammaticActivation)
			{
				PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
				if ((Object)(object)val != (Object)null && (Object)(object)((GrabbableObject)__instance).playerHeldBy == (Object)(object)val)
				{
					if (!used)
					{
						LethalMusicSyncPlugin.pendingChatBroadcasts.Enqueue("[LMS_SYNC] BOOMBOX PAUSE");
					}
					else
					{
						LethalMusicSyncPlugin.pendingChatBroadcasts.Enqueue("[LMS_SYNC] BOOMBOX RESUME");
					}
				}
			}
			return false;
		}
		return true;
	}
}
[HarmonyPatch(typeof(Terminal), "Start")]
public static class TerminalHelpPatch
{
	[HarmonyPostfix]
	public static void Postfix(Terminal __instance)
	{
		if (!((Object)(object)__instance != (Object)null))
		{
			return;
		}
		if ((Object)(object)__instance.screenText != (Object)null)
		{
			__instance.screenText.characterLimit = 9999;
			LethalMusicSyncPlugin.Log("[LMS] Increased Terminal input characterLimit to 9999!");
		}
		if (!((Object)(object)__instance.terminalNodes != (Object)null) || __instance.terminalNodes.allKeywords == null)
		{
			return;
		}
		TerminalKeyword[] allKeywords = __instance.terminalNodes.allKeywords;
		foreach (TerminalKeyword val in allKeywords)
		{
			if ((Object)(object)val != (Object)null && val.word == "help" && (Object)(object)val.specialKeywordResult != (Object)null)
			{
				TerminalNode specialKeywordResult = val.specialKeywordResult;
				specialKeywordResult.displayText += "\n\n>MUSIC CONTROLS\nUse speaker/music/boombox: play [URL], queue [URL], list, skip, skip to [index], loop [track/queue/off], stop, clear, vol [0-100].\n* Speaker: 3D Spatial directional Audio from ship ceiling!\n* Music: 2D Global Ambient flat Background Music!\n* Boombox: Local Portable 3D Audio!\n\n";
				LethalMusicSyncPlugin.Log("[LMS] Injected music commands into the Terminal Help menu!");
				break;
			}
		}
	}
}
[HarmonyPatch(typeof(Terminal), "TextChanged")]
public static class TerminalTextChangedPatch
{
	[HarmonyPrefix]
	public static void Prefix(Terminal __instance)
	{
		if ((Object)(object)__instance != (Object)null && (Object)(object)__instance.currentNode != (Object)null && __instance.currentNode.maxCharactersToType < 9999)
		{
			__instance.currentNode.maxCharactersToType = 9999;
		}
	}
}