Decompiled source of TVSync v2.0.0

TVSync.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using TVSync.Networking;
using TVSync.Patches;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Video;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TVSync")]
[assembly: AssemblyDescription("Synchronized YouTube TV playback for Lethal Company multiplayer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCompany("TVSync")]
[assembly: AssemblyProduct("TVSync")]
[assembly: AssemblyCopyright("Copyright 2025")]
[assembly: ComVisible(false)]
[assembly: Guid("ad955809-1ecb-4d74-a640-73a1179c4823")]
[assembly: AssemblyInformationalVersion("2.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.0.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TVSync
{
	public static class ChatHelper
	{
		private static string _lastMessage = "";

		public static void ShowMessage(HUDManager hud, string message, string senderName)
		{
			if (!((Object)(object)hud == (Object)null) && !string.IsNullOrEmpty(message) && !(_lastMessage == message))
			{
				_lastMessage = message;
				hud.PingHUDElement(hud.Chat, 4f, 1f, 0.2f);
				if (hud.ChatMessageHistory.Count >= 4)
				{
					hud.ChatMessageHistory.RemoveAt(0);
				}
				string item = (string.IsNullOrEmpty(senderName) ? ("<color=#7069ff>" + message + "</color>") : ("<color=#00BFFF>" + senderName + "</color>: <color=#FFFFFF>" + message + "</color>"));
				hud.ChatMessageHistory.Add(item);
				StringBuilder stringBuilder = new StringBuilder();
				for (int i = 0; i < hud.ChatMessageHistory.Count; i++)
				{
					stringBuilder.Append("\n");
					stringBuilder.Append(hud.ChatMessageHistory[i]);
				}
				((TMP_Text)hud.chatText).text = stringBuilder.ToString();
			}
		}

		public static void ResetDedup()
		{
			_lastMessage = "";
		}
	}
	public class TVSyncConfig
	{
		public ConfigEntry<string> Language { get; private set; }

		public ConfigEntry<float> DriftThresholdMs { get; private set; }

		public ConfigEntry<float> SyncIntervalSec { get; private set; }

		public ConfigEntry<float> ReadyTimeoutSec { get; private set; }

		public ConfigEntry<float> MaxSeekThresholdMs { get; private set; }

		public Lang LangData { get; private set; }

		public void Init()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "TVSync.cfg"), true);
			Language = val.Bind<string>("General", "Language", "en", "en = English, ru = Русский");
			DriftThresholdMs = val.Bind<float>("Sync", "DriftThresholdMs", 150f, "Maximum drift in ms before correction is applied");
			SyncIntervalSec = val.Bind<float>("Sync", "SyncIntervalSec", 3f, "How often the host broadcasts sync data (seconds)");
			ReadyTimeoutSec = val.Bind<float>("Sync", "ReadyTimeoutSec", 30f, "Maximum time to wait for all clients to be ready (seconds)");
			MaxSeekThresholdMs = val.Bind<float>("Sync", "MaxSeekThresholdMs", 500f, "Drift above this threshold causes a hard seek instead of speed adjustment");
			LangData = new Lang();
			if (Language.Value.ToLower() == "ru")
			{
				LangData.InitRU();
			}
			else
			{
				LangData.InitEN();
			}
		}
	}
	public class Lang
	{
		public ConfigEntry<string> LibLoading;

		public ConfigEntry<string> TVTooltip;

		public ConfigEntry<string> LibReady;

		public ConfigEntry<string> VideoLoading;

		public ConfigEntry<string> HelpText;

		public ConfigEntry<string> InvalidUrl;

		public ConfigEntry<string> PleaseWait;

		public ConfigEntry<string> VideoReady;

		public ConfigEntry<string> VolumeChanged;

		public ConfigEntry<string> InvalidVolume;

		public ConfigEntry<string> LinkInvalid;

		public ConfigEntry<string> TimeChanged;

		public ConfigEntry<string> MemoryToggle;

		public ConfigEntry<string> WaitingForPlayers;

		public ConfigEntry<string> AllPlayersReady;

		public ConfigEntry<string> SyncStarting;

		public ConfigEntry<string> PlayerTimedOut;

		public ConfigEntry<string> HostOnly;

		public ConfigEntry<string> Paused;

		public ConfigEntry<string> Resumed;

		public ConfigEntry<string> Stopped;

		public void InitEN()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			ConfigFile val = new ConfigFile("TVSync\\lang\\television_en.cfg", true);
			LibLoading = val.Bind<string>("General", "Main_1", "Please wait, downloading libraries...", (ConfigDescription)null);
			TVTooltip = val.Bind<string>("General", "Main_2", "Toggle TV : [E]\n@2 - @3\n@1 volume\nVol Up [PG UP]\nVol Down [PG DN]", (ConfigDescription)null);
			LibReady = val.Bind<string>("General", "Main_3", "Libraries loaded.", (ConfigDescription)null);
			VideoLoading = val.Bind<string>("General", "Main_4", "Video is loading!", (ConfigDescription)null);
			HelpText = val.Bind<string>("General", "Main_5", "/tplay [LINK] - Play video\n/ttime [TIME] - Seek to time\n/treset - Restart\n/tpause - Pause\n/tresume - Resume\n/tstop - Stop\n/tvolume [0-100] - Volume\n/tposition [BOOL] - Remember position", (ConfigDescription)null);
			InvalidUrl = val.Bind<string>("General", "Main_6", "Invalid URL!", (ConfigDescription)null);
			PleaseWait = val.Bind<string>("General", "Main_7", "Please wait...", (ConfigDescription)null);
			VideoReady = val.Bind<string>("General", "Main_8", "Video loaded.", (ConfigDescription)null);
			VolumeChanged = val.Bind<string>("General", "Main_9", "@1 set volume to @2", (ConfigDescription)null);
			InvalidVolume = val.Bind<string>("General", "Main_10", "Invalid Volume!", (ConfigDescription)null);
			LinkInvalid = val.Bind<string>("General", "Main_11", "Link is invalid!", (ConfigDescription)null);
			TimeChanged = val.Bind<string>("General", "Main_12", "Time: @1", (ConfigDescription)null);
			MemoryToggle = val.Bind<string>("General", "Main_13", "Memory: @1", (ConfigDescription)null);
			WaitingForPlayers = val.Bind<string>("General", "Main_14", "Waiting for all players to be ready...", (ConfigDescription)null);
			AllPlayersReady = val.Bind<string>("General", "Main_15", "All players ready! Starting playback.", (ConfigDescription)null);
			SyncStarting = val.Bind<string>("General", "Main_16", "Synchronized playback starting...", (ConfigDescription)null);
			PlayerTimedOut = val.Bind<string>("General", "Main_17", "Timed out waiting for players. Starting anyway.", (ConfigDescription)null);
			HostOnly = val.Bind<string>("General", "Main_18", "Only the host can use this command.", (ConfigDescription)null);
			Paused = val.Bind<string>("General", "Main_19", "Playback paused.", (ConfigDescription)null);
			Resumed = val.Bind<string>("General", "Main_20", "Playback resumed.", (ConfigDescription)null);
			Stopped = val.Bind<string>("General", "Main_21", "Playback stopped.", (ConfigDescription)null);
		}

		public void InitRU()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			ConfigFile val = new ConfigFile("TVSync\\lang\\television_ru.cfg", true);
			LibLoading = val.Bind<string>("General", "Main_1", "Пожалуйста, подождите, загружаются библиотеки...", (ConfigDescription)null);
			TVTooltip = val.Bind<string>("General", "Main_2", "Вкл/Выкл ТВ : [E]\n@2 - @3\n@1 громкость\nГромче [PG UP]\nТише [PG DN]", (ConfigDescription)null);
			LibReady = val.Bind<string>("General", "Main_3", "Библиотеки загружены.", (ConfigDescription)null);
			VideoLoading = val.Bind<string>("General", "Main_4", "Видео загружается!", (ConfigDescription)null);
			HelpText = val.Bind<string>("General", "Main_5", "/tplay [ССЫЛКА] - Проиграть\n/ttime [ВРЕМЯ] - Перемотать\n/treset - Сброс\n/tpause - Пауза\n/tresume - Продолжить\n/tstop - Остановить\n/tvolume [0-100] - Громкость\n/tposition [BOOL] - Запомнить позицию", (ConfigDescription)null);
			InvalidUrl = val.Bind<string>("General", "Main_6", "Неверный URL!", (ConfigDescription)null);
			PleaseWait = val.Bind<string>("General", "Main_7", "Пожалуйста подождите...", (ConfigDescription)null);
			VideoReady = val.Bind<string>("General", "Main_8", "Видео готово.", (ConfigDescription)null);
			VolumeChanged = val.Bind<string>("General", "Main_9", "@1 изменил громкость @2", (ConfigDescription)null);
			InvalidVolume = val.Bind<string>("General", "Main_10", "Неверная громкость!", (ConfigDescription)null);
			LinkInvalid = val.Bind<string>("General", "Main_11", "Ссылка недействительная!", (ConfigDescription)null);
			TimeChanged = val.Bind<string>("General", "Main_12", "Позиция: @1", (ConfigDescription)null);
			MemoryToggle = val.Bind<string>("General", "Main_13", "Запоминание: @1", (ConfigDescription)null);
			WaitingForPlayers = val.Bind<string>("General", "Main_14", "Ожидание готовности всех игроков...", (ConfigDescription)null);
			AllPlayersReady = val.Bind<string>("General", "Main_15", "Все игроки готовы! Начинаем воспроизведение.", (ConfigDescription)null);
			SyncStarting = val.Bind<string>("General", "Main_16", "Синхронное воспроизведение начинается...", (ConfigDescription)null);
			PlayerTimedOut = val.Bind<string>("General", "Main_17", "Время ожидания игроков истекло. Начинаем.", (ConfigDescription)null);
			HostOnly = val.Bind<string>("General", "Main_18", "Только хост может использовать эту команду.", (ConfigDescription)null);
			Paused = val.Bind<string>("General", "Main_19", "Воспроизведение приостановлено.", (ConfigDescription)null);
			Resumed = val.Bind<string>("General", "Main_20", "Воспроизведение продолжено.", (ConfigDescription)null);
			Stopped = val.Bind<string>("General", "Main_21", "Воспроизведение остановлено.", (ConfigDescription)null);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "TVSync.SynchronizedTV";

		public const string PLUGIN_NAME = "TVSync";

		public const string PLUGIN_VERSION = "2.0.0";
	}
	[BepInPlugin("TVSync.SynchronizedTV", "TVSync", "2.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		public static readonly string DataDir = Path.Combine("TVSync");

		public static readonly string LangDir = Path.Combine(DataDir, "lang");

		public static readonly string OtherDir = Path.Combine(DataDir, "other");

		public static readonly string YtDlpPath = Path.Combine(OtherDir, "yt-dlp.exe");

		public static readonly string CachePath = Path.Combine(DataDir, "cache");

		public static readonly string VersionPath = Path.Combine(OtherDir, "yt-dlp.version");

		public static readonly string VideoPath = Path.Combine(OtherDir, "test.mp4");

		public static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static TVSyncConfig SyncConfig { get; private set; }

		public static bool YtDlpReady { get; private set; }

		private void Awake()
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			SyncConfig = new TVSyncConfig();
			SyncConfig.Init();
			EnsureDirectories();
			CleanTempFiles();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"TVSync v2.0.0 loaded!");
			Thread thread = new Thread(UpdateYtDlp);
			thread.IsBackground = true;
			thread.Start();
			_harmony = new Harmony("TVSync.SynchronizedTV");
			_harmony.PatchAll(typeof(TVPatches));
			_harmony.PatchAll(typeof(HUDPatches));
			_harmony.PatchAll(typeof(NetworkPatches));
		}

		private void EnsureDirectories()
		{
			if (!Directory.Exists(LangDir))
			{
				Directory.CreateDirectory(LangDir);
			}
			if (!Directory.Exists(OtherDir))
			{
				Directory.CreateDirectory(OtherDir);
			}
		}

		private void CleanTempFiles()
		{
			TryDeleteFile(VideoPath);
			TryDeleteFile(VideoPath + ".part");
			if (!File.Exists(CachePath))
			{
				File.WriteAllText(CachePath, "");
			}
		}

		private static void TryDeleteFile(string path)
		{
			try
			{
				if (File.Exists(path))
				{
					File.Delete(path);
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not delete " + path + ": " + ex.Message));
			}
		}

		private void UpdateYtDlp()
		{
			try
			{
				string latestYtDlpTag = GetLatestYtDlpTag();
				if (string.IsNullOrEmpty(latestYtDlpTag))
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"TVSync: Could not determine latest yt-dlp version. Skipping update.");
					YtDlpReady = File.Exists(YtDlpPath);
					return;
				}
				bool flag = true;
				if (File.Exists(YtDlpPath) && File.Exists(VersionPath) && File.ReadAllText(VersionPath).Trim() == latestYtDlpTag)
				{
					flag = false;
				}
				if (flag)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("TVSync: Downloading yt-dlp version " + latestYtDlpTag + "..."));
					DownloadFileSync(new Uri("https://github.com/yt-dlp/yt-dlp/releases/download/" + latestYtDlpTag + "/yt-dlp.exe"), YtDlpPath);
					if (File.Exists(YtDlpPath))
					{
						File.WriteAllText(VersionPath, latestYtDlpTag);
					}
				}
				YtDlpReady = File.Exists(YtDlpPath);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"TVSync: yt-dlp ready.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("TVSync: yt-dlp update failed: " + ex.Message));
				YtDlpReady = File.Exists(YtDlpPath);
			}
		}

		private string GetLatestYtDlpTag()
		{
			try
			{
				HttpWebRequest obj = (HttpWebRequest)WebRequest.Create("https://github.com/yt-dlp/yt-dlp/releases/latest");
				obj.Method = "HEAD";
				obj.AllowAutoRedirect = false;
				obj.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)";
				obj.Timeout = 10000;
				using WebResponse webResponse = obj.GetResponse();
				string text = webResponse.Headers["Location"];
				if (!string.IsNullOrEmpty(text))
				{
					return text.Substring(text.LastIndexOf('/') + 1);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("TVSync: Failed to fetch latest yt-dlp tag: " + ex.Message));
			}
			return null;
		}

		public static void DownloadFileSync(Uri uri, string filename)
		{
			try
			{
				TryDeleteFile(filename);
				using WebClient webClient = new WebClient();
				webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
				webClient.DownloadFile(uri, filename);
			}
			catch (Exception ex)
			{
				Log.LogError((object)$"TVSync: Download failed for {uri}: {ex.Message}");
				throw;
			}
		}
	}
	public class TVController : MonoBehaviour
	{
		private bool _isDownloading;

		private bool _isWaitingForReady;

		private bool _isPlaying;

		private bool _isPaused;

		private double _scheduledStartNetworkTime;

		private string _currentUrl = "";

		private double _hostPlaybackTime;

		private float _lastSyncTime;

		private float _syncInterval;

		private float _driftThresholdSec;

		private float _maxSeekThresholdSec;

		private float _readyTimeoutSec;

		public static TVController Instance { get; private set; }

		public TVScript TV { get; private set; }

		public bool PositionMemory { get; set; }

		public double SavedPosition { get; set; }

		public float Volume
		{
			get
			{
				if (!((Object)(object)TV != (Object)null))
				{
					return 0.5f;
				}
				return TV.tvSFX.volume;
			}
			set
			{
				if ((Object)(object)TV != (Object)null)
				{
					TV.tvSFX.volume = Mathf.Clamp01(value);
				}
				SaveVolume(value);
			}
		}

		public double CurrentTime
		{
			get
			{
				if (!((Object)(object)TV != (Object)null) || !((Object)(object)TV.video != (Object)null))
				{
					return 0.0;
				}
				return TV.video.time;
			}
		}

		public double TotalTime
		{
			get
			{
				if (!((Object)(object)TV != (Object)null) || !((Object)(object)TV.video != (Object)null))
				{
					return 0.0;
				}
				return TV.video.length;
			}
		}

		public bool IsPlaying => _isPlaying;

		public bool IsPaused => _isPaused;

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		public void Init(TVScript tvScript)
		{
			TV = tvScript;
			TVSyncConfig syncConfig = Plugin.SyncConfig;
			_syncInterval = syncConfig.SyncIntervalSec.Value;
			_driftThresholdSec = syncConfig.DriftThresholdMs.Value / 1000f;
			_maxSeekThresholdSec = syncConfig.MaxSeekThresholdMs.Value / 1000f;
			_readyTimeoutSec = syncConfig.ReadyTimeoutSec.Value;
			LoadVolume();
		}

		private void Update()
		{
			if (!((Object)(object)TV == (Object)null) && !((Object)(object)TV.video == (Object)null))
			{
				if (NetworkSync.IsHost && _isPlaying && !_isPaused && Time.time - _lastSyncTime >= _syncInterval)
				{
					_lastSyncTime = Time.time;
					BroadcastSyncState();
				}
				if (_isWaitingForReady && _scheduledStartNetworkTime > 0.0 && GetNetworkTime() >= _scheduledStartNetworkTime)
				{
					_isWaitingForReady = false;
					BeginPlaybackNow();
				}
			}
		}

		public void HostPlayVideo(string url, HUDManager hud, string playerName)
		{
			if (_isDownloading)
			{
				ShowChat(Plugin.SyncConfig.LangData.VideoLoading.Value);
				return;
			}
			_currentUrl = url;
			NetworkSync.SendPlayVideo(url);
			ShowChat(Plugin.SyncConfig.LangData.PleaseWait.Value);
		}

		public void HostPause()
		{
			if (NetworkSync.IsHost && _isPlaying && !_isPaused)
			{
				NetworkSync.SendPause(TV.video.time);
			}
		}

		public void HostResume()
		{
			if (NetworkSync.IsHost && _isPaused)
			{
				double time = TV.video.time;
				double networkTime = GetNetworkTime() + 0.5;
				NetworkSync.SendResume(time, networkTime);
			}
		}

		public void HostSeek(double seekTime)
		{
			if (NetworkSync.IsHost)
			{
				NetworkSync.SendSeek(seekTime);
			}
		}

		public void HostStop()
		{
			if (NetworkSync.IsHost)
			{
				NetworkSync.SendStop();
			}
		}

		public void HostRestart()
		{
			if (NetworkSync.IsHost)
			{
				NetworkSync.SendSeek(0.0);
			}
		}

		public void OnPlayVideoReceived(string url)
		{
			_currentUrl = url;
			_isPlaying = false;
			_isPaused = false;
			((MonoBehaviour)this).StartCoroutine(DownloadAndPrepareCoroutine(url));
		}

		public void OnSyncStateReceived(PlaybackState state)
		{
			if (!NetworkSync.IsHost && _isPlaying && _isPaused == state.IsPaused)
			{
				ApplyDriftCorrection(state);
			}
		}

		public void OnPauseReceived(double pauseTime)
		{
			if (!((Object)(object)TV == (Object)null) && !((Object)(object)TV.video == (Object)null))
			{
				_isPaused = true;
				TV.video.Pause();
				TV.tvSFX.Pause();
				TV.video.time = pauseTime;
			}
		}

		public void OnResumeReceived(double resumeTime, double scheduledNetworkTime)
		{
			if (!((Object)(object)TV == (Object)null) && !((Object)(object)TV.video == (Object)null))
			{
				TV.video.time = resumeTime;
				_scheduledStartNetworkTime = scheduledNetworkTime;
				_isPaused = false;
				_isWaitingForReady = true;
			}
		}

		public void OnSeekReceived(double seekTime)
		{
			if (!((Object)(object)TV == (Object)null) && !((Object)(object)TV.video == (Object)null))
			{
				TV.video.time = seekTime;
				_hostPlaybackTime = seekTime;
			}
		}

		public void OnStopReceived()
		{
			if (!((Object)(object)TV == (Object)null))
			{
				_isPlaying = false;
				_isPaused = false;
				_isWaitingForReady = false;
				if (TV.tvOn)
				{
					TV.tvOn = false;
					SetTVScreenMaterial(on: false);
					TV.tvSFX.Stop();
					TV.video.Stop();
				}
			}
		}

		public void OnChatReceived(string message)
		{
			ShowChat(message);
		}

		public void SendCurrentStateToClient(ulong clientId)
		{
			if (NetworkSync.IsHost)
			{
				PlaybackState state = new PlaybackState
				{
					PlaybackTime = (((Object)(object)TV != (Object)null) ? TV.video.time : 0.0),
					NetworkTime = GetNetworkTime(),
					PlaybackSpeed = 1f,
					IsPaused = _isPaused,
					IsPlaying = _isPlaying,
					VideoUrl = _currentUrl
				};
				NetworkSync.SendSyncStateToClient(clientId, state);
			}
		}

		private IEnumerator DownloadAndPrepareCoroutine(string url)
		{
			_isDownloading = true;
			try
			{
				if (!Directory.Exists(Plugin.OtherDir))
				{
					Directory.CreateDirectory(Plugin.OtherDir);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("TVSync: Could not create directory: " + ex.Message));
			}
			if (TV.tvOn)
			{
				TV.video.Stop();
				TV.tvSFX.Stop();
			}
			string videoPath = Path.GetFullPath(Plugin.VideoPath);
			try
			{
				if (File.Exists(videoPath))
				{
					File.Delete(videoPath);
				}
				string path = videoPath + ".part";
				if (File.Exists(path))
				{
					File.Delete(path);
				}
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("TVSync: Could not clean old video: " + ex2.Message));
			}
			ShowChat(Plugin.SyncConfig.LangData.PleaseWait.Value);
			float waitStart = Time.time;
			while (!Plugin.YtDlpReady && Time.time - waitStart < 60f)
			{
				yield return (object)new WaitForSeconds(1f);
			}
			if (!Plugin.YtDlpReady)
			{
				Plugin.Log.LogError((object)"TVSync: yt-dlp not ready after 60s");
				_isDownloading = false;
				ShowChat(Plugin.SyncConfig.LangData.LinkInvalid.Value);
				yield break;
			}
			string ytDlpFullPath = Path.GetFullPath(Plugin.YtDlpPath);
			string workDir = Path.GetFullPath(Plugin.OtherDir);
			string text = Path.Combine(workDir, "cookies.txt");
			string text2 = (File.Exists(text) ? ("--cookies \"" + text + "\" ") : "");
			string arguments = text2 + "-f \"b[height<=360][ext=mp4]/b[ext=mp4]/b\" --force-ipv4 -N 4 \"" + url + "\" -o test.mp4";
			Plugin.Log.LogInfo((object)("TVSync: Starting download: " + ytDlpFullPath + " " + arguments));
			Plugin.Log.LogInfo((object)("TVSync: Working directory: " + workDir));
			bool downloadComplete = false;
			bool downloadFailed = false;
			string errorMsg = "";
			Thread downloadThread = new Thread((ThreadStart)delegate
			{
				try
				{
					Process process = new Process();
					process.StartInfo.FileName = ytDlpFullPath;
					process.StartInfo.UseShellExecute = false;
					process.StartInfo.Arguments = arguments;
					process.StartInfo.WorkingDirectory = workDir;
					process.StartInfo.CreateNoWindow = true;
					process.StartInfo.RedirectStandardError = true;
					process.StartInfo.RedirectStandardOutput = true;
					process.Start();
					string text4 = process.StandardOutput.ReadToEnd();
					string text5 = process.StandardError.ReadToEnd();
					process.WaitForExit(120000);
					if (!process.HasExited)
					{
						process.Kill();
						downloadFailed = true;
						errorMsg = "yt-dlp timed out after 2 minutes";
					}
					else
					{
						if (!string.IsNullOrEmpty(text5))
						{
							Plugin.Log.LogWarning((object)("TVSync: yt-dlp stderr: " + text5));
						}
						if (!string.IsNullOrEmpty(text4))
						{
							Plugin.Log.LogInfo((object)("TVSync: yt-dlp stdout: " + text4));
						}
						Plugin.Log.LogInfo((object)$"TVSync: yt-dlp exit code: {process.ExitCode}");
						for (int i = 0; i < 30; i++)
						{
							if (File.Exists(videoPath))
							{
								downloadComplete = true;
								return;
							}
							Thread.Sleep(500);
						}
						downloadFailed = true;
						errorMsg = "Video file not found at: " + videoPath;
					}
				}
				catch (Exception ex3)
				{
					downloadFailed = true;
					errorMsg = ex3.ToString();
				}
			})
			{
				IsBackground = true
			};
			downloadThread.Start();
			while (downloadThread.IsAlive)
			{
				yield return (object)new WaitForSeconds(0.5f);
			}
			if (downloadFailed || !downloadComplete)
			{
				Plugin.Log.LogError((object)("TVSync: Download failed: " + errorMsg));
				_isDownloading = false;
				ShowChat(Plugin.SyncConfig.LangData.LinkInvalid.Value);
				yield break;
			}
			string text3 = "file:///" + videoPath.Replace('\\', '/');
			Plugin.Log.LogInfo((object)("TVSync: Loading video from: " + text3));
			TV.video.source = (VideoSource)1;
			TV.video.url = text3;
			TV.video.controlledAudioTrackCount = 1;
			TV.video.audioOutputMode = (VideoAudioOutputMode)1;
			TV.video.SetTargetAudioSource((ushort)0, TV.tvSFX);
			TV.video.playbackSpeed = 1f;
			TV.video.Prepare();
			float prepStart = Time.time;
			while (!TV.video.isPrepared && Time.time - prepStart < 30f)
			{
				yield return (object)new WaitForSeconds(0.1f);
			}
			_isDownloading = false;
			if (!TV.video.isPrepared)
			{
				Plugin.Log.LogError((object)"TVSync: VideoPlayer failed to prepare");
				ShowChat(Plugin.SyncConfig.LangData.LinkInvalid.Value);
				yield break;
			}
			Plugin.Log.LogInfo((object)"TVSync: Video prepared. Reporting ready.");
			ShowChat(Plugin.SyncConfig.LangData.VideoReady.Value);
			NetworkSync.SendClientReady();
			if (NetworkSync.IsHost)
			{
				((MonoBehaviour)this).StartCoroutine(WaitForReadyAndStart());
			}
		}

		private IEnumerator WaitForReadyAndStart()
		{
			int count = NetworkManager.Singleton.ConnectedClientsIds.Count;
			_isWaitingForReady = true;
			ShowChat(Plugin.SyncConfig.LangData.WaitingForPlayers.Value);
			NetworkSync.BeginWaitForReady(count, delegate
			{
				Plugin.Log.LogInfo((object)"TVSync: All clients ready!");
			});
			float waitStart = Time.time;
			while (NetworkSync.ReadyCount < NetworkSync.ExpectedCount)
			{
				if (Time.time - waitStart > _readyTimeoutSec)
				{
					Plugin.Log.LogWarning((object)$"TVSync: Timeout waiting for clients ({NetworkSync.ReadyCount}/{NetworkSync.ExpectedCount})");
					ShowChat(Plugin.SyncConfig.LangData.PlayerTimedOut.Value);
					NetworkSync.ForceAllReady();
					break;
				}
				yield return (object)new WaitForSeconds(0.25f);
			}
			ShowChat(Plugin.SyncConfig.LangData.AllPlayersReady.Value);
			NetworkSync.SendResume(0.0, _scheduledStartNetworkTime = GetNetworkTime() + 1.0);
		}

		private void BeginPlaybackNow()
		{
			if (!((Object)(object)TV == (Object)null) && !((Object)(object)TV.video == (Object)null))
			{
				Plugin.Log.LogInfo((object)"TVSync: Beginning synchronized playback!");
				TV.tvOn = true;
				SetTVScreenMaterial(on: true);
				TV.video.Play();
				TV.tvSFX.Play();
				TV.tvSFX.PlayOneShot(TV.switchTVOn);
				WalkieTalkie.TransmitOneShotAudio(TV.tvSFX, TV.switchTVOn, 1f);
				_isPlaying = true;
				_isPaused = false;
				_lastSyncTime = Time.time;
			}
		}

		private void BroadcastSyncState()
		{
			if (!((Object)(object)TV == (Object)null) && !((Object)(object)TV.video == (Object)null))
			{
				NetworkSync.SendSyncState(new PlaybackState
				{
					PlaybackTime = TV.video.time,
					NetworkTime = GetNetworkTime(),
					PlaybackSpeed = TV.video.playbackSpeed,
					IsPaused = _isPaused,
					IsPlaying = _isPlaying,
					VideoUrl = _currentUrl
				});
			}
		}

		private void ApplyDriftCorrection(PlaybackState hostState)
		{
			if (!((Object)(object)TV == (Object)null) && !((Object)(object)TV.video == (Object)null) && TV.video.isPlaying)
			{
				double num = GetNetworkTime() - hostState.NetworkTime;
				double num2 = hostState.PlaybackTime + num * (double)hostState.PlaybackSpeed;
				double time = TV.video.time;
				double num3 = num2 - time;
				double num4 = Math.Abs(num3);
				if (num4 < (double)_driftThresholdSec)
				{
					TV.video.playbackSpeed = 1f;
				}
				else if (num4 > (double)_maxSeekThresholdSec)
				{
					Plugin.Log.LogInfo((object)$"TVSync: Large drift {num3:F3}s — seeking to {num2:F2}");
					TV.video.time = num2;
					TV.video.playbackSpeed = 1f;
				}
				else
				{
					float playbackSpeed = ((num3 > 0.0) ? 1.03f : 0.97f);
					TV.video.playbackSpeed = playbackSpeed;
				}
			}
		}

		public void HandleLateJoin()
		{
			if (!NetworkSync.IsHost)
			{
				Plugin.Log.LogInfo((object)"TVSync: Requesting current state (late join)...");
				NetworkSync.SendRequestState();
			}
		}

		public void HandleFullStateSync(PlaybackState state)
		{
			if (!string.IsNullOrEmpty(state.VideoUrl) && state.IsPlaying)
			{
				_currentUrl = state.VideoUrl;
				((MonoBehaviour)this).StartCoroutine(LateJoinCoroutine(state));
			}
		}

		private IEnumerator LateJoinCoroutine(PlaybackState state)
		{
			yield return DownloadAndPrepareCoroutine(state.VideoUrl);
			if (TV.video.isPrepared)
			{
				double num = GetNetworkTime() - state.NetworkTime;
				double val = state.PlaybackTime + num * (double)state.PlaybackSpeed;
				TV.video.time = Math.Max(0.0, val);
				if (state.IsPaused)
				{
					TV.video.time = state.PlaybackTime;
					_isPaused = true;
				}
				else
				{
					BeginPlaybackNow();
				}
			}
		}

		public void SetTVScreenMaterial(bool on)
		{
			if (!((Object)(object)TV == (Object)null) && !((Object)(object)TV.tvMesh == (Object)null))
			{
				Material[] sharedMaterials = ((Renderer)TV.tvMesh).sharedMaterials;
				if (sharedMaterials.Length > 1)
				{
					sharedMaterials[1] = (on ? TV.tvOnMaterial : TV.tvOffMaterial);
					((Renderer)TV.tvMesh).sharedMaterials = sharedMaterials;
				}
				if ((Object)(object)TV.tvLight != (Object)null)
				{
					((Behaviour)TV.tvLight).enabled = on;
				}
			}
		}

		public void HandleTVToggle(bool on)
		{
			if (on)
			{
				if (PositionMemory)
				{
					TV.video.time = SavedPosition;
				}
				SetTVScreenMaterial(on: true);
				TV.video.Play();
				TV.tvSFX.Play();
				TV.tvSFX.PlayOneShot(TV.switchTVOn);
				WalkieTalkie.TransmitOneShotAudio(TV.tvSFX, TV.switchTVOn, 1f);
			}
			else
			{
				if (PositionMemory)
				{
					SavedPosition = TV.video.time;
				}
				SetTVScreenMaterial(on: false);
				TV.tvSFX.Stop();
				TV.video.Stop();
				TV.tvSFX.PlayOneShot(TV.switchTVOff);
				WalkieTalkie.TransmitOneShotAudio(TV.tvSFX, TV.switchTVOff, 1f);
			}
		}

		private void LoadVolume()
		{
			try
			{
				if (File.Exists(Plugin.CachePath))
				{
					string text = File.ReadAllText(Plugin.CachePath).Trim();
					if (!string.IsNullOrEmpty(text) && float.TryParse(text, out var result))
					{
						if ((Object)(object)TV != (Object)null)
						{
							TV.tvSFX.volume = Mathf.Clamp01(result);
						}
					}
					else if ((Object)(object)TV != (Object)null)
					{
						TV.tvSFX.volume = 0.5f;
					}
				}
				else if ((Object)(object)TV != (Object)null)
				{
					TV.tvSFX.volume = 0.5f;
				}
			}
			catch
			{
				if ((Object)(object)TV != (Object)null)
				{
					TV.tvSFX.volume = 0.5f;
				}
			}
		}

		private void SaveVolume(float vol)
		{
			try
			{
				File.WriteAllText(Plugin.CachePath, vol.ToString("F2"));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("TVSync: Could not save volume: " + ex.Message));
			}
		}

		public void ShowChat(string message)
		{
			HUDManager instance = HUDManager.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				ChatHelper.ShowMessage(instance, message, "TVSync");
			}
		}

		private static double GetNetworkTime()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsConnectedClient)
			{
				NetworkTime serverTime = NetworkManager.Singleton.ServerTime;
				return ((NetworkTime)(ref serverTime)).Time;
			}
			return Time.timeAsDouble;
		}
	}
}
namespace TVSync.Patches
{
	public static class TVPatches
	{
		[HarmonyPatch(typeof(TVScript), "OnEnable")]
		[HarmonyPrefix]
		public static bool OnEnable_Prefix(TVScript __instance)
		{
			TVController tVController = ((Component)__instance).GetComponent<TVController>();
			if ((Object)(object)tVController == (Object)null)
			{
				tVController = ((Component)__instance).gameObject.AddComponent<TVController>();
			}
			tVController.Init(__instance);
			__instance.video.clip = null;
			__instance.tvSFX.clip = null;
			__instance.video.Stop();
			__instance.tvSFX.Stop();
			return false;
		}

		[HarmonyPatch(typeof(TVScript), "TurnTVOnOff")]
		[HarmonyPrefix]
		public static bool TurnTVOnOff_Prefix(TVScript __instance, bool on)
		{
			__instance.tvOn = on;
			TVController instance = TVController.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.HandleTVToggle(on);
			}
			return false;
		}

		[HarmonyPatch(typeof(TVScript), "TVFinishedClip")]
		[HarmonyPrefix]
		public static bool TVFinishedClip_Prefix()
		{
			return false;
		}

		[HarmonyPatch(typeof(TVScript), "Update")]
		[HarmonyPrefix]
		public static bool Update_Prefix()
		{
			return false;
		}
	}
	public static class HUDPatches
	{
		private static readonly Regex UrlRegex = new Regex("^https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&\\/=]*)$", RegexOptions.Compiled);

		[HarmonyPatch(typeof(PlayerControllerB), "SetHoverTipAndCurrentInteractTrigger")]
		[HarmonyPostfix]
		public static void SetHoverTipAndCurrentInteractTrigger_Postfix(PlayerControllerB __instance)
		{
			InteractTrigger hoveringOverTrigger = __instance.hoveringOverTrigger;
			if ((Object)(object)hoveringOverTrigger == (Object)null || !hoveringOverTrigger.interactable || !((Object)(object)((Component)hoveringOverTrigger).transform.parent != (Object)null) || (!((Object)((Component)hoveringOverTrigger).transform.parent).name.Contains("Television") && !hoveringOverTrigger.hoverTip.Contains("Switch TV")))
			{
				return;
			}
			TVController instance = TVController.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.TV == (Object)null)
			{
				return;
			}
			if (!Plugin.YtDlpReady)
			{
				hoveringOverTrigger.hoverTip = Plugin.SyncConfig.LangData.LibLoading.Value;
				return;
			}
			double currentTime = instance.CurrentTime;
			double totalTime = instance.TotalTime;
			string newValue = FormatTime(currentTime);
			string newValue2 = FormatTime(totalTime);
			float volume = instance.Volume;
			hoveringOverTrigger.hoverTip = Plugin.SyncConfig.LangData.TVTooltip.Value.Replace("@1", $"{Mathf.RoundToInt(volume * 100f)}%").Replace("@2", newValue).Replace("@3", newValue2);
			if (((ButtonControl)Keyboard.current.pageDownKey).wasPressedThisFrame && volume > 0f)
			{
				instance.Volume = Mathf.Max(0f, volume - 0.1f);
			}
			if (((ButtonControl)Keyboard.current.pageUpKey).wasPressedThisFrame && volume < 1f)
			{
				instance.Volume = Mathf.Min(1f, volume + 0.1f);
			}
		}

		[HarmonyPatch(typeof(HUDManager), "Start")]
		[HarmonyPostfix]
		public static void HUDStart_Postfix(HUDManager __instance)
		{
			__instance.chatTextField.characterLimit = 200;
		}

		[HarmonyPatch(typeof(HUDManager), "AddPlayerChatMessageServerRpc")]
		[HarmonyPostfix]
		public static void AddPlayerChatMessageServerRpc_Postfix(HUDManager __instance, string chatMessage, int playerId)
		{
			if (chatMessage.Length > 50)
			{
				typeof(HUDManager).GetMethod("AddPlayerChatMessageClientRpc", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(__instance, new object[2] { chatMessage, playerId });
			}
		}

		[HarmonyPatch(typeof(HUDManager), "AddChatMessage")]
		[HarmonyPrefix]
		public static bool AddChatMessage_Prefix(HUDManager __instance, string chatMessage, string nameOfUserWhoTyped)
		{
			if (__instance.lastChatMessage != chatMessage)
			{
				__instance.lastChatMessage = chatMessage;
				__instance.PingHUDElement(__instance.Chat, 4f, 1f, 0.2f);
				if (__instance.ChatMessageHistory.Count >= 4)
				{
					__instance.ChatMessageHistory.RemoveAt(0);
				}
				StringBuilder stringBuilder = new StringBuilder(chatMessage);
				for (int i = 0; i < Math.Min(4, StartOfRound.Instance.allPlayerScripts.Length); i++)
				{
					stringBuilder.Replace($"[playerNum{i}]", StartOfRound.Instance.allPlayerScripts[i].playerUsername);
				}
				chatMessage = stringBuilder.ToString();
				string item = (string.IsNullOrEmpty(nameOfUserWhoTyped) ? ("<color=#7069ff>" + chatMessage + "</color>") : ("<color=#FF0000>" + nameOfUserWhoTyped + "</color>: <color=#FFFF00>'" + chatMessage + "'</color>"));
				__instance.ChatMessageHistory.Add(item);
				StringBuilder stringBuilder2 = new StringBuilder();
				for (int j = 0; j < __instance.ChatMessageHistory.Count; j++)
				{
					stringBuilder2.Append("\n");
					stringBuilder2.Append(__instance.ChatMessageHistory[j]);
				}
				((TMP_Text)__instance.chatText).text = stringBuilder2.ToString();
				ProcessCommand(__instance, chatMessage, nameOfUserWhoTyped);
			}
			return false;
		}

		[HarmonyPatch(typeof(HUDManager), "SubmitChat_performed")]
		[HarmonyPrefix]
		public static bool SubmitChat_performed_Prefix(HUDManager __instance, ref CallbackContext context)
		{
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			if (!((CallbackContext)(ref context)).performed)
			{
				return false;
			}
			__instance.localPlayer = GameNetworkManager.Instance.localPlayerController;
			if ((Object)(object)__instance.localPlayer == (Object)null)
			{
				return false;
			}
			if (!__instance.localPlayer.isTypingChat)
			{
				return false;
			}
			if ((!((NetworkBehaviour)__instance.localPlayer).IsOwner || (((NetworkBehaviour)__instance).IsServer && !__instance.localPlayer.isHostPlayerObject)) && !__instance.localPlayer.isTestingPlayer)
			{
				return false;
			}
			if (__instance.localPlayer.isPlayerDead)
			{
				return false;
			}
			if (!string.IsNullOrEmpty(__instance.chatTextField.text) && __instance.chatTextField.text.Length < 200)
			{
				__instance.AddTextToChatOnServer(__instance.chatTextField.text, (int)__instance.localPlayer.playerClientId);
			}
			for (int i = 0; i < StartOfRound.Instance.allPlayerScripts.Length; i++)
			{
				if (StartOfRound.Instance.allPlayerScripts[i].isPlayerControlled && Vector3.Distance(((Component)GameNetworkManager.Instance.localPlayerController).transform.position, ((Component)StartOfRound.Instance.allPlayerScripts[i]).transform.position) > 24.4f && (!GameNetworkManager.Instance.localPlayerController.holdingWalkieTalkie || !StartOfRound.Instance.allPlayerScripts[i].holdingWalkieTalkie))
				{
					__instance.playerCouldRecieveTextChatAnimator.SetTrigger("ping");
					break;
				}
			}
			__instance.localPlayer.isTypingChat = false;
			__instance.chatTextField.text = "";
			EventSystem.current.SetSelectedGameObject((GameObject)null);
			__instance.PingHUDElement(__instance.Chat, 2f, 1f, 0.2f);
			((Behaviour)__instance.typingIndicator).enabled = false;
			return false;
		}

		[HarmonyPatch(typeof(HUDManager), "Update")]
		[HarmonyPrefix]
		public static void HUDUpdate_Prefix(HUDManager __instance)
		{
		}

		private static void ProcessCommand(HUDManager hud, string chatMessage, string senderName)
		{
			if (string.IsNullOrEmpty(chatMessage))
			{
				return;
			}
			string[] array = chatMessage.Split(' ');
			string text = array[0].Replace("/", "").ToLower();
			Lang langData = Plugin.SyncConfig.LangData;
			TVController instance = TVController.Instance;
			if (text == null)
			{
				return;
			}
			switch (text.Length)
			{
			case 5:
				switch (text[1])
				{
				case 'h':
					if (text == "thelp")
					{
						ChatHelper.ResetDedup();
						ChatHelper.ShowMessage(hud, langData.HelpText.Value, "TVSync");
					}
					break;
				case 'p':
				{
					if (!(text == "tplay") || !NetworkSync.IsHost)
					{
						break;
					}
					if (array.Length < 2)
					{
						ChatHelper.ShowMessage(hud, langData.InvalidUrl.Value, "TVSync");
						break;
					}
					if (!UrlRegex.IsMatch(array[1]))
					{
						ChatHelper.ShowMessage(hud, langData.InvalidUrl.Value, "TVSync");
						break;
					}
					string text2 = (array[1].Contains("://") ? array[1].Split(new string[1] { "://" }, StringSplitOptions.None)[1] : array[1]);
					if (!text2.Contains("youtube.com") && !text2.Contains("youtu.be"))
					{
						ChatHelper.ShowMessage(hud, langData.InvalidUrl.Value, "TVSync");
					}
					else if (array[1].Contains("list"))
					{
						ChatHelper.ShowMessage(hud, langData.InvalidUrl.Value, "TVSync");
					}
					else if ((Object)(object)instance != (Object)null)
					{
						instance.HostPlayVideo(array[1], hud, senderName);
					}
					break;
				}
				case 't':
					if (text == "ttime" && NetworkSync.IsHost && array.Length >= 2 && !((Object)(object)instance == (Object)null))
					{
						double num = ParseTimeString(array[1]);
						if (num >= 0.0)
						{
							instance.HostSeek(num);
							ChatHelper.ResetDedup();
							ChatHelper.ShowMessage(hud, langData.TimeChanged.Value.Replace("@1", FormatTime(num)), "TVSync");
						}
					}
					break;
				case 's':
					if (text == "tstop" && NetworkSync.IsHost && !((Object)(object)instance == (Object)null))
					{
						instance.HostStop();
						ChatHelper.ResetDedup();
						ChatHelper.ShowMessage(hud, langData.Stopped.Value, "TVSync");
					}
					break;
				}
				break;
			case 6:
				switch (text[1])
				{
				case 'r':
					if (text == "treset" && NetworkSync.IsHost && !((Object)(object)instance == (Object)null))
					{
						instance.HostRestart();
						ChatHelper.ResetDedup();
						ChatHelper.ShowMessage(hud, langData.TimeChanged.Value.Replace("@1", "00:00:00"), "TVSync");
					}
					break;
				case 'p':
					if (text == "tpause" && NetworkSync.IsHost && !((Object)(object)instance == (Object)null))
					{
						instance.HostPause();
						ChatHelper.ResetDedup();
						ChatHelper.ShowMessage(hud, langData.Paused.Value, "TVSync");
					}
					break;
				}
				break;
			case 7:
				switch (text[1])
				{
				case 'r':
					if (text == "tresume" && NetworkSync.IsHost && !((Object)(object)instance == (Object)null))
					{
						instance.HostResume();
						ChatHelper.ResetDedup();
						ChatHelper.ShowMessage(hud, langData.Resumed.Value, "TVSync");
					}
					break;
				case 'v':
				{
					if (!(text == "tvolume") || array.Length < 2 || !(senderName == GameNetworkManager.Instance.localPlayerController.playerUsername))
					{
						break;
					}
					if (int.TryParse(array[1], out var result))
					{
						float volume = Mathf.Clamp01((float)result / 100f);
						if ((Object)(object)instance != (Object)null)
						{
							instance.Volume = volume;
							ChatHelper.ResetDedup();
							ChatHelper.ShowMessage(hud, langData.VolumeChanged.Value.Replace("@1", senderName ?? "").Replace("@2", array[1] + "%"), "TVSync");
						}
					}
					else
					{
						ChatHelper.ShowMessage(hud, langData.InvalidVolume.Value, "TVSync");
					}
					break;
				}
				}
				break;
			case 9:
				if (text == "tposition" && array.Length >= 2 && !((Object)(object)instance == (Object)null))
				{
					if (array[1].ToLower() == "true")
					{
						instance.PositionMemory = true;
					}
					else if (array[1].ToLower() == "false")
					{
						instance.PositionMemory = false;
					}
					string newValue = (instance.PositionMemory ? "enabled" : "disabled");
					ChatHelper.ResetDedup();
					ChatHelper.ShowMessage(hud, langData.MemoryToggle.Value.Replace("@1", newValue), "TVSync");
				}
				break;
			case 8:
				break;
			}
		}

		private static double ParseTimeString(string timeStr)
		{
			string[] array = timeStr.Split(':');
			try
			{
				return array.Length switch
				{
					1 => Convert.ToDouble(array[0]), 
					2 => Convert.ToInt32(array[0]) * 60 + Convert.ToInt32(array[1]), 
					3 => Convert.ToInt32(array[0]) * 3600 + Convert.ToInt32(array[1]) * 60 + Convert.ToInt32(array[2]), 
					_ => -1.0, 
				};
			}
			catch
			{
				return -1.0;
			}
		}

		public static string FormatTime(double seconds)
		{
			int num = (int)seconds;
			int num2 = num / 3600;
			int num3 = num % 3600 / 60;
			int num4 = num % 60;
			return $"{num2:00}:{num3:00}:{num4:00}";
		}
	}
	public static class NetworkPatches
	{
		[HarmonyPatch(typeof(GameNetworkManager), "StartDisconnect")]
		[HarmonyPostfix]
		public static void StartDisconnect_Postfix()
		{
			NetworkSync.Unregister();
		}

		[HarmonyPatch(typeof(StartOfRound), "Start")]
		[HarmonyPostfix]
		public static void StartOfRound_Start_Postfix()
		{
			NetworkSync.Register();
			if (!NetworkSync.IsHost && (Object)(object)TVController.Instance != (Object)null)
			{
				TVController.Instance.HandleLateJoin();
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "OnPlayerConnectedClientRpc")]
		[HarmonyPostfix]
		public static void OnPlayerConnected_Postfix(ulong clientId)
		{
			if (NetworkSync.IsHost)
			{
				Plugin.Log.LogInfo((object)$"TVSync: Client {clientId} connected. Will send state on request.");
			}
		}
	}
}
namespace TVSync.Networking
{
	public static class NetworkSync
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnReceivePlayVideo;

			public static HandleNamedMessageDelegate <1>__OnReceiveSyncState;

			public static HandleNamedMessageDelegate <2>__OnReceiveClientReady;

			public static HandleNamedMessageDelegate <3>__OnReceivePause;

			public static HandleNamedMessageDelegate <4>__OnReceiveResume;

			public static HandleNamedMessageDelegate <5>__OnReceiveSeek;

			public static HandleNamedMessageDelegate <6>__OnReceiveStop;

			public static HandleNamedMessageDelegate <7>__OnReceiveChat;

			public static HandleNamedMessageDelegate <8>__OnReceiveRequestState;
		}

		private const string MSG_PLAY_VIDEO = "TVSync_PlayVideo";

		private const string MSG_SYNC_STATE = "TVSync_SyncState";

		private const string MSG_CLIENT_READY = "TVSync_ClientReady";

		private const string MSG_PAUSE = "TVSync_Pause";

		private const string MSG_RESUME = "TVSync_Resume";

		private const string MSG_SEEK = "TVSync_Seek";

		private const string MSG_STOP = "TVSync_Stop";

		private const string MSG_CHAT = "TVSync_Chat";

		private const string MSG_REQUEST_STATE = "TVSync_RequestState";

		private static HashSet<ulong> _readyClients = new HashSet<ulong>();

		private static int _expectedReadyCount = 0;

		private static Action _onAllReady;

		public static bool IsRegistered { get; private set; }

		public static bool IsHost
		{
			get
			{
				if ((Object)(object)NetworkManager.Singleton != (Object)null)
				{
					return NetworkManager.Singleton.IsHost;
				}
				return false;
			}
		}

		public static bool IsConnected
		{
			get
			{
				if ((Object)(object)NetworkManager.Singleton != (Object)null)
				{
					return NetworkManager.Singleton.IsConnectedClient;
				}
				return false;
			}
		}

		public static int ReadyCount => _readyClients.Count;

		public static int ExpectedCount => _expectedReadyCount;

		public static void Register()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_005a: 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_0065: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_00cc: 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_00d7: Expected O, but got Unknown
			//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
			//IL_0118: 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: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			if (IsRegistered)
			{
				return;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null);
			if (val != null)
			{
				object obj = <>O.<0>__OnReceivePlayVideo;
				if (obj == null)
				{
					HandleNamedMessageDelegate val2 = OnReceivePlayVideo;
					<>O.<0>__OnReceivePlayVideo = val2;
					obj = (object)val2;
				}
				val.RegisterNamedMessageHandler("TVSync_PlayVideo", (HandleNamedMessageDelegate)obj);
				object obj2 = <>O.<1>__OnReceiveSyncState;
				if (obj2 == null)
				{
					HandleNamedMessageDelegate val3 = OnReceiveSyncState;
					<>O.<1>__OnReceiveSyncState = val3;
					obj2 = (object)val3;
				}
				val.RegisterNamedMessageHandler("TVSync_SyncState", (HandleNamedMessageDelegate)obj2);
				object obj3 = <>O.<2>__OnReceiveClientReady;
				if (obj3 == null)
				{
					HandleNamedMessageDelegate val4 = OnReceiveClientReady;
					<>O.<2>__OnReceiveClientReady = val4;
					obj3 = (object)val4;
				}
				val.RegisterNamedMessageHandler("TVSync_ClientReady", (HandleNamedMessageDelegate)obj3);
				object obj4 = <>O.<3>__OnReceivePause;
				if (obj4 == null)
				{
					HandleNamedMessageDelegate val5 = OnReceivePause;
					<>O.<3>__OnReceivePause = val5;
					obj4 = (object)val5;
				}
				val.RegisterNamedMessageHandler("TVSync_Pause", (HandleNamedMessageDelegate)obj4);
				object obj5 = <>O.<4>__OnReceiveResume;
				if (obj5 == null)
				{
					HandleNamedMessageDelegate val6 = OnReceiveResume;
					<>O.<4>__OnReceiveResume = val6;
					obj5 = (object)val6;
				}
				val.RegisterNamedMessageHandler("TVSync_Resume", (HandleNamedMessageDelegate)obj5);
				object obj6 = <>O.<5>__OnReceiveSeek;
				if (obj6 == null)
				{
					HandleNamedMessageDelegate val7 = OnReceiveSeek;
					<>O.<5>__OnReceiveSeek = val7;
					obj6 = (object)val7;
				}
				val.RegisterNamedMessageHandler("TVSync_Seek", (HandleNamedMessageDelegate)obj6);
				object obj7 = <>O.<6>__OnReceiveStop;
				if (obj7 == null)
				{
					HandleNamedMessageDelegate val8 = OnReceiveStop;
					<>O.<6>__OnReceiveStop = val8;
					obj7 = (object)val8;
				}
				val.RegisterNamedMessageHandler("TVSync_Stop", (HandleNamedMessageDelegate)obj7);
				object obj8 = <>O.<7>__OnReceiveChat;
				if (obj8 == null)
				{
					HandleNamedMessageDelegate val9 = OnReceiveChat;
					<>O.<7>__OnReceiveChat = val9;
					obj8 = (object)val9;
				}
				val.RegisterNamedMessageHandler("TVSync_Chat", (HandleNamedMessageDelegate)obj8);
				object obj9 = <>O.<8>__OnReceiveRequestState;
				if (obj9 == null)
				{
					HandleNamedMessageDelegate val10 = OnReceiveRequestState;
					<>O.<8>__OnReceiveRequestState = val10;
					obj9 = (object)val10;
				}
				val.RegisterNamedMessageHandler("TVSync_RequestState", (HandleNamedMessageDelegate)obj9);
				IsRegistered = true;
				Plugin.Log.LogInfo((object)"TVSync: Network message handlers registered.");
			}
		}

		public static void Unregister()
		{
			if (IsRegistered)
			{
				NetworkManager singleton = NetworkManager.Singleton;
				CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null);
				if (val != null)
				{
					val.UnregisterNamedMessageHandler("TVSync_PlayVideo");
					val.UnregisterNamedMessageHandler("TVSync_SyncState");
					val.UnregisterNamedMessageHandler("TVSync_ClientReady");
					val.UnregisterNamedMessageHandler("TVSync_Pause");
					val.UnregisterNamedMessageHandler("TVSync_Resume");
					val.UnregisterNamedMessageHandler("TVSync_Seek");
					val.UnregisterNamedMessageHandler("TVSync_Stop");
					val.UnregisterNamedMessageHandler("TVSync_Chat");
					val.UnregisterNamedMessageHandler("TVSync_RequestState");
					IsRegistered = false;
					_readyClients.Clear();
				}
			}
		}

		public static void SendPlayVideo(string url)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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)
			if (IsHost)
			{
				FastBufferWriter writer = BeginMessage();
				WriteString(writer, url);
				SendToAllClients("TVSync_PlayVideo", writer);
				((FastBufferWriter)(ref writer)).Dispose();
				TVController.Instance?.OnPlayVideoReceived(url);
			}
		}

		public static void SendSyncState(PlaybackState state)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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)
			if (IsHost)
			{
				FastBufferWriter writer = BeginMessage();
				state.Serialize(writer);
				SendToAllClients("TVSync_SyncState", writer);
				((FastBufferWriter)(ref writer)).Dispose();
			}
		}

		public static void SendPause(double pauseTime)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (IsHost)
			{
				FastBufferWriter writer = BeginMessage();
				((FastBufferWriter)(ref writer)).WriteValueSafe<double>(ref pauseTime, default(ForPrimitives));
				SendToAllClients("TVSync_Pause", writer);
				((FastBufferWriter)(ref writer)).Dispose();
				TVController.Instance?.OnPauseReceived(pauseTime);
			}
		}

		public static void SendResume(double resumeTime, double networkTime)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			if (IsHost)
			{
				FastBufferWriter writer = BeginMessage();
				((FastBufferWriter)(ref writer)).WriteValueSafe<double>(ref resumeTime, default(ForPrimitives));
				((FastBufferWriter)(ref writer)).WriteValueSafe<double>(ref networkTime, default(ForPrimitives));
				SendToAllClients("TVSync_Resume", writer);
				((FastBufferWriter)(ref writer)).Dispose();
				TVController.Instance?.OnResumeReceived(resumeTime, networkTime);
			}
		}

		public static void SendSeek(double seekTime)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (IsHost)
			{
				FastBufferWriter writer = BeginMessage();
				((FastBufferWriter)(ref writer)).WriteValueSafe<double>(ref seekTime, default(ForPrimitives));
				SendToAllClients("TVSync_Seek", writer);
				((FastBufferWriter)(ref writer)).Dispose();
				TVController.Instance?.OnSeekReceived(seekTime);
			}
		}

		public static void SendStop()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (IsHost)
			{
				FastBufferWriter writer = BeginMessage();
				byte b = 0;
				((FastBufferWriter)(ref writer)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
				SendToAllClients("TVSync_Stop", writer);
				((FastBufferWriter)(ref writer)).Dispose();
				TVController.Instance?.OnStopReceived();
			}
		}

		public static void SendChat(string message)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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)
			if (IsHost)
			{
				FastBufferWriter writer = BeginMessage();
				WriteString(writer, message);
				SendToAllClients("TVSync_Chat", writer);
				((FastBufferWriter)(ref writer)).Dispose();
			}
		}

		public static void SendClientReady()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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)
			if (IsHost)
			{
				OnClientReady(NetworkManager.Singleton.LocalClientId);
				return;
			}
			FastBufferWriter writer = BeginMessage();
			ulong localClientId = NetworkManager.Singleton.LocalClientId;
			((FastBufferWriter)(ref writer)).WriteValueSafe<ulong>(ref localClientId, default(ForPrimitives));
			SendToServer("TVSync_ClientReady", writer);
			((FastBufferWriter)(ref writer)).Dispose();
		}

		public static void SendRequestState()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_0025: 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)
			if (!IsHost)
			{
				FastBufferWriter writer = BeginMessage();
				ulong localClientId = NetworkManager.Singleton.LocalClientId;
				((FastBufferWriter)(ref writer)).WriteValueSafe<ulong>(ref localClientId, default(ForPrimitives));
				SendToServer("TVSync_RequestState", writer);
				((FastBufferWriter)(ref writer)).Dispose();
			}
		}

		public static void BeginWaitForReady(int expectedCount, Action onAllReady)
		{
			_readyClients.Clear();
			_expectedReadyCount = expectedCount;
			_onAllReady = onAllReady;
		}

		private static void OnClientReady(ulong clientId)
		{
			_readyClients.Add(clientId);
			Plugin.Log.LogInfo((object)$"TVSync: Client {clientId} ready ({_readyClients.Count}/{_expectedReadyCount})");
			if (_readyClients.Count >= _expectedReadyCount)
			{
				_onAllReady?.Invoke();
				_onAllReady = null;
			}
		}

		public static void ForceAllReady()
		{
			_onAllReady?.Invoke();
			_onAllReady = null;
		}

		private static void OnReceivePlayVideo(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			string text = ReadString(reader);
			Plugin.Log.LogInfo((object)("TVSync: Received PlayVideo: " + text));
			TVController.Instance?.OnPlayVideoReceived(text);
		}

		private static void OnReceiveSyncState(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			PlaybackState state = PlaybackState.Deserialize(reader);
			TVController.Instance?.OnSyncStateReceived(state);
		}

		private static void OnReceiveClientReady(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)
			ulong clientId = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref clientId, default(ForPrimitives));
			if (IsHost)
			{
				OnClientReady(clientId);
			}
		}

		private static void OnReceivePause(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)
			double pauseTime = default(double);
			((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref pauseTime, default(ForPrimitives));
			TVController.Instance?.OnPauseReceived(pauseTime);
		}

		private static void OnReceiveResume(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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			double resumeTime = default(double);
			((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref resumeTime, default(ForPrimitives));
			double scheduledNetworkTime = default(double);
			((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref scheduledNetworkTime, default(ForPrimitives));
			TVController.Instance?.OnResumeReceived(resumeTime, scheduledNetworkTime);
		}

		private static void OnReceiveSeek(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)
			double seekTime = default(double);
			((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref seekTime, default(ForPrimitives));
			TVController.Instance?.OnSeekReceived(seekTime);
		}

		private static void OnReceiveStop(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)
			byte b = default(byte);
			((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
			TVController.Instance?.OnStopReceived();
		}

		private static void OnReceiveChat(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			string message = ReadString(reader);
			TVController.Instance?.OnChatReceived(message);
		}

		private static void OnReceiveRequestState(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)
			ulong clientId = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref clientId, default(ForPrimitives));
			if (IsHost)
			{
				TVController.Instance?.SendCurrentStateToClient(clientId);
			}
		}

		private static FastBufferWriter BeginMessage()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return new FastBufferWriter(4096, (Allocator)2, -1);
		}

		private static void SendToAllClients(string msgName, FastBufferWriter writer)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null);
			if (val == null)
			{
				return;
			}
			foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds)
			{
				if (connectedClientsId != NetworkManager.Singleton.LocalClientId)
				{
					val.SendNamedMessage(msgName, connectedClientsId, writer, (NetworkDelivery)3);
				}
			}
		}

		private static void SendToServer(string msgName, FastBufferWriter writer)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null);
			if (val != null)
			{
				val.SendNamedMessage(msgName, 0uL, writer, (NetworkDelivery)3);
			}
		}

		public static void SendSyncStateToClient(ulong clientId, PlaybackState state)
		{
			//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_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null);
			if (val != null)
			{
				FastBufferWriter val2 = BeginMessage();
				state.Serialize(val2);
				val.SendNamedMessage("TVSync_SyncState", clientId, val2, (NetworkDelivery)3);
				((FastBufferWriter)(ref val2)).Dispose();
			}
		}

		private static void WriteString(FastBufferWriter writer, string value)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			byte[] bytes = Encoding.UTF8.GetBytes(value ?? "");
			int num = bytes.Length;
			((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num, default(ForPrimitives));
			if (bytes.Length != 0)
			{
				((FastBufferWriter)(ref writer)).WriteBytesSafe(bytes, -1, 0);
			}
		}

		private static string ReadString(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)
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			if (num <= 0)
			{
				return "";
			}
			byte[] bytes = new byte[num];
			((FastBufferReader)(ref reader)).ReadBytesSafe(ref bytes, num, 0);
			return Encoding.UTF8.GetString(bytes);
		}
	}
	public struct PlaybackState
	{
		public double PlaybackTime;

		public double NetworkTime;

		public float PlaybackSpeed;

		public bool IsPaused;

		public bool IsPlaying;

		public string VideoUrl;

		public void Serialize(FastBufferWriter writer)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_004c: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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)
			((FastBufferWriter)(ref writer)).WriteValueSafe<double>(ref PlaybackTime, default(ForPrimitives));
			((FastBufferWriter)(ref writer)).WriteValueSafe<double>(ref NetworkTime, default(ForPrimitives));
			((FastBufferWriter)(ref writer)).WriteValueSafe<float>(ref PlaybackSpeed, default(ForPrimitives));
			((FastBufferWriter)(ref writer)).WriteValueSafe<bool>(ref IsPaused, default(ForPrimitives));
			((FastBufferWriter)(ref writer)).WriteValueSafe<bool>(ref IsPlaying, default(ForPrimitives));
			byte[] bytes = Encoding.UTF8.GetBytes(VideoUrl ?? "");
			int num = bytes.Length;
			((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num, default(ForPrimitives));
			if (bytes.Length != 0)
			{
				((FastBufferWriter)(ref writer)).WriteBytesSafe(bytes, -1, 0);
			}
		}

		public static PlaybackState Deserialize(FastBufferReader reader)
		{
			//IL_0013: 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_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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			PlaybackState result = default(PlaybackState);
			((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref result.PlaybackTime, default(ForPrimitives));
			((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref result.NetworkTime, default(ForPrimitives));
			((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref result.PlaybackSpeed, default(ForPrimitives));
			((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref result.IsPaused, default(ForPrimitives));
			((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref result.IsPlaying, default(ForPrimitives));
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			if (num > 0)
			{
				byte[] bytes = new byte[num];
				((FastBufferReader)(ref reader)).ReadBytesSafe(ref bytes, num, 0);
				result.VideoUrl = Encoding.UTF8.GetString(bytes);
			}
			else
			{
				result.VideoUrl = "";
			}
			return result;
		}
	}
}