Decompiled source of Television v0.1.0

plugins/tech.rethunk.mortar.Television.dll

Decompiled 3 days 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;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using ConfigSync;
using GameNetcodeStuff;
using Microsoft.CodeAnalysis;
using MonoMod.RuntimeDetour;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.Video;

[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("Rethunk Tech")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Rethunk Tech")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+ba5778a89cd1d43b0f62d95fa4d90ffd7fc04813")]
[assembly: AssemblyProduct("tech.rethunk.mortar.Television")]
[assembly: AssemblyTitle("Television")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Rethunk-Tech/mortar-lc-television")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 Television
{
	[BepInPlugin("tech.rethunk.mortar.Television", "Television", "0.1.0")]
	[BepInProcess("Lethal Company.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private sealed class Show(string? url, VideoClip? clip, AudioClip? audio, double length)
		{
			public string? Url { get; } = url;

			public VideoClip? Clip { get; } = clip;

			public AudioClip? Audio { get; } = audio;

			public double Length { get; } = length;

			public string Name
			{
				get
				{
					if (Url == null)
					{
						return ((Object)Clip).name;
					}
					return Path.GetFileNameWithoutExtension(Url);
				}
			}

			public string Key
			{
				get
				{
					if (Url == null)
					{
						return "game:" + ((Object)Clip).name;
					}
					return Path.GetFileName(Url);
				}
			}
		}

		private sealed class Measurer : MonoBehaviour
		{
		}

		private sealed class Runner : MonoBehaviour
		{
			private float untilSync = 5f;

			private void Update()
			{
				untilSync -= Time.unscaledDeltaTime;
				if (untilSync > 0f)
				{
					return;
				}
				untilSync = 5f;
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton != (Object)null && singleton.IsServer && singleton.CustomMessagingManager != null && singleton.ConnectedClientsIds.Count > 1)
				{
					TVScript val = Object.FindObjectOfType<TVScript>();
					if (val != null && val.tvOn)
					{
						SendStateToAll();
					}
				}
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static Action<IReadOnlyCollection<string>> <0>__Reload;

			public static Func<string, bool> <1>__Exists;

			public static Func<string, IEnumerable<string>> <2>__GetFiles;

			public static Func<string, bool> <3>__IsVideo;

			public static Action<Action<TVScript>, TVScript> <4>__Update;

			public static Action<Action<TVScript, bool>, TVScript, bool> <5>__TurnOnOff;

			public static Action<Action<TVScript, VideoPlayer>, TVScript, VideoPlayer> <6>__Finished;

			public static Action<Action<StartOfRound>, StartOfRound> <7>__ShipLoaded;

			public static HandleNamedMessageDelegate <8>__Received;

			public static Action<CallbackContext> <9>__Pressed;
		}

		private const float MeasureTimeout = 10f;

		private const string Message = "tech.rethunk.mortar.Television";

		private const byte Ask = 0;

		private const byte Skip = 1;

		private const byte State = 2;

		private static readonly List<Hook> hooks = new List<Hook>();

		private static string[] videos = Array.Empty<string>();

		private static double[]? lengths;

		private static bool measuring;

		private static readonly Dictionary<string, double> known = new Dictionary<string, double>();

		private static int measureRound;

		private static SyncedEntry<bool> channel = null;

		private static SyncedEntry<bool> mixGame = null;

		private static SyncedEntry<bool> shuffle = null;

		private static readonly Dictionary<TVScript, int> nextShow = new Dictionary<TVScript, int>();

		private static double offset;

		private static bool listening;

		private static List<(string Key, double Length)>? hostSchedule;

		private static string? playingKey;

		private static int darkToken;

		private static Runner runner = null;

		private const float SyncEvery = 5f;

		private const double Drift = 1.5;

		private static readonly Dictionary<TVScript, InteractTrigger> triggers = new Dictionary<TVScript, InteractTrigger>();

		private static readonly Dictionary<InteractTrigger, string> hoverTips = new Dictionary<InteractTrigger, string>();

		internal static ManualLogSource Log { get; private set; } = null;

		private void Awake()
		{
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			videos = Videos();
			Log.LogInfo((object)string.Format("{0} {1} found {2} videos", "Television", "0.1.0", videos.Length));
			SyncedConfig val = SyncedConfig.For((BaseUnityPlugin)(object)this);
			channel = val.Share<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("TV", "Channel", true, "Play the videos as one channel on the lobby's clock, so everyone sees the same moment. Off starts a video from its beginning each time the TV comes on."), (Sync)1);
			mixGame = val.Share<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("TV", "Include game clips", false, "Keep the TV's own clips in the schedule with the packs' videos."), (Sync)1);
			shuffle = val.Share<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("TV", "Shuffle", true, "Play the videos in an order picked from the lobby's seed instead of by file name."), (Sync)1);
			Action action = delegate
			{
				TVScript[] array = Object.FindObjectsOfType<TVScript>();
				foreach (TVScript val3 in array)
				{
					if (val3.wasTvOnLastFrame)
					{
						Play(val3);
					}
				}
				if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer && NetworkManager.Singleton.CustomMessagingManager != null)
				{
					SendStateToAll();
				}
			};
			channel.Changed += action;
			mixGame.Changed += action;
			shuffle.Changed += action;
			GameObject val2 = new GameObject("Television");
			Object.DontDestroyOnLoad((Object)(object)val2);
			((Object)val2).hideFlags = (HideFlags)61;
			runner = val2.AddComponent<Runner>();
			Watch.Start(val2, Reload, Paths.PluginPath);
			if (videos.Length != 0)
			{
				TakeOver();
			}
		}

		private static string[] Videos()
		{
			return (from dir in Directory.GetDirectories(Paths.PluginPath, "mortar", SearchOption.AllDirectories)
				select Path.Combine(dir, "tv")).Where(Directory.Exists).Concat(Directory.GetDirectories(Paths.PluginPath, "Television Videos", SearchOption.AllDirectories)).SelectMany(Directory.GetFiles)
				.Where(IsVideo)
				.Distinct()
				.OrderBy<string, string>((string f) => Path.GetFileName(f), StringComparer.Ordinal)
				.ToArray();
		}

		private static bool IsVideo(string file)
		{
			if (!file.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase))
			{
				return file.EndsWith(".webm", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static void TakeOver()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			if (hooks.Count <= 0)
			{
				hooks.Add(new Hook((MethodBase)typeof(TVScript).GetMethod("Update", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Action<Action<TVScript>, TVScript>(Update)));
				hooks.Add(new Hook((MethodBase)typeof(TVScript).GetMethod("TurnTVOnOff"), (Delegate)new Action<Action<TVScript, bool>, TVScript, bool>(TurnOnOff)));
				hooks.Add(new Hook((MethodBase)typeof(TVScript).GetMethod("TVFinishedClip", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Action<Action<TVScript, VideoPlayer>, TVScript, VideoPlayer>(Finished)));
				hooks.Add(new Hook((MethodBase)typeof(StartOfRound).GetMethod("Start", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Action<Action<StartOfRound>, StartOfRound>(ShipLoaded)));
				StartMeasure();
			}
		}

		private static void Reload(IReadOnlyCollection<string> changed)
		{
			if (!changed.Any((string f) => IsVideo(f) || Path.GetExtension(f) == ""))
			{
				return;
			}
			if (lengths != null)
			{
				for (int num = 0; num < videos.Length; num++)
				{
					if (!changed.Contains(videos[num]))
					{
						known[videos[num]] = lengths[num];
					}
				}
			}
			foreach (string item in changed)
			{
				known.Remove(item);
			}
			videos = Videos();
			Log.LogInfo((object)$"Found {videos.Length} videos");
			lengths = null;
			measuring = false;
			measureRound++;
			if (videos.Length != 0)
			{
				TakeOver();
				StartMeasure();
			}
		}

		private static void ShipLoaded(Action<StartOfRound> orig, StartOfRound self)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			orig(self);
			offset = 0.0;
			hostSchedule = null;
			playingKey = null;
			nextShow.Clear();
			triggers.Clear();
			hoverTips.Clear();
			NetworkManager singleton = NetworkManager.Singleton;
			if (((singleton != null) ? singleton.CustomMessagingManager : null) != null)
			{
				CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
				object obj = <>O.<8>__Received;
				if (obj == null)
				{
					HandleNamedMessageDelegate val = Received;
					<>O.<8>__Received = val;
					obj = (object)val;
				}
				customMessagingManager.RegisterNamedMessageHandler("tech.rethunk.mortar.Television", (HandleNamedMessageDelegate)obj);
				if (!singleton.IsServer)
				{
					Send(0uL, 0);
				}
				if (!listening)
				{
					IngamePlayerSettings.Instance.playerInput.actions.FindAction("ItemSecondaryUse", false).performed += Pressed;
					listening = true;
				}
			}
		}

		private static void Pressed(CallbackContext context)
		{
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if ((Object)(object)val == (Object)null || val.isTypingChat || val.inTerminalMenu || (Object)(object)val.hoveringOverTrigger == (Object)null)
			{
				return;
			}
			TVScript val2 = TvOf(val.hoveringOverTrigger);
			if (!((Object)(object)val2 == (Object)null) && val2.tvOn)
			{
				if (NetworkManager.Singleton.IsServer)
				{
					SkipAhead(val2);
				}
				else
				{
					Send(0uL, 1);
				}
			}
		}

		private static TVScript? TvOf(InteractTrigger trigger)
		{
			for (int i = 0; i < ((UnityEventBase)trigger.onInteract).GetPersistentEventCount(); i++)
			{
				Object persistentTarget = ((UnityEventBase)trigger.onInteract).GetPersistentTarget(i);
				TVScript val = (TVScript)(object)((persistentTarget is TVScript) ? persistentTarget : null);
				if (val != null)
				{
					return val;
				}
			}
			return null;
		}

		private static void SkipAhead(TVScript tv)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			List<Show> list = ((lengths == null) ? new List<Show>() : Schedule(tv));
			if (list.Count == 0)
			{
				return;
			}
			if (channel.Value)
			{
				NetworkTime serverTime = NetworkManager.Singleton.ServerTime;
				double num = (((NetworkTime)(ref serverTime)).Time + offset) % list.Sum((Show show) => show.Length);
				int num2;
				for (num2 = 0; num2 < list.Count - 1 && num >= list[num2].Length; num2++)
				{
					num -= list[num2].Length;
				}
				offset += list[num2].Length - num + 0.05;
			}
			SendStateToAll(!channel.Value);
			if (tv.wasTvOnLastFrame)
			{
				Play(tv);
			}
		}

		private unsafe static void Send(ulong to, byte kind, bool advance = false)
		{
			//IL_0015: 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_002a: 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)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(64, (Allocator)2, 65536);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref kind, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe<double>(ref offset, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref advance, default(ForPrimitives));
				TVScript val2 = Object.FindObjectOfType<TVScript>();
				List<Show> list = ((kind == 2 && (Object)(object)val2 != (Object)null && lengths != null) ? Schedule(val2) : new List<Show>());
				int count = list.Count;
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives));
				foreach (Show item in list)
				{
					((FastBufferWriter)(ref val)).WriteValueSafe(item.Key, false);
					double length = item.Length;
					((FastBufferWriter)(ref val)).WriteValueSafe<double>(ref length, default(ForPrimitives));
				}
				NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("tech.rethunk.mortar.Television", to, val, (NetworkDelivery)4);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void SendStateToAll(bool advance = false)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			foreach (ulong connectedClientsId in singleton.ConnectedClientsIds)
			{
				if (connectedClientsId != 0L)
				{
					Send(connectedClientsId, 2, advance);
				}
			}
		}

		private static void Received(ulong sender, FastBufferReader reader)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			byte b = default(byte);
			((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
			double num = default(double);
			((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref num, default(ForPrimitives));
			bool flag = default(bool);
			((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref flag, default(ForPrimitives));
			TVScript val = Object.FindObjectOfType<TVScript>();
			if (NetworkManager.Singleton.IsServer)
			{
				switch (b)
				{
				case 0:
					Send(sender, 2);
					break;
				case 1:
					if ((Object)(object)val != (Object)null && val.tvOn)
					{
						SkipAhead(val);
					}
					break;
				}
				return;
			}
			int num2 = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num2, default(ForPrimitives));
			List<(string, double)> list = new List<(string, double)>(num2);
			string item = default(string);
			double item2 = default(double);
			for (int i = 0; i < num2; i++)
			{
				((FastBufferReader)(ref reader)).ReadValueSafe(ref item, false);
				((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref item2, default(ForPrimitives));
				list.Add((item, item2));
			}
			bool flag2 = num != offset || hostSchedule == null || !hostSchedule.SequenceEqual<(string, double)>(list);
			offset = num;
			hostSchedule = ((list.Count > 0) ? list : null);
			if (!((Object)(object)val == (Object)null) && val.wasTvOnLastFrame && (flag || (channel.Value && (flag2 || OutOfStep(val)))))
			{
				Play(val);
			}
		}

		private static bool OutOfStep(TVScript tv)
		{
			if (hostSchedule == null)
			{
				return false;
			}
			var (index, num) = Locate(hostSchedule.Select<(string, double), double>(((string Key, double Length) s) => s.Length).ToList());
			if (!(hostSchedule[index].Key != playingKey))
			{
				if (tv.video.isPlaying)
				{
					return Math.Abs(tv.video.time - num) > 1.5;
				}
				return false;
			}
			return true;
		}

		private static (int Index, double At) Locate(List<double> lengths)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			NetworkTime serverTime = NetworkManager.Singleton.ServerTime;
			double num = (((NetworkTime)(ref serverTime)).Time + offset) % lengths.Sum();
			int i;
			for (i = 0; i < lengths.Count - 1 && num >= lengths[i]; i++)
			{
				num -= lengths[i];
			}
			return (Index: i, At: num);
		}

		private static void Hover(TVScript tv, string? name)
		{
			if (!triggers.TryGetValue(tv, out InteractTrigger value))
			{
				value = ((IEnumerable<InteractTrigger>)Object.FindObjectsOfType<InteractTrigger>()).FirstOrDefault((Func<InteractTrigger, bool>)((InteractTrigger t) => (Object)(object)TvOf(t) == (Object)(object)tv));
				if ((Object)(object)value == (Object)null)
				{
					return;
				}
				triggers[tv] = value;
				hoverTips[value] = value.hoverTip;
			}
			string text = hoverTips[value];
			value.hoverTip = ((name == null) ? text : (text + "\nNext channel : [Q]\n" + name));
		}

		private static void StartMeasure()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			if (!measuring && lengths == null)
			{
				measuring = true;
				GameObject val = new GameObject("Television lengths");
				Object.DontDestroyOnLoad((Object)(object)val);
				((Object)val).hideFlags = (HideFlags)61;
				((MonoBehaviour)val.AddComponent<Measurer>()).StartCoroutine(Measure(val, measureRound));
			}
		}

		private static void Update(Action<TVScript> orig, TVScript self)
		{
			GameNetworkManager instance = GameNetworkManager.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.localPlayerController == (Object)null || (Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.ShutdownInProgress)
			{
				return;
			}
			bool flag = self.tvOn && !instance.localPlayerController.isInsideFactory;
			if (flag != self.wasTvOnLastFrame)
			{
				self.wasTvOnLastFrame = flag;
				self.SetTVScreenMaterial(flag);
				if (flag)
				{
					Play(self);
					return;
				}
				self.video.Stop();
				StopGameAudio(self);
				Hover(self, null);
			}
		}

		private static void TurnOnOff(Action<TVScript, bool> orig, TVScript self, bool on)
		{
			self.tvOn = on;
			AudioClip val = (on ? self.switchTVOn : self.switchTVOff);
			self.tvSFX.PlayOneShot(val);
			WalkieTalkie.TransmitOneShotAudio(self.tvSFX, val, 1f);
		}

		private static void Finished(Action<TVScript, VideoPlayer> orig, TVScript self, VideoPlayer source)
		{
			if (self.wasTvOnLastFrame)
			{
				Play(self);
			}
		}

		private static void Play(TVScript tv)
		{
			if (lengths == null)
			{
				StartMeasure();
				return;
			}
			List<Show> list = Schedule(tv);
			if (channel.Value && hostSchedule != null && !NetworkManager.Singleton.IsServer)
			{
				(int Index, double At) tuple = Locate(hostSchedule.Select<(string, double), double>(((string Key, double Length) s) => s.Length).ToList());
				int item = tuple.Index;
				double item2 = tuple.At;
				(string, double) tuple2 = hostSchedule[item];
				string key = tuple2.Item1;
				double item3 = tuple2.Item2;
				Show show = AllShows(tv).FirstOrDefault((Show show2) => show2.Key == key);
				if (show != null)
				{
					PlayShow(tv, show, item2);
				}
				else
				{
					Dark(tv, key, item3 - item2);
				}
			}
			else
			{
				if (list.Count == 0)
				{
					return;
				}
				if (channel.Value)
				{
					var (index, at) = Locate(list.Select((Show show2) => show2.Length).ToList());
					PlayShow(tv, list[index], at);
				}
				else
				{
					nextShow.TryGetValue(tv, out var value);
					value %= list.Count;
					nextShow[tv] = value + 1;
					PlayShow(tv, list[value], 0.0);
				}
			}
		}

		private static void Dark(TVScript tv, string key, double seconds)
		{
			tv.video.Stop();
			StopGameAudio(tv);
			tv.SetTVScreenMaterial(false);
			Hover(tv, null);
			playingKey = key;
			((MonoBehaviour)runner).StartCoroutine(AfterDark(tv, ++darkToken, (float)seconds));
		}

		private static IEnumerator AfterDark(TVScript tv, int token, float seconds)
		{
			yield return (object)new WaitForSeconds(seconds);
			if (token == darkToken && (Object)(object)tv != (Object)null && tv.wasTvOnLastFrame)
			{
				Play(tv);
			}
		}

		private static IEnumerable<Show> AllShows(TVScript tv)
		{
			for (int i = 0; i < tv.tvClips.Length; i++)
			{
				yield return new Show(null, tv.tvClips[i], tv.tvAudioClips[i], tv.tvClips[i].length);
			}
			for (int i = 0; i < videos.Length; i++)
			{
				if (lengths[i] > 0.0)
				{
					yield return new Show(videos[i], null, null, lengths[i]);
				}
			}
		}

		private static List<Show> Schedule(TVScript tv)
		{
			List<Show> list = new List<Show>();
			if (mixGame.Value || lengths.All((double length) => length <= 0.0))
			{
				for (int num = 0; num < tv.tvClips.Length; num++)
				{
					list.Add(new Show(null, tv.tvClips[num], tv.tvAudioClips[num], tv.tvClips[num].length));
				}
			}
			for (int num2 = 0; num2 < videos.Length; num2++)
			{
				if (lengths[num2] > 0.0)
				{
					list.Add(new Show(videos[num2], null, null, lengths[num2]));
				}
			}
			if (shuffle.Value && (Object)(object)StartOfRound.Instance != (Object)null)
			{
				Random random = new Random(StartOfRound.Instance.randomMapSeed);
				list = list.OrderBy((Show _) => random.Next()).ToList();
			}
			return list;
		}

		private static void PlayShow(TVScript tv, Show show, double at)
		{
			VideoPlayer video = tv.video;
			darkToken++;
			playingKey = show.Key;
			tv.SetTVScreenMaterial(true);
			video.Stop();
			StopGameAudio(tv);
			if ((Object)(object)show.Clip != (Object)null)
			{
				video.source = (VideoSource)0;
				video.clip = show.Clip;
				video.audioOutputMode = (VideoAudioOutputMode)0;
				tv.tvSFX.clip = show.Audio;
				tv.tvSFX.time = (float)at;
				tv.tvSFX.Play();
			}
			else
			{
				video.audioOutputMode = (VideoAudioOutputMode)1;
				video.controlledAudioTrackCount = 1;
				video.EnableAudioTrack((ushort)0, true);
				video.SetTargetAudioSource((ushort)0, tv.tvSFX);
				video.source = (VideoSource)1;
				video.url = new Uri(show.Url).AbsoluteUri;
			}
			video.isLooping = false;
			video.time = at;
			video.Play();
			Hover(tv, show.Name);
		}

		private static void StopGameAudio(TVScript tv)
		{
			if ((Object)(object)tv.tvSFX.clip != (Object)null)
			{
				tv.tvSFX.Stop();
				tv.tvSFX.clip = null;
			}
		}

		private static IEnumerator Measure(GameObject holder, int round)
		{
			VideoPlayer probe = holder.AddComponent<VideoPlayer>();
			probe.playOnAwake = false;
			probe.audioOutputMode = (VideoAudioOutputMode)0;
			probe.source = (VideoSource)1;
			bool failed = false;
			probe.errorReceived += (ErrorEventHandler)delegate
			{
				failed = true;
			};
			string[] list = videos;
			double[] measured = new double[list.Length];
			for (int i = 0; i < list.Length; i++)
			{
				if (!known.TryGetValue(list[i], out measured[i]))
				{
					failed = false;
					probe.url = new Uri(list[i]).AbsoluteUri;
					probe.Prepare();
					float waited = 0f;
					while (!probe.isPrepared && !failed && waited < 10f)
					{
						waited += Time.unscaledDeltaTime;
						yield return null;
					}
					measured[i] = (probe.isPrepared ? probe.length : 0.0);
					if (measured[i] <= 0.0)
					{
						Log.LogWarning((object)(list[i] + " can't play here, so it's skipped"));
					}
				}
			}
			Object.Destroy((Object)(object)holder);
			if (round == measureRound)
			{
				lengths = measured;
				TVScript val = Object.FindObjectOfType<TVScript>();
				if ((Object)(object)val != (Object)null && val.wasTvOnLastFrame)
				{
					Play(val);
				}
				if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer && NetworkManager.Singleton.CustomMessagingManager != null)
				{
					SendStateToAll();
				}
			}
		}
	}
	internal sealed class Watch : MonoBehaviour
	{
		private static readonly TimeSpan quiet = TimeSpan.FromSeconds(1.0);

		private readonly List<FileSystemWatcher> watchers = new List<FileSystemWatcher>();

		private readonly object gate = new object();

		private readonly HashSet<string> changed = new HashSet<string>();

		private DateTime last;

		private Action<IReadOnlyCollection<string>> onChange = delegate
		{
		};

		public static void Start(GameObject host, Action<IReadOnlyCollection<string>> onChange, params string[] dirs)
		{
			Watch watch = host.AddComponent<Watch>();
			watch.onChange = onChange;
			foreach (string item in dirs.Where(Directory.Exists))
			{
				try
				{
					FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(item)
					{
						IncludeSubdirectories = true
					};
					fileSystemWatcher.Created += delegate(object _, FileSystemEventArgs e)
					{
						watch.Note(e.FullPath);
					};
					fileSystemWatcher.Changed += delegate(object _, FileSystemEventArgs e)
					{
						watch.Note(e.FullPath);
					};
					fileSystemWatcher.Deleted += delegate(object _, FileSystemEventArgs e)
					{
						watch.Note(e.FullPath);
					};
					fileSystemWatcher.Renamed += delegate(object _, RenamedEventArgs e)
					{
						watch.Note(e.OldFullPath);
						watch.Note(e.FullPath);
					};
					fileSystemWatcher.EnableRaisingEvents = true;
					watch.watchers.Add(fileSystemWatcher);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Can't watch " + item + " for new files, so they need a restart: " + ex.Message));
				}
			}
		}

		private void Note(string path)
		{
			lock (gate)
			{
				changed.Add(path);
				last = DateTime.UtcNow;
			}
		}

		private void Update()
		{
			string[] obj;
			lock (gate)
			{
				if (changed.Count == 0 || DateTime.UtcNow - last < quiet)
				{
					return;
				}
				obj = changed.ToArray();
				changed.Clear();
			}
			try
			{
				onChange((IReadOnlyCollection<string>)(object)obj);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Couldn't pick up changed files: {arg}");
			}
		}

		private void OnDestroy()
		{
			foreach (FileSystemWatcher watcher in watchers)
			{
				watcher.Dispose();
			}
		}
	}
	internal static class MyPluginInfo
	{
		public const string Id = "tech.rethunk.mortar.Television";

		public const string Name = "Television";

		public const string Version = "0.1.0";
	}
}