Decompiled source of CartRadio v1.0.0

plugins/CartRadio/CartRadio.dll

Decompiled 10 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CartRadio")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CartRadio")]
[assembly: AssemblyTitle("CartRadio")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CartRadio
{
	public sealed class CartSpeaker : MonoBehaviour
	{
		public AudioSource Source { get; private set; }

		public PhysGrabCart Cart { get; private set; }

		private void Awake()
		{
			Cart = ((Component)this).GetComponentInParent<PhysGrabCart>();
			Source = ((Component)this).gameObject.AddComponent<AudioSource>();
			Source.playOnAwake = false;
			Source.spatialBlend = 1f;
			Source.dopplerLevel = 0f;
			Source.rolloffMode = (AudioRolloffMode)1;
			Source.minDistance = 1.5f;
			Source.maxDistance = 22f;
			Source.loop = false;
			Source.priority = 160;
			Source.volume = 0.55f;
		}

		public void ApplyTimeline(RadioState state, double clock, bool forceSeek)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			Source.volume = state.Volume;
			if (!Object.op_Implicit((Object)(object)Source.clip) || (int)Source.clip.loadState != 2)
			{
				return;
			}
			float num = (float)state.PositionAt(clock);
			if (num >= Source.clip.length)
			{
				Source.Stop();
				return;
			}
			if (forceSeek || Mathf.Abs(Source.time - num) > 0.35f)
			{
				Source.time = Mathf.Clamp(num, 0f, Mathf.Max(0f, Source.clip.length - 0.02f));
			}
			if (state.Playing && !Source.isPlaying)
			{
				Source.Play();
			}
			else if (!state.Playing && Source.isPlaying)
			{
				Source.Pause();
			}
		}
	}
	public sealed class Track
	{
		public string Id;

		public string Name;

		public string Path;
	}
	public static class MusicLibrary
	{
		public static Track[] Scan(string folder, CancellationToken cancellation)
		{
			Directory.CreateDirectory(folder);
			List<Track> list = new List<Track>();
			long num = 0L;
			foreach (string item in Directory.GetFiles(folder).OrderBy<string, string>((string p) => p, StringComparer.Ordinal))
			{
				cancellation.ThrowIfCancellationRequested();
				string text = Path.GetExtension(item).ToLowerInvariant();
				if (text != ".wav" && text != ".mp3" && text != ".ogg")
				{
					continue;
				}
				FileInfo fileInfo = new FileInfo(item);
				if ((fileInfo.Attributes & FileAttributes.ReparsePoint) != FileAttributes.None || fileInfo.Length == 0L || fileInfo.Length > 20971520)
				{
					continue;
				}
				if (list.Count >= 64 || num + fileInfo.Length > 268435456)
				{
					break;
				}
				num += fileInfo.Length;
				using FileStream inputStream = File.OpenRead(item);
				using SHA256 sHA = SHA256.Create();
				string id = BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "").ToLowerInvariant();
				if (!list.Any((Track t) => t.Id == id))
				{
					list.Add(new Track
					{
						Id = id,
						Name = Path.GetFileNameWithoutExtension(item),
						Path = fileInfo.FullName
					});
				}
			}
			return list.ToArray();
		}
	}
	[BepInPlugin("pl.cart.radio", "Cart Radio", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin, IOnEventCallback
	{
		private Track[] tracks = Array.Empty<Track>();

		private Task<Track[]> scan;

		private readonly CancellationTokenSource cancellation = new CancellationTokenSource();

		private RadioState state = new RadioState();

		private readonly Dictionary<int, float> requestTimes = new Dictionary<int, float>();

		private readonly Dictionary<int, float> helloTimes = new Dictionary<int, float>();

		private PhysGrabCart[] carts = Array.Empty<PhysGrabCart>();

		private PhysGrabCart nearest;

		private PhysGrabCart panelCart;

		private PhysGrabCart soloCart;

		private CartSpeaker activeSpeaker;

		private AudioClip clip;

		private string loadedId = "";

		private string loadingId = "";

		private string failedId = "";

		private Coroutine loader;

		private float cartScanAt;

		private float syncAt;

		private float broadcastAt;

		private float requestAt;

		private float statusUntil;

		private Room room;

		private int masterId;

		private bool haveSnapshot;

		private ConfigEntry<float> personalVolume;

		private ConfigEntry<Key> panelKey;

		private string status = "Wczytywanie biblioteki muzyki...";

		private bool disposed;

		private static readonly FieldInfo DisabledField = AccessTools.Field(typeof(PlayerAvatar), "isDisabled");

		private static readonly FieldInfo MenuStateField = AccessTools.Field(typeof(MenuManager), "currentMenuState");

		private bool panelOpen;

		private CursorLockMode oldLock;

		private bool oldVisible;

		private Vector2 scroll;

		private float lastButtonAt = -1f;

		private readonly List<InputKey> noKeys = new List<InputKey>();

		private GUIStyle titleStyle;

		private GUIStyle labelStyle;

		private bool Networked
		{
			get
			{
				if (Object.op_Implicit((Object)(object)GameManager.instance) && PhotonNetwork.InRoom)
				{
					return SemiFunc.IsMultiplayer();
				}
				return false;
			}
		}

		private bool IsHost
		{
			get
			{
				if (Networked)
				{
					return PhotonNetwork.IsMasterClient;
				}
				return true;
			}
		}

		private double Clock
		{
			get
			{
				if (!Networked)
				{
					return Time.realtimeSinceStartupAsDouble;
				}
				return PhotonNetwork.Time;
			}
		}

		private bool InGameplay
		{
			get
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Invalid comparison between Unknown and I4
				if (Object.op_Implicit((Object)(object)GameDirector.instance))
				{
					return (int)GameDirector.instance.currentState == 2;
				}
				return false;
			}
		}

		private bool OtherMenuOpen
		{
			get
			{
				if (SemiFunc.NoTextInputsActive())
				{
					if (Object.op_Implicit((Object)(object)MenuManager.instance) && MenuStateField != null)
					{
						return (int)MenuStateField.GetValue(MenuManager.instance) == 0;
					}
					return false;
				}
				return true;
			}
		}

		private void Awake()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			personalVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "PersonalVolume", 0.8f, new ConfigDescription("Your local listening volume. 0 mutes the radio only for you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			panelKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Controls", "PanelKey", (Key)99, "Open radio controls near a cart.");
			string folder = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Music");
			scan = Task.Run(() => MusicLibrary.Scan(folder, cancellation.Token));
			PhotonNetwork.AddCallbackTarget((object)this);
			SceneManager.activeSceneChanged += SceneChanged;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Cart Radio loaded. Music folder: " + folder));
		}

		private void SceneChanged(Scene oldScene, Scene nextScene)
		{
			ClosePanel();
			if (Object.op_Implicit((Object)(object)activeSpeaker))
			{
				activeSpeaker.Source.Stop();
			}
			activeSpeaker = null;
			soloCart = null;
			carts = Array.Empty<PhysGrabCart>();
			nearest = null;
			cartScanAt = 0f;
			if (IsHost)
			{
				state.Playing = false;
				state.CartViewId = 0;
				state.PositionSeconds = 0.0;
				state.ServerTime = Clock;
				state.Revision++;
				Broadcast();
			}
			haveSnapshot = IsHost;
		}

		private static bool Alive(PlayerAvatar player)
		{
			if (Object.op_Implicit((Object)(object)player) && DisabledField != null)
			{
				return !(bool)DisabledField.GetValue(player);
			}
			return false;
		}

		private void Update()
		{
			if (disposed)
			{
				return;
			}
			if (scan != null && scan.IsCompleted)
			{
				if (scan.Status == TaskStatus.RanToCompletion)
				{
					tracks = scan.Result;
					SetStatus("Wczytano utworów: " + tracks.Length);
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogError((object)scan.Exception);
					SetStatus("Nie udało się odczytać folderu Music.");
				}
				scan = null;
			}
			UpdateRoom();
			if (Time.unscaledTime >= cartScanAt)
			{
				carts = Object.FindObjectsOfType<PhysGrabCart>();
				PhysGrabCart[] array = carts;
				foreach (PhysGrabCart cart in array)
				{
					EnsureSpeaker(cart);
				}
				cartScanAt = Time.unscaledTime + 0.5f;
			}
			PlayerAvatar local = PlayerAvatar.instance;
			nearest = ((InGameplay && Alive(local)) ? (from c in carts
				where Object.op_Implicit((Object)(object)c) && ((Component)c).gameObject.activeInHierarchy && Distance(local, c) <= 3f
				orderby Distance(local, c)
				select c).FirstOrDefault() : null);
			UpdatePanel();
			if (Time.unscaledTime >= syncAt)
			{
				syncAt = Time.unscaledTime + 0.2f;
				SyncAudio(force: false);
			}
			if (IsHost && state.Playing && Object.op_Implicit((Object)(object)clip) && loadedId == state.TrackId && state.PositionAt(Clock) >= (double)clip.length)
			{
				Advance(1);
			}
		}

		private static float Distance(PlayerAvatar player, PhysGrabCart cart)
		{
			//IL_0006: 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)
			return Vector3.Distance(((Component)player).transform.position, ((Component)cart).transform.position);
		}

		private void UpdateRoom()
		{
			Room val = (Networked ? PhotonNetwork.CurrentRoom : null);
			if (room != val)
			{
				room = val;
				masterId = 0;
				state = new RadioState();
				haveSnapshot = IsHost;
				requestTimes.Clear();
				helloTimes.Clear();
				if (Object.op_Implicit((Object)(object)activeSpeaker))
				{
					activeSpeaker.Source.Stop();
				}
				activeSpeaker = null;
				soloCart = null;
			}
			if (!Networked)
			{
				return;
			}
			int actorNumber = PhotonNetwork.MasterClient.ActorNumber;
			if (masterId != actorNumber)
			{
				masterId = actorNumber;
				requestTimes.Clear();
				helloTimes.Clear();
				if (IsHost)
				{
					state.Revision++;
					Broadcast();
				}
				else
				{
					haveSnapshot = false;
				}
			}
			if (IsHost && Time.unscaledTime >= broadcastAt)
			{
				broadcastAt = Time.unscaledTime + 3f;
				Broadcast();
			}
			else if (!IsHost && !haveSnapshot && Time.unscaledTime >= requestAt)
			{
				requestAt = Time.unscaledTime + 3f;
				Send(new object[2] { "pl.cart-radio.v1", "hello" }, (ReceiverGroup)2);
			}
		}

		private void Send(object[] payload, ReceiverGroup receivers)
		{
			//IL_000e: 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_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			if (Networked)
			{
				PhotonNetwork.RaiseEvent((byte)178, (object)payload, new RaiseEventOptions
				{
					Receivers = receivers
				}, SendOptions.SendReliable);
			}
		}

		private void Broadcast(int targetActor = 0)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (Networked && PhotonNetwork.IsMasterClient)
			{
				object obj;
				if (targetActor <= 0)
				{
					obj = (object)new RaiseEventOptions
					{
						Receivers = (ReceiverGroup)0
					};
				}
				else
				{
					RaiseEventOptions val = new RaiseEventOptions();
					val.TargetActors = new int[1] { targetActor };
					obj = val;
				}
				RaiseEventOptions val2 = (RaiseEventOptions)obj;
				PhotonNetwork.RaiseEvent((byte)178, (object)RadioProtocol.Encode(state), val2, SendOptions.SendReliable);
			}
		}

		public void OnEvent(EventData incoming)
		{
			if (!Networked || incoming.Code != 178 || !(incoming.CustomData is object[] array) || array.Length < 2 || !(array[0] is string text) || text != "pl.cart-radio.v1" || !(array[1] is string text2))
			{
				return;
			}
			if (text2 == "state")
			{
				if (incoming.Sender == PhotonNetwork.MasterClient.ActorNumber && !IsHost && RadioProtocol.TryDecode(array, out var radioState) && (!haveSnapshot || radioState.Revision >= state.Revision))
				{
					bool num = !haveSnapshot || radioState.Revision != state.Revision;
					state = radioState;
					haveSnapshot = true;
					if (num)
					{
						SyncAudio(force: true);
					}
				}
			}
			else if (text2 == "hello" && array.Length == 2 && IsHost)
			{
				if (!Limited(helloTimes, incoming.Sender, 2f))
				{
					Broadcast(incoming.Sender);
				}
			}
			else if (text2 == "request" && IsHost && array.Length == 6 && array[2] is string operation && array[3] is int num2 && array[4] is string { Length: <=128 } text3 && array[5] is float volume && !Limited(requestTimes, incoming.Sender, 0.2f))
			{
				PhotonView val = PhotonView.Find(num2);
				PhysGrabCart val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<PhysGrabCart>() : null);
				PlayerAvatar player = (Object.op_Implicit((Object)(object)GameDirector.instance) ? ((IEnumerable<PlayerAvatar>)GameDirector.instance.PlayerList).FirstOrDefault((Func<PlayerAvatar, bool>)((PlayerAvatar p) => Object.op_Implicit((Object)(object)p) && Object.op_Implicit((Object)(object)p.photonView) && p.photonView.OwnerActorNr == incoming.Sender)) : null);
				if (InGameplay && Object.op_Implicit((Object)(object)val2) && Alive(player) && !(Distance(player, val2) > 3.75f))
				{
					Handle(operation, val2, text3, volume);
				}
			}
		}

		private bool Limited(Dictionary<int, float> times, int actor, float delay)
		{
			if (times.TryGetValue(actor, out var value) && Time.unscaledTime - value < delay)
			{
				return true;
			}
			if (times.Count > 64)
			{
				times.Clear();
			}
			times[actor] = Time.unscaledTime;
			return false;
		}

		private void Request(string operation, string trackId = "", float volume = 0f)
		{
			if (!Object.op_Implicit((Object)(object)panelCart) || !Alive(PlayerAvatar.instance) || Distance(PlayerAvatar.instance, panelCart) > 3f || Time.unscaledTime - lastButtonAt < 0.25f)
			{
				return;
			}
			lastButtonAt = Time.unscaledTime;
			if (IsHost)
			{
				Handle(operation, panelCart, trackId, volume);
				return;
			}
			PhotonView component = ((Component)panelCart).GetComponent<PhotonView>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				SetStatus("Wózek nie ma identyfikatora sieciowego.");
				return;
			}
			Send(new object[6] { "pl.cart-radio.v1", "request", operation, component.ViewID, trackId, volume }, (ReceiverGroup)2);
			SetStatus("Wysłano polecenie do hosta.");
		}

		private void Handle(string operation, PhysGrabCart cart, string trackId, float volume)
		{
			if ((operation != "play" && operation != "pause" && operation != "stop" && operation != "next" && operation != "previous" && operation != "select" && operation != "volume") || (operation == "select" && !tracks.Any((Track t) => t.Id == trackId)) || (operation == "volume" && (float.IsNaN(volume) || float.IsInfinity(volume))))
			{
				return;
			}
			if (tracks.Length == 0 && operation != "stop")
			{
				SetStatus("Folder Music jest pusty albo jeszcze się wczytuje.");
				return;
			}
			RadioState next = state.Copy();
			next.PositionSeconds = state.PositionAt(Clock);
			next.ServerTime = Clock;
			PhotonView component = ((Component)cart).GetComponent<PhotonView>();
			next.CartViewId = ((Networked && Object.op_Implicit((Object)(object)component)) ? component.ViewID : 0);
			soloCart = cart;
			if (operation == "select")
			{
				next.TrackId = trackId;
				next.PositionSeconds = 0.0;
				next.Playing = true;
			}
			if (operation == "next" || operation == "previous")
			{
				int num = Array.FindIndex(tracks, (Track t) => t.Id == next.TrackId);
				int num2 = ((operation == "next") ? 1 : (-1));
				int num3 = ((num >= 0) ? ((num + num2 + tracks.Length) % tracks.Length) : 0);
				next.TrackId = tracks[num3].Id;
				next.PositionSeconds = 0.0;
				next.Playing = true;
			}
			if (operation == "play")
			{
				if (!tracks.Any((Track t) => t.Id == next.TrackId))
				{
					next.TrackId = tracks[0].Id;
					next.PositionSeconds = 0.0;
				}
				if (Object.op_Implicit((Object)(object)clip) && loadedId == next.TrackId && next.PositionSeconds >= (double)clip.length)
				{
					next.PositionSeconds = 0.0;
				}
				next.Playing = true;
			}
			if (operation == "pause")
			{
				next.Playing = false;
			}
			if (operation == "stop")
			{
				next.Playing = false;
				next.PositionSeconds = 0.0;
			}
			if (operation == "volume")
			{
				next.Volume = Mathf.Clamp01(volume);
			}
			next.Revision++;
			state = next;
			failedId = "";
			SyncAudio(force: true);
			Broadcast();
		}

		private void Advance(int direction)
		{
			PhysGrabCart val = ResolveCart();
			if (Object.op_Implicit((Object)(object)val))
			{
				Handle((direction > 0) ? "next" : "previous", val, "", 0f);
				return;
			}
			state.Playing = false;
			state.Revision++;
			Broadcast();
		}

		private PhysGrabCart ResolveCart()
		{
			if (!Networked)
			{
				return soloCart;
			}
			if (state.CartViewId <= 0)
			{
				return null;
			}
			PhotonView val = PhotonView.Find(state.CartViewId);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			return ((Component)val).GetComponent<PhysGrabCart>();
		}

		private CartSpeaker EnsureSpeaker(PhysGrabCart cart)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			CartSpeaker componentInChildren = ((Component)cart).GetComponentInChildren<CartSpeaker>();
			if (Object.op_Implicit((Object)(object)componentInChildren))
			{
				return componentInChildren;
			}
			GameObject val = new GameObject("Cart Radio Speaker");
			val.transform.SetParent(((Component)cart).transform, false);
			val.transform.position = (Object.op_Implicit((Object)(object)cart.handlePoint) ? cart.handlePoint.position : (((Component)cart).transform.position + Vector3.up));
			componentInChildren = val.AddComponent<CartSpeaker>();
			RadioModel.Build(val.transform, cart);
			return componentInChildren;
		}

		private void SyncAudio(bool force)
		{
			if (!InGameplay)
			{
				if (Object.op_Implicit((Object)(object)activeSpeaker))
				{
					activeSpeaker.Source.Pause();
				}
				return;
			}
			PhysGrabCart val = ResolveCart();
			if (!Object.op_Implicit((Object)(object)val))
			{
				if (Object.op_Implicit((Object)(object)activeSpeaker))
				{
					activeSpeaker.Source.Stop();
				}
				activeSpeaker = null;
				return;
			}
			if (!Object.op_Implicit((Object)(object)activeSpeaker) || (Object)(object)activeSpeaker.Cart != (Object)(object)val)
			{
				if (Object.op_Implicit((Object)(object)activeSpeaker))
				{
					activeSpeaker.Source.Stop();
				}
				activeSpeaker = EnsureSpeaker(val);
				force = true;
			}
			if (string.IsNullOrEmpty(state.TrackId))
			{
				activeSpeaker.Source.Stop();
				return;
			}
			Track track = tracks.FirstOrDefault((Track t) => t.Id == state.TrackId);
			if (track == null)
			{
				activeSpeaker.Source.Stop();
				if (scan == null && failedId != state.TrackId)
				{
					failedId = state.TrackId;
					SetStatus("Brakuje pliku aktualnego utworu. Skopiuj ten sam plik od hosta do Music.");
				}
			}
			else if (loadedId != state.TrackId)
			{
				activeSpeaker.Source.Stop();
				if (loadingId != state.TrackId && failedId != state.TrackId)
				{
					if (loader != null)
					{
						((MonoBehaviour)this).StopCoroutine(loader);
					}
					loadingId = state.TrackId;
					loader = ((MonoBehaviour)this).StartCoroutine(LoadTrack(track));
				}
			}
			else
			{
				if ((Object)(object)activeSpeaker.Source.clip != (Object)(object)clip)
				{
					activeSpeaker.Source.clip = clip;
					force = true;
				}
				activeSpeaker.ApplyTimeline(state, Clock, force);
				activeSpeaker.Source.volume = state.Volume * personalVolume.Value;
			}
		}

		private IEnumerator LoadTrack(Track track)
		{
			SetStatus("Wczytywanie: " + track.Name);
			string text = Path.GetExtension(track.Path).ToLowerInvariant();
			AudioType val = (AudioType)((text == ".ogg") ? 14 : ((text == ".mp3") ? 13 : 20));
			UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(track.Path).AbsoluteUri, val);
			try
			{
				request.timeout = 25;
				yield return request.SendWebRequest();
				loadingId = "";
				loader = null;
				if ((int)request.result != 1)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Audio decode failed for " + track.Name + ": " + request.error));
					FailTrack(track, "Nie można odtworzyć pliku: " + track.Name);
					yield break;
				}
				AudioClip content = DownloadHandlerAudioClip.GetContent(request);
				if (!Object.op_Implicit((Object)(object)content) || content.length <= 0f || content.length > 900f || (long)content.samples * (long)content.channels > 32000000)
				{
					if (Object.op_Implicit((Object)(object)content))
					{
						Object.Destroy((Object)(object)content);
					}
					FailTrack(track, "Plik jest niepoprawny lub przekracza limit długości / pamięci.");
					yield break;
				}
				if (state.TrackId != track.Id)
				{
					Object.Destroy((Object)(object)content);
					yield break;
				}
				if (Object.op_Implicit((Object)(object)activeSpeaker))
				{
					activeSpeaker.Source.Stop();
					activeSpeaker.Source.clip = null;
				}
				if (Object.op_Implicit((Object)(object)clip))
				{
					Object.Destroy((Object)(object)clip);
				}
				clip = content;
				loadedId = track.Id;
				failedId = "";
				SyncAudio(force: true);
				SetStatus("Gotowe: " + track.Name);
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private void SetStatus(string text)
		{
			status = text;
			statusUntil = Time.unscaledTime + 10f;
		}

		private void FailTrack(Track track, string message)
		{
			failedId = track.Id;
			SetStatus(message);
			if (IsHost && state.TrackId == track.Id)
			{
				state.Playing = false;
				state.PositionSeconds = 0.0;
				state.ServerTime = Clock;
				state.Revision++;
				Broadcast();
			}
		}

		private void OnDestroy()
		{
			disposed = true;
			ClosePanel();
			cancellation.Cancel();
			PhotonNetwork.RemoveCallbackTarget((object)this);
			SceneManager.activeSceneChanged -= SceneChanged;
			if (loader != null)
			{
				((MonoBehaviour)this).StopCoroutine(loader);
			}
			CartSpeaker[] array = Object.FindObjectsOfType<CartSpeaker>();
			for (int i = 0; i < array.Length; i++)
			{
				Object.Destroy((Object)(object)((Component)array[i]).gameObject);
			}
			if (Object.op_Implicit((Object)(object)clip))
			{
				Object.Destroy((Object)(object)clip);
			}
		}

		private void UpdatePanel()
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			Keyboard current = Keyboard.current;
			if (panelOpen && (!Object.op_Implicit((Object)(object)panelCart) || !InGameplay || !Alive(PlayerAvatar.instance) || Distance(PlayerAvatar.instance, panelCart) > 3f || OtherMenuOpen || !Application.isFocused))
			{
				ClosePanel();
			}
			if (current != null && (int)panelKey.Value != 0 && ((ButtonControl)current[panelKey.Value]).wasPressedThisFrame)
			{
				if (panelOpen)
				{
					ClosePanel();
				}
				else if (Object.op_Implicit((Object)(object)nearest) && !OtherMenuOpen && Application.isFocused)
				{
					panelCart = nearest;
					oldLock = Cursor.lockState;
					oldVisible = Cursor.visible;
					panelOpen = true;
					Cursor.lockState = (CursorLockMode)0;
					Cursor.visible = true;
				}
			}
			if (panelOpen && current != null && ((ButtonControl)current.escapeKey).wasPressedThisFrame)
			{
				ClosePanel();
			}
			if (panelOpen && Object.op_Implicit((Object)(object)InputManager.instance))
			{
				InputManager.instance.DisableMovement(0.1f);
				InputManager.instance.DisableAiming(0.1f);
				InputManager.instance.DisableControlsExcept(0.1f, noKeys);
			}
		}

		private void LateUpdate()
		{
			if (panelOpen)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
		}

		private void ClosePanel()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (panelOpen)
			{
				panelOpen = false;
				panelCart = null;
				bool otherMenuOpen = OtherMenuOpen;
				Cursor.lockState = (CursorLockMode)((!otherMenuOpen) ? ((int)oldLock) : 0);
				Cursor.visible = otherMenuOpen || oldVisible;
			}
		}

		private void OnDisable()
		{
			ClosePanel();
		}

		private void OnGUI()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c1: Unknown result type (might be due to invalid IL or missing references)
			if (!InGameplay || !Alive(PlayerAvatar.instance))
			{
				return;
			}
			if (titleStyle == null)
			{
				titleStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 23,
					fontStyle = (FontStyle)1,
					richText = false
				};
				labelStyle = new GUIStyle(GUI.skin.label)
				{
					wordWrap = true,
					richText = false,
					fontSize = 14
				};
			}
			if (!panelOpen)
			{
				if (Object.op_Implicit((Object)(object)nearest) && !OtherMenuOpen)
				{
					GUI.Box(new Rect((float)Screen.width / 2f - 170f, (float)(Screen.height - 115), 340f, 35f), "[" + ((object)panelKey.Value/*cast due to .constrained prefix*/).ToString() + "] Radio w wózku");
				}
				return;
			}
			float num = Mathf.Min(540, Screen.width - 24);
			float num2 = Mathf.Min(610, Screen.height - 24);
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(((float)Screen.width - num) / 2f, ((float)Screen.height - num2) / 2f, num, num2);
			GUI.Box(val, "");
			GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val)).y + 12f, ((Rect)(ref val)).width - 36f, ((Rect)(ref val)).height - 24f));
			GUILayout.Label("RADIO W WÓZKU", titleStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Wspólna muzyka • MP3 / OGG / WAV", labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Space(10f);
			Track track = tracks.FirstOrDefault((Track t) => t.Id == state.TrackId);
			GUILayout.Label((track != null) ? track.Name : (string.IsNullOrEmpty(state.TrackId) ? "Wybierz utwór" : "Brak pliku utworu u Ciebie"), labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label(state.Playing ? "Odtwarzanie" : "Pauza / zatrzymano", labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Poprzedni", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) }))
			{
				Request("previous");
			}
			if (GUILayout.Button(state.Playing ? "Pauza" : "Odtwórz", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) }))
			{
				Request(state.Playing ? "pause" : "play");
			}
			if (GUILayout.Button("Następny", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) }))
			{
				Request("next");
			}
			if (GUILayout.Button("Stop", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) }))
			{
				Request("stop");
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(8f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Głośność radia: " + Mathf.RoundToInt(state.Volume * 100f) + "%", labelStyle, Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("−", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(42f) }))
			{
				Request("volume", "", state.Volume - 0.1f);
			}
			if (GUILayout.Button("+", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(42f) }))
			{
				Request("volume", "", state.Volume + 0.1f);
			}
			GUILayout.EndHorizontal();
			GUILayout.Label("Twoja głośność: " + Mathf.RoundToInt(personalVolume.Value * 100f) + "%", labelStyle, Array.Empty<GUILayoutOption>());
			float num3 = GUILayout.HorizontalSlider(personalVolume.Value, 0f, 1f, Array.Empty<GUILayoutOption>());
			if (Mathf.Abs(num3 - personalVolume.Value) > 0.015f)
			{
				personalVolume.Value = num3;
			}
			GUILayout.Space(8f);
			GUILayout.Label("Biblioteka: " + tracks.Length + " utworów", labelStyle, Array.Empty<GUILayoutOption>());
			scroll = GUILayout.BeginScrollView(scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Mathf.Max(70f, num2 - 380f)) });
			Track[] array = tracks;
			foreach (Track track2 in array)
			{
				if (GUILayout.Button(track2.Name, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(29f) }))
				{
					Request("select", track2.Id);
				}
			}
			if (tracks.Length == 0)
			{
				GUILayout.Label("Dodaj pliki do CartRadio/Music i uruchom grę ponownie.", labelStyle, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
			GUILayout.Space(8f);
			GUILayout.Label((Time.unscaledTime < statusUntil) ? status : "Każdy gracz potrzebuje tych samych plików w Music.", labelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (GUILayout.Button("Zamknij [" + ((object)panelKey.Value/*cast due to .constrained prefix*/).ToString() + "]", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
			{
				ClosePanel();
			}
			GUILayout.EndArea();
		}
	}
	internal static class RadioModel
	{
		public static void Build(Transform root, PhysGrabCart cart)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			MeshRenderer componentInChildren = ((Component)cart).GetComponentInChildren<MeshRenderer>();
			if (Object.op_Implicit((Object)(object)componentInChildren) && Object.op_Implicit((Object)(object)((Renderer)componentInChildren).sharedMaterial))
			{
				Material val = new Material(((Renderer)componentInChildren).sharedMaterial);
				if (val.HasProperty("_MainTex"))
				{
					val.SetTexture("_MainTex", (Texture)null);
				}
				if (val.HasProperty("_Color"))
				{
					val.color = new Color(0.3f, 0.36f, 0.3f);
				}
				Material val2 = new Material(val);
				if (val2.HasProperty("_Color"))
				{
					val2.color = new Color(0.07f, 0.09f, 0.08f);
				}
				Material val3 = new Material(val);
				if (val3.HasProperty("_Color"))
				{
					val3.color = new Color(0.56f, 0.7f, 0.43f);
				}
				((Component)root).gameObject.AddComponent<RadioMaterialOwner>().Materials = (Material[])(object)new Material[3] { val, val2, val3 };
				Part(root, new Vector3(0f, 0.12f, 0f), new Vector3(0.3f, 0.19f, 0.1f), val, ((Component)componentInChildren).gameObject.layer);
				for (int i = 0; i < 6; i++)
				{
					Part(root, new Vector3(-0.065f + (float)i * 0.021f, 0.105f, -0.055f), new Vector3(0.009f, 0.1f, 0.01f), val2, ((Component)componentInChildren).gameObject.layer);
				}
				Part(root, new Vector3(0.098f, 0.15f, -0.055f), new Vector3(0.065f, 0.035f, 0.01f), val3, ((Component)componentInChildren).gameObject.layer);
				Part(root, new Vector3(0.1f, 0.09f, -0.06f), new Vector3(0.035f, 0.035f, 0.025f), val2, ((Component)componentInChildren).gameObject.layer);
				Part(root, new Vector3(-0.1f, 0.27f, 0.025f), new Vector3(0.008f, 0.18f, 0.008f), val2, ((Component)componentInChildren).gameObject.layer);
			}
		}

		private static void Part(Transform parent, Vector3 position, Vector3 scale, Material material, int layer)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3);
			((Object)obj).name = "Cart Radio Housing";
			obj.transform.SetParent(parent, false);
			obj.transform.localPosition = position;
			obj.transform.localScale = scale;
			obj.layer = layer;
			Collider component = obj.GetComponent<Collider>();
			component.enabled = false;
			Object.Destroy((Object)(object)component);
			((Renderer)obj.GetComponent<MeshRenderer>()).sharedMaterial = material;
		}
	}
	public sealed class RadioMaterialOwner : MonoBehaviour
	{
		public Material[] Materials;

		private void OnDestroy()
		{
			if (Materials == null)
			{
				return;
			}
			Material[] materials = Materials;
			foreach (Material val in materials)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}
	}
	public sealed class RadioState
	{
		public int Revision;

		public int CartViewId;

		public string TrackId = "";

		public bool Playing;

		public double PositionSeconds;

		public double ServerTime;

		public float Volume = 0.55f;

		public double PositionAt(double now)
		{
			return Math.Max(0.0, PositionSeconds + (Playing ? Math.Max(0.0, now - ServerTime) : 0.0));
		}

		public RadioState Copy()
		{
			return (RadioState)MemberwiseClone();
		}

		public bool IsValid()
		{
			if (Revision >= 0 && CartViewId >= 0 && TrackId != null && TrackId.Length <= 128 && !double.IsNaN(PositionSeconds) && !double.IsInfinity(PositionSeconds) && PositionSeconds >= 0.0 && PositionSeconds <= 86400.0 && !double.IsNaN(ServerTime) && !double.IsInfinity(ServerTime) && !float.IsNaN(Volume) && Volume >= 0f)
			{
				return Volume <= 1f;
			}
			return false;
		}
	}
	public static class RadioProtocol
	{
		public const byte EventCode = 178;

		public const string Signature = "pl.cart-radio.v1";

		public static object[] Encode(RadioState state)
		{
			return new object[9] { "pl.cart-radio.v1", "state", state.Revision, state.CartViewId, state.TrackId, state.Playing, state.PositionSeconds, state.ServerTime, state.Volume };
		}

		public static bool TryDecode(object payload, out RadioState state)
		{
			state = null;
			if (!(payload is object[] array) || array.Length != 9 || !(array[0] is string text) || text != "pl.cart-radio.v1" || !(array[1] is string text2) || text2 != "state" || !(array[2] is int revision) || !(array[3] is int cartViewId) || !(array[4] is string trackId) || !(array[5] is bool playing) || !(array[6] is double positionSeconds) || !(array[7] is double serverTime) || !(array[8] is float volume))
			{
				return false;
			}
			RadioState radioState = new RadioState
			{
				Revision = revision,
				CartViewId = cartViewId,
				TrackId = trackId,
				Playing = playing,
				PositionSeconds = positionSeconds,
				ServerTime = serverTime,
				Volume = volume
			};
			if (!radioState.IsValid())
			{
				return false;
			}
			state = radioState;
			return true;
		}
	}
}