Decompiled source of BestestTelevisionMod v1.4.0

BestestTelevisionMod.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 BestestTVModPlugin
{
	public static class KeySymbolConverter
	{
		public static string GetKeySymbol(Key key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Expected I4, but got Unknown
			switch ((int)key)
			{
			case 0:
				return "";
			case 1:
				return "[Space]";
			case 2:
				return "[Enter]";
			case 3:
				return "[Tab]";
			case 4:
				return "[`]";
			case 5:
				return "[']";
			case 6:
				return "[;]";
			case 7:
				return "[,]";
			case 8:
				return "[.]";
			case 9:
				return "[/]";
			case 10:
				return "[\\]";
			case 11:
				return "[[]";
			case 12:
				return "[]]";
			case 13:
				return "[-]";
			case 14:
				return "[=]";
			case 15:
			case 16:
			case 17:
			case 18:
			case 19:
			case 20:
			case 21:
			case 22:
			case 23:
			case 24:
			case 25:
			case 26:
			case 27:
			case 28:
			case 29:
			case 30:
			case 31:
			case 32:
			case 33:
			case 34:
			case 35:
			case 36:
			case 37:
			case 38:
			case 39:
			case 40:
				return "[" + ((object)(Key)(ref key)).ToString() + "]";
			case 41:
			case 42:
			case 43:
			case 44:
			case 45:
			case 46:
			case 47:
			case 48:
			case 49:
			case 50:
				return "[" + ((object)(Key)(ref key)).ToString().Substring(5) + "]";
			case 51:
			case 52:
			case 53:
			case 54:
			case 55:
			case 56:
			case 57:
			case 58:
			case 59:
			case 60:
			case 61:
				return "[←]";
			case 62:
				return "[→]";
			case 63:
				return "[↑]";
			case 64:
				return "[↓]";
			case 65:
			case 66:
			case 67:
			case 68:
			case 69:
			case 70:
			case 71:
			case 72:
			case 73:
			case 74:
			case 75:
			case 76:
			case 77:
			case 78:
				return "[÷]";
			case 79:
				return "[*]";
			case 80:
				return "[+]";
			case 81:
				return "[-]";
			case 82:
				return "[.]";
			case 83:
				return "[=]";
			case 84:
				return "[#0]";
			case 85:
				return "[#1]";
			case 86:
				return "[#2]";
			case 87:
				return "[#3]";
			case 88:
				return "[#4]";
			case 89:
				return "[#5]";
			case 90:
				return "[#6]";
			case 91:
				return "[#7]";
			case 92:
				return "[#8]";
			case 93:
				return "[#9]";
			default:
				return "[" + ((object)(Key)(ref key)).ToString() + "]";
			}
		}
	}
	public class ConfigManager
	{
		public static ConfigManager Instance { get; private set; }

		public static ConfigEntry<VideoAspectRatio> tvScalingOption { get; set; }

		public static ConfigEntry<bool> storingResets { get; set; }

		public static ConfigEntry<bool> enableHudTips { get; set; }

		public static ConfigEntry<bool> tvOnAlways { get; set; }

		public static ConfigEntry<bool> tvPlaysSequentially { get; set; }

		public static ConfigEntry<bool> tvSkipsAfterOffOn { get; set; }

		public static ConfigEntry<bool> shuffleOnStartup { get; set; }

		public static ConfigEntry<bool> enableSeeking { get; set; }

		public static ConfigEntry<bool> enableChannels { get; set; }

		public static ConfigEntry<bool> mouseWheelVolume { get; set; }

		public static ConfigEntry<bool> hideHoverTip { get; set; }

		public static ConfigEntry<bool> restrictChannels { get; set; }

		public static ConfigEntry<bool> tvLightEnabled { get; set; }

		public static ConfigEntry<double> seekAmount { get; set; }

		public static ConfigEntry<bool> enableSync { get; set; }

		public static ConfigEntry<Key> reloadVideosKeyBind { get; set; }

		public static ConfigEntry<Key> shuffleKeyBind { get; set; }

		public static ConfigEntry<Key> seekReverseKeyBind { get; set; }

		public static ConfigEntry<Key> seekForwardKeyBind { get; set; }

		public static ConfigEntry<Key> skipReverseKeyBind { get; set; }

		public static ConfigEntry<Key> skipForwardKeyBind { get; set; }

		public static ConfigEntry<Key> increaseSeekKeyBind { get; set; }

		public static ConfigEntry<Key> decreaseSeekKeyBind { get; set; }

		public static ConfigEntry<Key> pauseKeyBind { get; set; }

		public static ConfigEntry<bool> enableLogging { get; set; }

		public static ConfigFile configFile { get; private set; }

		public static void Init(ConfigFile config)
		{
			Instance = new ConfigManager(config);
		}

		private ConfigManager(ConfigFile cfg)
		{
			configFile = cfg;
			tvScalingOption = cfg.Bind<VideoAspectRatio>("Options", "Aspect Ratio", (VideoAspectRatio)1, "Available choices:\nNoScaling\nFitVertically\nFitHorizontally\nFitInside\nFitOutside\nStretch");
			storingResets = cfg.Bind<bool>("Options", "Storing Resets List", true, "Does storing the television reset the video index back to 1?");
			shuffleOnStartup = cfg.Bind<bool>("Options", "Shuffle On Startup", false, "If true, generates a fresh shuffle seed once at load time so videos play in a random order instead of alphabetical. To re-shuffle later at runtime, use the 'Shuffle Videos' keybind under [Bindings].");
			tvLightEnabled = cfg.Bind<bool>("Options", "Television Lights", true, "Does light emit from the television when it is turned on?");
			tvOnAlways = cfg.Bind<bool>("Options", "TV Always On", false, "Should the TV stay on after it's been turned on once?\n");
			tvPlaysSequentially = cfg.Bind<bool>("Options", "TV Plays Sequentially", true, "Play videos in order or loop?\n");
			tvSkipsAfterOffOn = cfg.Bind<bool>("Options", "TV Skips After Off On", false, "Should what is currently playing be skipped after the television is turned off and back on again?\n");
			enableSeeking = cfg.Bind<bool>("Options", "Enable Seeking", true, "Use brackets to fast forward or rewind?");
			enableChannels = cfg.Bind<bool>("Options", "Enable Channels", true, "Use comma or period to skip videos?");
			mouseWheelVolume = cfg.Bind<bool>("Options", "Mouse Wheel Volume", true, "Should the mouse wheel control the volume?");
			hideHoverTip = cfg.Bind<bool>("Options", "Hide Hovertips", false, "Hide the controls when hovering over the TV");
			restrictChannels = cfg.Bind<bool>("Options", "Restrict Channels", false, "Disable the channel controls, but keep the UI, unless Hide Hovertips is also checked");
			enableHudTips = cfg.Bind<bool>("Options", "Enable HUD Tips", true, "Show in-game HUD notifications for plugin actions (video reload, shuffle locally or from a remote player, etc.).");
			seekAmount = cfg.Bind<double>("Options", "Seek length", 16.0, "How many seconds to seek for.");
			enableSync = cfg.Bind<bool>("Multiplayer", "Enable Sync", true, "Synchronize TV state (shuffle seed, current channel, pause state, video time) between players.");
			reloadVideosKeyBind = cfg.Bind<Key>("Bindings", "Reload Videos", (Key)63, "Reload videos list. Not being synced! If some player, among those with enableSync = true, changes videos in video folders and then reloads their list, other players will get their incompatible state within their next broadcast which can crash the game maybe. If everyone (with enableSync = true) does the same changes to videos, everything should be fine tho (I hope so)");
			shuffleKeyBind = cfg.Bind<Key>("Bindings", "Shuffle Videos", (Key)10, "Re-shuffle the playback order at runtime (generates a new seed).");
			seekReverseKeyBind = cfg.Bind<Key>("Bindings", "Seek Backwards", (Key)11, "Go backwards in the currently playing video.");
			seekForwardKeyBind = cfg.Bind<Key>("Bindings", "Seek Forwards", (Key)12, "Go forwards in the currently playing video.");
			skipReverseKeyBind = cfg.Bind<Key>("Bindings", "Skip Backwards", (Key)7, "Skip to the previous video.");
			skipForwardKeyBind = cfg.Bind<Key>("Bindings", "Skip Forwards", (Key)8, "Skip to the next video.");
			increaseSeekKeyBind = cfg.Bind<Key>("Bindings", "Increase Seek length", (Key)14, "Doubles the seek length.");
			decreaseSeekKeyBind = cfg.Bind<Key>("Bindings", "Decrease Seek length", (Key)13, "Halves the seek length.");
			pauseKeyBind = cfg.Bind<Key>("Bindings", "Pause / Resume", (Key)30, "Pauses or resumes the currently playing video without turning the TV off.");
			enableLogging = cfg.Bind<bool>("Debug", "Logging Enabled", false, "Is logging enabled?");
		}
	}
	[HarmonyPatch(typeof(TVScript))]
	public class TVScriptPatches
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static EventHandler <0>__OnVideoEnded;
		}

		public static MethodInfo aspectRatio = typeof(VideoPlayer).GetMethod("VideoAspectRatio", BindingFlags.Instance | BindingFlags.NonPublic);

		public static FieldInfo? currentClipProperty = typeof(TVScript).GetField("currentClip", BindingFlags.Instance | BindingFlags.NonPublic);

		public static FieldInfo currentTimeProperty = typeof(TVScript).GetField("currentClipTime", BindingFlags.Instance | BindingFlags.NonPublic);

		public static bool tvIsCurrentlyOn = false;

		public static bool tvIsPaused = false;

		public static RenderTexture renderTexture;

		public static AudioSource audioSource;

		public static VideoPlayer videoSource;

		public Light tvLight;

		public static int TVIndex;

		public static TVScript LastTVInstance;

		private static bool screenMaterialOffApplied = false;

		private static MethodInfo cachedSetTVScreenMaterial;

		private static readonly object[] setTVScreenMaterialArgsTrue = new object[1] { true };

		private static readonly object[] setTVScreenMaterialArgsFalse = new object[1] { false };

		private static int lastAdvanceFrame = -1;

		[HarmonyPatch(typeof(StartOfRound), "Start")]
		[HarmonyPostfix]
		public static void SetTVIndex()
		{
			TVIndex = 0;
			tvIsCurrentlyOn = false;
			tvIsPaused = false;
			NetSync.EnsureRegistered();
		}

		public static void ApplyRemoteState(int remoteIndex, double remoteTime, bool remoteOn, bool remotePaused)
		{
			if (!ConfigManager.enableSync.Value || VideoManager.Videos.Count == 0)
			{
				return;
			}
			int count = VideoManager.Videos.Count;
			int num = (remoteIndex % count + count) % count;
			bool flag = num != TVIndex;
			TVIndex = num;
			if ((Object)(object)videoSource != (Object)null)
			{
				if (flag)
				{
					videoSource.Stop();
					videoSource.url = "file://" + VideoManager.GetVideo(TVIndex);
				}
				try
				{
					videoSource.time = remoteTime;
				}
				catch
				{
				}
				if (remoteOn)
				{
					if ((Object)(object)LastTVInstance != (Object)null)
					{
						SetTVScreenMaterial(LastTVInstance, b: true);
					}
					tvIsCurrentlyOn = true;
					tvIsPaused = remotePaused;
					try
					{
						if (remotePaused)
						{
							videoSource.Pause();
						}
						else
						{
							videoSource.Play();
						}
					}
					catch
					{
					}
				}
				else
				{
					if ((Object)(object)LastTVInstance != (Object)null)
					{
						SetTVScreenMaterial(LastTVInstance, b: false);
					}
					tvIsCurrentlyOn = false;
					tvIsPaused = false;
					try
					{
						videoSource.Stop();
					}
					catch
					{
					}
				}
			}
			if (ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogInfo((object)$"[NetSync] ApplyRemoteState idx={TVIndex} t={remoteTime} on={remoteOn} paused={remotePaused} seed={VideoManager.Seed}");
			}
		}

		public static void ApplyRemoteSeek(double remoteTime)
		{
			if (ConfigManager.enableSync.Value && !((Object)(object)videoSource == (Object)null))
			{
				try
				{
					videoSource.time = remoteTime;
				}
				catch
				{
				}
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)$"[NetSync] ApplyRemoteSeek t={remoteTime}");
				}
			}
		}

		public static void ApplyRemotePause(double remoteTime, bool remotePaused)
		{
			if (!ConfigManager.enableSync.Value)
			{
				return;
			}
			tvIsPaused = remotePaused;
			if ((Object)(object)videoSource == (Object)null)
			{
				return;
			}
			try
			{
				videoSource.time = remoteTime;
			}
			catch
			{
			}
			try
			{
				if (remotePaused)
				{
					videoSource.Pause();
				}
				else if (tvIsCurrentlyOn)
				{
					videoSource.Play();
				}
			}
			catch
			{
			}
			if (ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogInfo((object)$"[NetSync] ApplyRemotePause t={remoteTime} paused={remotePaused}");
			}
		}

		private static double CurrentVideoTime()
		{
			try
			{
				return ((Object)(object)videoSource != (Object)null) ? videoSource.time : 0.0;
			}
			catch
			{
				return 0.0;
			}
		}

		private static bool HasAdvanceAuthority(TVScript __instance)
		{
			if (!ConfigManager.enableSync.Value)
			{
				return true;
			}
			if (!NetSync.IsNetworkReady)
			{
				return true;
			}
			return NetSync.IsHost;
		}

		[HarmonyPatch(typeof(TVScript), "Update")]
		[HarmonyPrefix]
		public static bool Update(TVScript __instance)
		{
			LastTVInstance = __instance;
			if (ConfigManager.enableSync.Value)
			{
				NetSync.Tick();
			}
			if (!tvIsCurrentlyOn)
			{
				if (!screenMaterialOffApplied)
				{
					SetTVScreenMaterial(__instance, b: false);
					screenMaterialOffApplied = true;
				}
			}
			else
			{
				screenMaterialOffApplied = false;
			}
			if ((Object)(object)videoSource == (Object)null)
			{
				videoSource = ((Component)__instance).GetComponent<VideoPlayer>();
				renderTexture = videoSource.targetTexture;
				if (VideoManager.Videos.Count > 0)
				{
					WhatItDo(__instance, TVIndex);
				}
			}
			return false;
		}

		[HarmonyPatch(typeof(TVScript), "TurnTVOnOff")]
		[HarmonyPrefix]
		public static bool TurnTVOnOff(bool on, TVScript __instance)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			LastTVInstance = __instance;
			__instance.tvOn = on;
			audioSource = __instance.tvSFX;
			videoSource = __instance.video;
			if ((int)videoSource.source != 1 || videoSource.url == "")
			{
				WhatItDo(__instance, TVIndex);
			}
			if (on)
			{
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)"Turning on TV");
				}
				SetTVScreenMaterial(__instance, b: true);
				tvIsCurrentlyOn = true;
				tvIsPaused = false;
				audioSource.Play();
				videoSource.Play();
				videoSource.time = 0.0;
				audioSource.PlayOneShot(__instance.switchTVOn);
				WalkieTalkie.TransmitOneShotAudio(__instance.tvSFX, __instance.switchTVOn, 1f);
			}
			else if (!ConfigManager.tvOnAlways.Value)
			{
				if (ConfigManager.tvSkipsAfterOffOn.Value && HasAdvanceAuthority(__instance))
				{
					if (HasAdvanceAuthority(__instance))
					{
						TVIndexUp();
					}
					else
					{
						int count = VideoManager.Videos.Count;
						if (count > 0)
						{
							TVIndex = (TVIndex + 1) % count;
						}
					}
					videoSource.source = (VideoSource)1;
					videoSource.controlledAudioTrackCount = 1;
					videoSource.audioOutputMode = (VideoAudioOutputMode)1;
					videoSource.SetTargetAudioSource((ushort)0, audioSource);
					videoSource.url = "file://" + VideoManager.GetVideo(TVIndex);
					videoSource.Prepare();
				}
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)"Turning off TV");
				}
				SetTVScreenMaterial(__instance, b: false);
				audioSource.Stop();
				videoSource.Stop();
				audioSource.PlayOneShot(__instance.switchTVOn);
				WalkieTalkie.TransmitOneShotAudio(audioSource, __instance.switchTVOff, 1f);
				tvIsCurrentlyOn = false;
				tvIsPaused = false;
			}
			if (!NetSync.IsApplyingRemote)
			{
				NetSync.BroadcastFullState(TVIndex, CurrentVideoTime(), tvIsCurrentlyOn);
			}
			return false;
		}

		public static void TVIndexUp(bool do_broadcast = true)
		{
			if (VideoManager.Videos.Count > 0)
			{
				TVIndex = (TVIndex + 1) % VideoManager.Videos.Count;
			}
			SetVideoSourceUrl();
			if (!NetSync.IsApplyingRemote && do_broadcast)
			{
				NetSync.BroadcastFullState(TVIndex, 0.0, tvIsCurrentlyOn);
			}
		}

		public static void TVIndexDown()
		{
			if (VideoManager.Videos.Count > 0)
			{
				TVIndex = (TVIndex + VideoManager.Videos.Count - 1) % VideoManager.Videos.Count;
			}
			SetVideoSourceUrl();
			if (!NetSync.IsApplyingRemote)
			{
				NetSync.BroadcastFullState(TVIndex, 0.0, tvIsCurrentlyOn);
			}
		}

		private static void SetVideoSourceUrl()
		{
			videoSource.Stop();
			videoSource.time = 0.0;
			videoSource.url = "file://" + VideoManager.GetVideo(TVIndex);
		}

		public static void SetTVScreenMaterial(TVScript __instance, bool b)
		{
			if (cachedSetTVScreenMaterial == null)
			{
				cachedSetTVScreenMaterial = typeof(TVScript).GetMethod("SetTVScreenMaterial", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			cachedSetTVScreenMaterial.Invoke(__instance, b ? setTVScreenMaterialArgsTrue : setTVScreenMaterialArgsFalse);
			if (!ConfigManager.tvLightEnabled.Value)
			{
				((Behaviour)__instance.tvLight).enabled = false;
			}
		}

		[HarmonyPatch(typeof(TVScript), "TVFinishedClip")]
		[HarmonyPrefix]
		public static bool TVFinishedClip(TVScript __instance, VideoPlayer source)
		{
			LastTVInstance = __instance;
			if (ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogInfo((object)"TVFinishedClip");
			}
			lastAdvanceFrame = Time.frameCount;
			if (VideoManager.Videos.Count > 0 && ConfigManager.tvPlaysSequentially.Value)
			{
				TVIndexUp(HasAdvanceAuthority(__instance));
				videoSource = (((Object)(object)__instance.video != (Object)null) ? __instance.video : ((Component)__instance).GetComponent<VideoPlayer>());
				WhatItDo(__instance, TVIndex);
				if (tvIsCurrentlyOn)
				{
					try
					{
						videoSource.Play();
					}
					catch
					{
					}
				}
			}
			return false;
		}

		private static void OnVideoEnded(VideoPlayer source)
		{
			if (lastAdvanceFrame == Time.frameCount || (Object)(object)LastTVInstance == (Object)null || VideoManager.Videos.Count <= 0 || !ConfigManager.tvPlaysSequentially.Value)
			{
				return;
			}
			lastAdvanceFrame = Time.frameCount;
			TVIndexUp(HasAdvanceAuthority(LastTVInstance));
			if (!((Object)(object)LastTVInstance != (Object)null))
			{
				return;
			}
			videoSource = (((Object)(object)LastTVInstance.video != (Object)null) ? LastTVInstance.video : ((Component)LastTVInstance).GetComponent<VideoPlayer>());
			WhatItDo(LastTVInstance, TVIndex);
			if (!tvIsCurrentlyOn)
			{
				return;
			}
			try
			{
				videoSource.Play();
			}
			catch
			{
			}
		}

		private static void WhatItDo(TVScript __instance, int TVIndex = -1)
		{
			//IL_0025: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			audioSource = __instance.tvSFX;
			if (VideoManager.Videos.Count > 0)
			{
				videoSource.aspectRatio = ConfigManager.tvScalingOption.Value;
				videoSource.clip = null;
				audioSource.clip = null;
				string text = "file://" + VideoManager.GetVideo(TVIndex);
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)text);
				}
				videoSource.url = text;
				videoSource.source = (VideoSource)1;
				VideoPlayer obj = videoSource;
				object obj2 = <>O.<0>__OnVideoEnded;
				if (obj2 == null)
				{
					EventHandler val = OnVideoEnded;
					<>O.<0>__OnVideoEnded = val;
					obj2 = (object)val;
				}
				obj.loopPointReached -= (EventHandler)obj2;
				VideoPlayer obj3 = videoSource;
				object obj4 = <>O.<0>__OnVideoEnded;
				if (obj4 == null)
				{
					EventHandler val2 = OnVideoEnded;
					<>O.<0>__OnVideoEnded = val2;
					obj4 = (object)val2;
				}
				obj3.loopPointReached += (EventHandler)obj4;
				if (tvIsCurrentlyOn)
				{
					videoSource.Play();
				}
				videoSource.controlledAudioTrackCount = 1;
				videoSource.audioOutputMode = (VideoAudioOutputMode)1;
				videoSource.SetTargetAudioSource((ushort)0, audioSource);
				videoSource.Stop();
				videoSource.Prepare();
			}
			else if (ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogError((object)"VideoManager.Videos list is empty. Put some videos in Television Videos folder.");
			}
		}

		[HarmonyPatch(typeof(ShipBuildModeManager), "StoreShipObjectClientRpc")]
		[HarmonyPostfix]
		private static void StoreShipObjectClientRpcPostfix(int unlockableID)
		{
			if (!ConfigManager.storingResets.Value)
			{
				return;
			}
			UnlockableItem val = StartOfRound.Instance.unlockablesList.unlockables[unlockableID];
			if (val.inStorage && val.unlockableName == "Television" && TVIndex != 0)
			{
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)"Resetting play sequence...");
				}
				SetTVIndex();
			}
		}

		[HarmonyPatch(typeof(TVScript), "__initializeVariables")]
		[HarmonyPostfix]
		public static void SetTelevisionHoverTip(TVScript __instance)
		{
			Transform parent = ((Component)__instance).transform.parent;
			if (((Object)(object)(((Object)(object)parent != (Object)null) ? ((Component)parent).GetComponentInChildren<InteractTrigger>() : null) == (Object)null || (!ConfigManager.enableSeeking.Value && !ConfigManager.enableChannels.Value && !ConfigManager.mouseWheelVolume.Value)) && ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogInfo((object)"Television trigger missing!");
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Update")]
		[HarmonyPostfix]
		public static void GetTVInput(PlayerControllerB __instance)
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0397: Unknown result type (might be due to invalid IL or missing references)
			//IL_0330: Unknown result type (might be due to invalid IL or missing references)
			//IL_043e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0447: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Unknown result type (might be due to invalid IL or missing references)
			//IL_0500: Unknown result type (might be due to invalid IL or missing references)
			//IL_050a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0522: Unknown result type (might be due to invalid IL or missing references)
			//IL_052c: Unknown result type (might be due to invalid IL or missing references)
			//IL_056f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0579: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0617: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsOwner || !__instance.isPlayerControlled || __instance.inTerminalMenu || __instance.isTypingChat || __instance.isPlayerDead)
			{
				return;
			}
			InteractTrigger hoveringOverTrigger = __instance.hoveringOverTrigger;
			if ((Object)(object)hoveringOverTrigger == (Object)null)
			{
				return;
			}
			Transform parent = ((Component)hoveringOverTrigger).transform.parent;
			GameObject val = (((Object)(object)parent != (Object)null) ? ((Component)parent).gameObject : null);
			if (!((Object)(object)val != (Object)null) || !((Object)val).name.Contains("Television"))
			{
				return;
			}
			videoSource = val.GetComponentInChildren<VideoPlayer>();
			audioSource = ((Component)val.transform.Find("TVAudio")).GetComponent<AudioSource>();
			double num = videoSource.time;
			float y = ((InputControl<Vector2>)(object)Mouse.current.scroll).ReadValue().y;
			float volume = audioSource.volume;
			Key value = ConfigManager.seekReverseKeyBind.Value;
			Key value2 = ConfigManager.seekForwardKeyBind.Value;
			Key value3 = ConfigManager.skipReverseKeyBind.Value;
			Key value4 = ConfigManager.skipForwardKeyBind.Value;
			Key value5 = ConfigManager.increaseSeekKeyBind.Value;
			Key value6 = ConfigManager.decreaseSeekKeyBind.Value;
			Key value7 = ConfigManager.pauseKeyBind.Value;
			Key value8 = ConfigManager.shuffleKeyBind.Value;
			Key value9 = ConfigManager.reloadVideosKeyBind.Value;
			if (!((Object)(object)videoSource != (Object)null))
			{
				return;
			}
			if (((ButtonControl)Keyboard.current[value]).wasPressedThisFrame && ConfigManager.enableSeeking.Value && tvIsCurrentlyOn)
			{
				num -= ConfigManager.seekAmount.Value;
				if (num < 0.0)
				{
					num = 0.0;
				}
				videoSource.time = num;
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)("AdjustTime: " + num));
				}
				NetSync.BroadcastSeek(TVIndex, num, tvIsCurrentlyOn);
			}
			if (((ButtonControl)Keyboard.current[value2]).wasPressedThisFrame && ConfigManager.enableSeeking.Value && tvIsCurrentlyOn)
			{
				num += ConfigManager.seekAmount.Value;
				videoSource.time = num;
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)("AdjustTime: " + num));
				}
				NetSync.BroadcastSeek(TVIndex, num, tvIsCurrentlyOn);
			}
			if (((ButtonControl)Keyboard.current[value3]).wasPressedThisFrame && ConfigManager.enableChannels.Value && !ConfigManager.restrictChannels.Value && tvIsCurrentlyOn)
			{
				TVIndexDown();
			}
			if (((ButtonControl)Keyboard.current[value4]).wasPressedThisFrame && ConfigManager.enableChannels.Value && !ConfigManager.restrictChannels.Value && tvIsCurrentlyOn)
			{
				TVIndexUp();
			}
			if ((int)value5 != 0 && ((ButtonControl)Keyboard.current[value5]).wasPressedThisFrame && ConfigManager.enableSeeking.Value)
			{
				ConfigEntry<double> seekAmount = ConfigManager.seekAmount;
				seekAmount.Value *= 2.0;
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)("Seek amount: " + ConfigManager.seekAmount.Value));
				}
			}
			if ((int)value6 != 0 && ((ButtonControl)Keyboard.current[value6]).wasPressedThisFrame && ConfigManager.enableSeeking.Value)
			{
				ConfigEntry<double> seekAmount2 = ConfigManager.seekAmount;
				seekAmount2.Value /= 2.0;
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)("Seek amount: " + ConfigManager.seekAmount.Value));
				}
			}
			if ((int)value7 != 0 && ((ButtonControl)Keyboard.current[value7]).wasPressedThisFrame && tvIsCurrentlyOn)
			{
				tvIsPaused = !tvIsPaused;
				if (tvIsPaused)
				{
					videoSource.Pause();
				}
				else
				{
					videoSource.Play();
				}
				double time = videoSource.time;
				num = time;
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)$"TV paused: {tvIsPaused} t={time}");
				}
				NetSync.BroadcastPause(TVIndex, time, tvIsCurrentlyOn, tvIsPaused);
			}
			if ((int)value8 != 0 && ((ButtonControl)Keyboard.current[value8]).wasPressedThisFrame)
			{
				BestestTVModPlugin.TriggerShuffle();
			}
			if ((int)value9 != 0 && ((ButtonControl)Keyboard.current[value9]).wasPressedThisFrame)
			{
				BestestTVModPlugin.TriggerReloadVideos();
			}
			if ((!videoSource.isPlaying && !tvIsPaused) || !tvIsCurrentlyOn)
			{
				num = 0.0;
				videoSource.time = num;
			}
			InteractTrigger componentInChildren = ((Component)parent).GetComponentInChildren<InteractTrigger>();
			if ((Object)(object)componentInChildren == (Object)null && ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogInfo((object)"Television trigger missing!");
			}
			if (!ConfigManager.hideHoverTip.Value)
			{
				string text = $"Seek for {ConfigManager.seekAmount.Value}s: {KeySymbolConverter.GetKeySymbol(value)}{KeySymbolConverter.GetKeySymbol(value2)}\n{TimeSpan.FromSeconds(num):hh\\:mm\\:ss\\.fff}\nChange seek length: {KeySymbolConverter.GetKeySymbol(value6)}{KeySymbolConverter.GetKeySymbol(value5)}";
				string text2 = $"Volume: {KeySymbolConverter.GetKeySymbol((Key)13)}{KeySymbolConverter.GetKeySymbol((Key)80)}\n{volume * 150f:0}%";
				string text3 = $"Channel: {KeySymbolConverter.GetKeySymbol(value3)}{KeySymbolConverter.GetKeySymbol(value4)}\n{TVIndex + 1}/{VideoManager.Videos.Count}";
				string text4 = "";
				if ((int)value8 != 0)
				{
					text4 = text4 + "Shuffle: " + KeySymbolConverter.GetKeySymbol(value8);
				}
				if ((int)value7 != 0)
				{
					text4 = text4 + (string.IsNullOrEmpty(text4) ? "" : ", ") + "Pause: " + KeySymbolConverter.GetKeySymbol(value7);
				}
				if ((int)value9 != 0)
				{
					text4 = text4 + (string.IsNullOrEmpty(text4) ? "" : ", ") + "Reload: " + KeySymbolConverter.GetKeySymbol(value9);
				}
				string text5 = "";
				if (ConfigManager.enableSeeking.Value)
				{
					text5 += text;
				}
				if (ConfigManager.enableChannels.Value)
				{
					text5 = text5 + (string.IsNullOrEmpty(text5) ? "" : "\n") + text3;
				}
				if (ConfigManager.mouseWheelVolume.Value)
				{
					text5 = text5 + (string.IsNullOrEmpty(text5) ? "" : "\n") + text2;
				}
				text5 = text5 + (string.IsNullOrEmpty(text5) ? "" : "\n") + text4;
				componentInChildren.hoverTip = text5;
			}
			else
			{
				componentInChildren.hoverTip = "";
			}
			if (y != 0f && ConfigManager.mouseWheelVolume.Value)
			{
				y /= 6000f;
				volume = Mathf.Clamp(volume + y, 0f, 1f);
				audioSource.volume = volume;
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogInfo((object)("Changed volume: " + volume));
				}
			}
		}
	}
	[BepInPlugin("DeathWrench.BestestTelevisionMod", "\u200bBestestTelevisionMod", "1.3.2")]
	public class BestestTVModPlugin : BaseUnityPlugin
	{
		public const string PLUGIN_GUID = "DeathWrench.BestestTelevisionMod";

		public const string PLUGIN_NAME = "\u200bBestestTelevisionMod";

		public const string PLUGIN_VERSION = "1.3.2";

		private static readonly Harmony Harmony = new Harmony("DeathWrench.BestestTelevisionMod".ToString());

		public static ManualLogSource Log = new ManualLogSource("\u200b\u200bBestestTelevisionMod");

		public static BestestTVModPlugin instance;

		public static void TriggerReloadVideos()
		{
			try
			{
				StaticReloadVideos();
			}
			catch (Exception ex)
			{
				if (ConfigManager.enableLogging.Value)
				{
					Log.LogWarning((object)("RefreshVideos failed: " + ex.Message));
				}
			}
		}

		public static void TriggerShuffle()
		{
			VideoManager.Shuffle();
			TVScriptPatches.TVIndexDown();
			TVScriptPatches.TVIndexUp();
			ShowHudTip("Shuffle", $"New seed: {VideoManager.Seed}", "ShuffleTip");
		}

		public static void StaticReloadVideos()
		{
			VideoManager.Videos.Clear();
			VideoManager.Load();
			ShowHudTip("Reloaded Videos", "Video list has been reloaded.", "ReloadVideosTip");
		}

		public static void ShowHudTip(string header, string body, string preferenceKey)
		{
			if (!ConfigManager.enableHudTips.Value || (Object)(object)HUDManager.Instance == (Object)null)
			{
				return;
			}
			try
			{
				HUDManager.Instance.DisplayTip(header, body, false, false, preferenceKey);
			}
			catch (Exception ex)
			{
				if (ConfigManager.enableLogging.Value)
				{
					Log.LogWarning((object)("ShowHudTip failed: " + ex.Message));
				}
			}
		}

		private void OnDisable()
		{
			NetSync.Unregister();
		}

		private void Awake()
		{
			ConfigManager.Init(((BaseUnityPlugin)this).Config);
			instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Harmony.PatchAll();
			VideoManager.Load();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"DeathWrench.BestestTelevisionMod 1.3.2 is loaded!");
		}
	}
	public static class NetSync
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnMessageReceived;
		}

		private const string MsgName = "BestestTVMod.TVState";

		private const byte KIND_FULL_STATE = 1;

		private const byte KIND_SEEK = 2;

		private const byte KIND_PAUSE = 4;

		private static bool registered = false;

		private static NetworkManager registeredNm = null;

		private static object registeredCmm = null;

		private static bool wasConnected = false;

		private static float lastRegistrationAttempt = -10f;

		private const float REGISTRATION_RETRY_INTERVAL = 1f;

		public static bool IsApplyingRemote { get; private set; }

		public static bool IsHost => PlayerIsHost(ResolveTV());

		public static bool IsNetworkReady
		{
			get
			{
				NetworkManager val = ResolveNM();
				if ((Object)(object)val != (Object)null && (val.IsServer || val.IsClient))
				{
					return val.CustomMessagingManager != null;
				}
				return false;
			}
		}

		private static string StateSnapshot()
		{
			TVScript lastTVInstance = TVScriptPatches.LastTVInstance;
			NetworkManager singleton = NetworkManager.Singleton;
			string text = ((!((Object)(object)lastTVInstance != (Object)null)) ? "tv=null" : (((Object)(object)((NetworkBehaviour)lastTVInstance).NetworkManager != (Object)null) ? "yes" : "no"));
			string text2 = "null";
			if ((Object)(object)singleton != (Object)null)
			{
				text2 = string.Format("id={0},IsServer={1},IsClient={2},IsHost={3},cmm={4}", ((Object)singleton).GetInstanceID(), singleton.IsServer, singleton.IsClient, singleton.IsHost, (singleton.CustomMessagingManager != null) ? "ok" : "null");
			}
			return string.Format("registered={0}, wasConnected={1}, LastTV={2}, tvHasNm={3}, Singleton=[{4}], regNmId={5}, regCmmSet={6}", registered, wasConnected, ((Object)(object)lastTVInstance != (Object)null) ? "set" : "null", text, text2, ((Object)(object)registeredNm != (Object)null) ? ((Object)registeredNm).GetInstanceID().ToString() : "null", registeredCmm != null);
		}

		private static void LogDebug(string msg)
		{
			if (ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogInfo((object)("[NetSync] " + msg));
			}
		}

		private static TVScript ResolveTV()
		{
			TVScript lastTVInstance = TVScriptPatches.LastTVInstance;
			if ((Object)(object)lastTVInstance != (Object)null && (Object)(object)((NetworkBehaviour)lastTVInstance).NetworkManager != (Object)null)
			{
				return lastTVInstance;
			}
			try
			{
				TVScript val = Object.FindObjectOfType<TVScript>();
				if ((Object)(object)val != (Object)null)
				{
					TVScriptPatches.LastTVInstance = val;
					return val;
				}
			}
			catch
			{
			}
			return null;
		}

		private static NetworkManager ResolveNM()
		{
			TVScript val = ResolveTV();
			if ((Object)(object)val != (Object)null && (Object)(object)((NetworkBehaviour)val).NetworkManager != (Object)null)
			{
				return ((NetworkBehaviour)val).NetworkManager;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton != (Object)null)
			{
				return singleton;
			}
			try
			{
				NetworkManager val2 = Object.FindObjectOfType<NetworkManager>();
				if ((Object)(object)val2 != (Object)null)
				{
					return val2;
				}
			}
			catch
			{
			}
			return null;
		}

		public static bool PlayerIsHost(TVScript instance)
		{
			if ((Object)(object)instance != (Object)null && (Object)(object)((NetworkBehaviour)instance).NetworkManager != (Object)null)
			{
				return ((NetworkBehaviour)instance).NetworkManager.IsHost;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton != (Object)null)
			{
				return singleton.IsHost;
			}
			return false;
		}

		public static void EnsureRegistered()
		{
			//IL_00f2: 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_00fd: Expected O, but got Unknown
			NetworkManager val = ResolveNM();
			if ((Object)(object)val == (Object)null)
			{
				LogDebug("EnsureRegistered: NM is null. " + StateSnapshot());
				return;
			}
			if (val.CustomMessagingManager == null)
			{
				LogDebug("EnsureRegistered: CMM is null on NM. " + StateSnapshot());
				return;
			}
			if (!val.IsServer && !val.IsClient)
			{
				LogDebug("EnsureRegistered: NM is not server or client (disconnected). " + StateSnapshot());
				return;
			}
			CustomMessagingManager customMessagingManager = val.CustomMessagingManager;
			bool flag = registeredNm != val;
			bool flag2 = registeredCmm != customMessagingManager;
			if (registered && (flag || flag2))
			{
				LogDebug($"EnsureRegistered: stale state detected (nmChanged={flag}, cmmChanged={flag2}). Forcing re-register. {StateSnapshot()}");
				registered = false;
				registeredNm = null;
				registeredCmm = null;
			}
			if (registered)
			{
				return;
			}
			try
			{
				try
				{
					customMessagingManager.UnregisterNamedMessageHandler("BestestTVMod.TVState");
				}
				catch
				{
				}
				object obj2 = <>O.<0>__OnMessageReceived;
				if (obj2 == null)
				{
					HandleNamedMessageDelegate val2 = OnMessageReceived;
					<>O.<0>__OnMessageReceived = val2;
					obj2 = (object)val2;
				}
				customMessagingManager.RegisterNamedMessageHandler("BestestTVMod.TVState", (HandleNamedMessageDelegate)obj2);
				registered = true;
				registeredNm = val;
				registeredCmm = customMessagingManager;
				LogDebug("Registered named message handler. " + StateSnapshot());
			}
			catch (Exception ex)
			{
				if (ex.Message != null && ex.Message.IndexOf("already", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					registered = true;
					registeredNm = val;
					registeredCmm = customMessagingManager;
					LogDebug("Handler was already registered; recovered. " + StateSnapshot());
				}
				else if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogWarning((object)("[NetSync] Failed to register handler: " + ex.Message + ". " + StateSnapshot()));
				}
			}
		}

		public static void Tick()
		{
			if (!ConfigManager.enableSync.Value)
			{
				return;
			}
			if (registered && wasConnected && (Object)(object)registeredNm != (Object)null)
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton == registeredNm && (Object)(object)singleton != (Object)null && (singleton.IsServer || singleton.IsClient) && singleton.CustomMessagingManager == registeredCmm)
				{
					return;
				}
			}
			NetworkManager val = ResolveNM();
			int num;
			if ((Object)(object)val != (Object)null)
			{
				if (!val.IsServer)
				{
					num = (val.IsClient ? 1 : 0);
					if (num == 0)
					{
						goto IL_00b0;
					}
				}
				else
				{
					num = 1;
				}
				if (!wasConnected)
				{
					LogDebug("Detected reconnect transition. Forcing re-register. " + StateSnapshot());
					registered = false;
					registeredNm = null;
					registeredCmm = null;
				}
			}
			else
			{
				num = 0;
			}
			goto IL_00b0;
			IL_00b0:
			if (num == 0 && wasConnected)
			{
				LogDebug("Detected disconnect transition. Clearing state. " + StateSnapshot());
				registered = false;
				registeredNm = null;
				registeredCmm = null;
			}
			wasConnected = (byte)num != 0;
			if (registered && (Object)(object)val != (Object)null)
			{
				bool flag = registeredNm != val;
				bool flag2 = registeredCmm != val.CustomMessagingManager;
				if (flag || flag2)
				{
					LogDebug($"Tick: NM/CMM identity changed (nmChanged={flag}, cmmChanged={flag2}). {StateSnapshot()}");
					registered = false;
					registeredNm = null;
					registeredCmm = null;
				}
			}
			if (!registered && !(Time.unscaledTime - lastRegistrationAttempt < 1f))
			{
				lastRegistrationAttempt = Time.unscaledTime;
				EnsureRegistered();
			}
		}

		public static void Unregister()
		{
			if (!registered)
			{
				LogDebug("Unregister: not currently registered. " + StateSnapshot());
				return;
			}
			try
			{
				NetworkManager val = registeredNm ?? ResolveNM();
				if ((Object)(object)val != (Object)null && val.CustomMessagingManager != null)
				{
					val.CustomMessagingManager.UnregisterNamedMessageHandler("BestestTVMod.TVState");
				}
				LogDebug("Unregistered named message handler.");
			}
			catch (Exception ex)
			{
				LogDebug("Unregister threw: " + ex.Message);
			}
			registered = false;
			registeredNm = null;
			registeredCmm = null;
			wasConnected = false;
		}

		public static void BroadcastFullState(int tvIndex, double currentTime, bool tvOn)
		{
			SendState(1, VideoManager.Seed, tvIndex, currentTime, tvOn, TVScriptPatches.tvIsPaused);
		}

		public static void BroadcastSeek(int tvIndex, double currentTime, bool tvOn)
		{
			SendState(2, VideoManager.Seed, tvIndex, currentTime, tvOn, TVScriptPatches.tvIsPaused);
		}

		public static void BroadcastPause(int tvIndex, double currentTime, bool tvOn, bool isPaused)
		{
			SendState(4, VideoManager.Seed, tvIndex, currentTime, tvOn, isPaused);
		}

		private static void SendState(byte kind, int seed, int tvIndex, double currentTime, bool tvOn, bool isPaused)
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: 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_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigManager.enableSync.Value)
			{
				LogDebug($"SendState({kind}): sync disabled in config.");
				return;
			}
			EnsureRegistered();
			NetworkManager val = ResolveNM();
			if ((Object)(object)val == (Object)null)
			{
				LogDebug($"SendState({kind}): NM is null, dropping. {StateSnapshot()}");
				return;
			}
			if (!val.IsServer && !val.IsClient)
			{
				LogDebug($"SendState({kind}): NM not connected, dropping. {StateSnapshot()}");
				return;
			}
			CustomMessagingManager customMessagingManager = val.CustomMessagingManager;
			if (customMessagingManager == null)
			{
				LogDebug($"SendState({kind}): CMM null, dropping. {StateSnapshot()}");
				return;
			}
			if (!registered)
			{
				LogDebug($"SendState({kind}): handler not registered, dropping. {StateSnapshot()}");
				return;
			}
			try
			{
				FastBufferWriter val2 = default(FastBufferWriter);
				((FastBufferWriter)(ref val2))..ctor(19, (Allocator)2, -1);
				try
				{
					((FastBufferWriter)(ref val2)).WriteValueSafe<byte>(ref kind, default(ForPrimitives));
					((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref seed, default(ForPrimitives));
					((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref tvIndex, default(ForPrimitives));
					((FastBufferWriter)(ref val2)).WriteValueSafe<double>(ref currentTime, default(ForPrimitives));
					((FastBufferWriter)(ref val2)).WriteValueSafe<bool>(ref tvOn, default(ForPrimitives));
					((FastBufferWriter)(ref val2)).WriteValueSafe<bool>(ref isPaused, default(ForPrimitives));
					if (val.IsServer)
					{
						customMessagingManager.SendNamedMessageToAll("BestestTVMod.TVState", val2, (NetworkDelivery)2);
						LogDebug($"Sent kind={kind} to all clients (host). seed={seed}, idx={tvIndex}");
					}
					else
					{
						customMessagingManager.SendNamedMessage("BestestTVMod.TVState", 0uL, val2, (NetworkDelivery)2);
						LogDebug($"Sent kind={kind} to host (client). seed={seed}, idx={tvIndex}");
					}
				}
				finally
				{
					((IDisposable)(FastBufferWriter)(ref val2)).Dispose();
				}
			}
			catch (Exception ex)
			{
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogWarning((object)("[NetSync] Send failed: " + ex.Message));
				}
			}
		}

		private static void OnMessageReceived(ulong senderClientId, 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				byte b = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
				int num = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
				int num2 = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num2, default(ForPrimitives));
				double num3 = default(double);
				((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref num3, default(ForPrimitives));
				bool flag = default(bool);
				((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref flag, default(ForPrimitives));
				bool flag2 = default(bool);
				((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref flag2, default(ForPrimitives));
				TVScript instance = ResolveTV();
				LogDebug($"Received kind={b} from client {senderClientId}. seed={num}, idx={num2}, t={num3:F2}, on={flag}, paused={flag2}. {StateSnapshot()}");
				IsApplyingRemote = true;
				bool flag3 = false;
				try
				{
					if (VideoManager.Seed != num)
					{
						VideoManager.SetSeed(num);
						flag3 = true;
					}
					switch (b)
					{
					case 1:
						TVScriptPatches.ApplyRemoteState(num2, num3, flag, flag2);
						break;
					case 2:
						TVScriptPatches.ApplyRemoteSeek(num3);
						break;
					case 4:
						TVScriptPatches.ApplyRemotePause(num3, flag2);
						break;
					}
				}
				finally
				{
					IsApplyingRemote = false;
				}
				if (flag3)
				{
					BestestTVModPlugin.ShowHudTip("Shuffle", $"Another player shuffled the videos.\nNew seed: {num}", "ShuffleTip");
				}
				NetworkManager val = ResolveNM();
				if (PlayerIsHost(instance) && (Object)(object)val != (Object)null && senderClientId != val.LocalClientId)
				{
					switch (b)
					{
					case 1:
						BroadcastFullState(num2, num3, flag);
						break;
					case 2:
						BroadcastSeek(num2, num3, flag);
						break;
					case 4:
						BroadcastPause(num2, num3, flag, flag2);
						break;
					}
				}
			}
			catch (Exception ex)
			{
				if (ConfigManager.enableLogging.Value)
				{
					BestestTVModPlugin.Log.LogWarning((object)("[NetSync] Receive failed: " + ex.Message));
				}
			}
		}
	}
	public static class VideoManager
	{
		public static List<string> Videos = new List<string>();

		public static int Seed = 0;

		private static int[] permutation = null;

		private static int cachedCount = -1;

		private static int cachedSeed = 0;

		public static void Load()
		{
			string[] directories = Directory.GetDirectories(Paths.PluginPath);
			foreach (string text in directories)
			{
				string path = Path.Combine(Paths.PluginPath, text, "Television Videos");
				if (Directory.Exists(path))
				{
					string[] files = Directory.GetFiles(path, "*.mp4");
					Videos.AddRange(files);
					if (ConfigManager.enableLogging.Value)
					{
						BestestTVModPlugin.Log.LogInfo((object)$"{text} has {files.Length} videos.");
					}
				}
			}
			string path2 = Path.Combine(Paths.PluginPath, "Television Videos");
			if (!Directory.Exists(path2))
			{
				Directory.CreateDirectory(path2);
			}
			string[] files2 = Directory.GetFiles(path2, "*.mp4");
			Videos.AddRange(files2);
			if (ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogInfo((object)$"Global has {files2.Length} videos.");
			}
			if (ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogInfo((object)$"Loaded {Videos.Count} total.");
			}
			if (ConfigManager.shuffleOnStartup.Value)
			{
				Shuffle();
			}
		}

		public static void Shuffle()
		{
			bool isApplyingRemote = NetSync.IsApplyingRemote;
			SetSeed(new Random().Next(int.MinValue, int.MaxValue));
			if (ConfigManager.enableLogging.Value)
			{
				BestestTVModPlugin.Log.LogInfo((object)$"Shuffle: new seed = {Seed}");
			}
			if (isApplyingRemote || !ConfigManager.enableSync.Value || !NetSync.IsNetworkReady)
			{
				return;
			}
			double currentTime = 0.0;
			try
			{
				if ((Object)(object)TVScriptPatches.videoSource != (Object)null)
				{
					currentTime = TVScriptPatches.videoSource.time;
				}
			}
			catch
			{
			}
			NetSync.BroadcastFullState(TVScriptPatches.TVIndex, currentTime, TVScriptPatches.tvIsCurrentlyOn);
		}

		public static void SetSeed(int seed)
		{
			if (Seed != seed)
			{
				Seed = seed;
				InvalidatePermutation();
			}
		}

		public static void ResetShuffle()
		{
			SetSeed(0);
		}

		public static int GetMappedIndex(int i)
		{
			int count = Videos.Count;
			if (count <= 0)
			{
				return i;
			}
			int num = (i % count + count) % count;
			if (Seed == 0)
			{
				return num;
			}
			EnsurePermutation();
			return permutation[num];
		}

		public static string GetVideo(int i)
		{
			return Videos[GetMappedIndex(i)];
		}

		private static void InvalidatePermutation()
		{
			permutation = null;
			cachedCount = -1;
			cachedSeed = 0;
		}

		private static void EnsurePermutation()
		{
			int count = Videos.Count;
			if (permutation == null || cachedCount != count || cachedSeed != Seed)
			{
				permutation = new int[count];
				for (int i = 0; i < count; i++)
				{
					permutation[i] = i;
				}
				Random random = new Random(Seed);
				for (int num = count - 1; num > 0; num--)
				{
					int num2 = random.Next(num + 1);
					int num3 = permutation[num];
					permutation[num] = permutation[num2];
					permutation[num2] = num3;
				}
				cachedCount = count;
				cachedSeed = Seed;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}