Decompiled source of TootTallySpectator v1.0.7

plugins/TootTallySpectator.dll

Decompiled 8 months ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BaboonAPI.Hooks.Initializer;
using BaboonAPI.Hooks.Tracks;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Microsoft.FSharp.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using TootTallyAccounts;
using TootTallyCore;
using TootTallyCore.APIServices;
using TootTallyCore.Graphics;
using TootTallyCore.Graphics.Animations;
using TootTallyCore.Utils.Assets;
using TootTallyCore.Utils.Helpers;
using TootTallyCore.Utils.TootTallyGlobals;
using TootTallyCore.Utils.TootTallyModules;
using TootTallyCore.Utils.TootTallyNotifs;
using TootTallyGameModifiers;
using TootTallySettings;
using TootTallyTrombuddies;
using TootTallyWebsocketLibs;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using WebSocketSharp;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("TootTallySpectator")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Share your gameplay and watch your trombuddies play")]
[assembly: AssemblyFileVersion("1.0.7.0")]
[assembly: AssemblyInformationalVersion("1.0.7")]
[assembly: AssemblyProduct("TootTallySpectator")]
[assembly: AssemblyTitle("TootTallySpectator")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.7.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TootTallySpectator
{
	public static class CompatibilityPatches
	{
		[HarmonyPatch(typeof(TrombuddiesGameObjectFactory), "CreateUserCard")]
		[HarmonyPostfix]
		public static void InitOverlay(User user, ref GameObject __result)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_00c7: 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)
			GameObject gameObject = ((Component)__result.transform.Find("LatencyFG/RightContent")).gameObject;
			if (!SpectatingManager.IsHosting && user.id == TootTallyUser.userInfo.id)
			{
				GameObjectFactory.CreateCustomButton(gameObject.transform, Vector2.zero, Vector2.one * 45f, AssetManager.GetSprite("SpectatorIcon.png"), "SpectateUserButton", (Action)delegate
				{
					SpectatingManager.OnSpectateButtonPress(user.id, user.username);
				});
			}
			if (user.id != TootTallyUser.userInfo.id && SpectatingManager.currentSpectatorIDList.Contains(user.id))
			{
				GameObjectFactory.CreateCustomButton(gameObject.transform, Vector2.zero, Vector2.one * 45f, AssetManager.GetSprite("SpectatorIcon.png"), "SpectateUserButton", (Action)delegate
				{
					SpectatingManager.OnSpectateButtonPress(user.id, user.username);
				});
			}
		}

		[HarmonyPatch(typeof(UserStatusUpdater), "SetPlayingUserStatus")]
		[HarmonyPrefix]
		public static bool OverwritePlayingUserStatus()
		{
			if (!SpectatingManager.IsSpectating)
			{
				return true;
			}
			UserStatusManager.SetUserStatus((UserStatus)6);
			return false;
		}

		[HarmonyPatch(typeof(GameController), "pauseQuitLevel")]
		[HarmonyPostfix]
		public static void OnQuitLoadBackedupModifiers()
		{
			if (SpectatingManager.IsSpectating)
			{
				GameModifierManager.LoadBackedupModifiers();
			}
		}

		[HarmonyPatch(typeof(PointSceneController), "updateSave")]
		[HarmonyPrefix]
		public static bool AvoidSaveChange()
		{
			return !SpectatingManager.IsSpectating;
		}

		[HarmonyPatch(typeof(PointSceneController), "checkScoreCheevos")]
		[HarmonyPrefix]
		public static bool AvoidAchievementCheck()
		{
			return !SpectatingManager.IsSpectating;
		}

		[HarmonyPatch(typeof(UserStatusManager), "OnHeartBeatRequestResponse")]
		[HarmonyPostfix]
		public static void UpdateSpecList()
		{
			SpectatingManager.UpdateSpectatorIDList();
		}
	}
	[BepInPlugin("TootTallySpectator", "TootTallySpectator", "1.0.7")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin, ITootTallyModule
	{
		public static Plugin Instance;

		private const string CONFIG_NAME = "TootTally.cfg";

		private Harmony _harmony;

		public static TootTallySettingPage settingPage;

		public ConfigEntry<bool> ModuleConfigEnabled { get; set; }

		public bool IsConfigInitialized { get; set; }

		public string Name
		{
			get
			{
				return "TootTallySpectator";
			}
			set
			{
				Name = value;
			}
		}

		public ConfigEntry<bool> AllowSpectate { get; private set; }

		public ConfigEntry<bool> ShowSpectatorCount { get; private set; }

		public static void LogInfo(string msg)
		{
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)msg);
		}

		public static void LogError(string msg)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)msg);
		}

		private void Awake()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if (!((Object)(object)Instance != (Object)null))
			{
				Instance = this;
				_harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				GameInitializationEvent.Register(((BaseUnityPlugin)this).Info, (Action)TryInitialize);
			}
		}

		private void TryInitialize()
		{
			ModuleConfigEnabled = ((BaseUnityPlugin)Plugin.Instance).Config.Bind<bool>("Modules", "Spectator", true, "Share your gameplay and watch other plays.");
			TootTallyModuleManager.AddModule((ITootTallyModule)(object)this);
			Plugin.Instance.AddModuleToSettingPage((ITootTallyModule)(object)this);
		}

		public void LoadModule()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_00ae: 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_0116: Unknown result type (might be due to invalid IL or missing references)
			AssetManager.LoadAssets(Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location), "Assets"));
			string text = Path.Combine(Paths.BepInExRootPath, "config/");
			ConfigFile val = new ConfigFile(text + "TootTally.cfg", true);
			AllowSpectate = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Allow Spectate", true, "Allow other players to spectate you while playing.");
			ShowSpectatorCount = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show Spectator Count", true, "Show the number of spectator while playing.");
			settingPage = TootTallySettingsManager.AddNewPage("Spectator", "Spectator", 40f, new Color(0f, 0f, 0f, 0f));
			settingPage.AddToggle("AllowSpectate", new Vector2(400f, 50f), "Allow Spectate", AllowSpectate, (UnityAction<bool>)SpectatingManager.OnAllowHostConfigChange);
			settingPage.AddToggle("ShowSpectatorCount", new Vector2(400f, 50f), "Show Spectator Count", ShowSpectatorCount, (UnityAction<bool>)null);
			Plugin.TryAddThunderstoreIconToPageButton(((BaseUnityPlugin)Instance).Info.Location, Name, settingPage);
			((Component)this).gameObject.AddComponent<SpectatingManager>();
			_harmony.PatchAll(typeof(SpectatingManager.SpectatingManagerPatches));
			_harmony.PatchAll(typeof(CompatibilityPatches));
			LogInfo("Module loaded!");
		}

		public void UnloadModule()
		{
			Object.DestroyImmediate((Object)(object)((Component)this).gameObject.GetComponent<SpectatingManager>());
			_harmony.UnpatchSelf();
			settingPage.Remove();
			LogInfo("Module unloaded!");
		}
	}
	public class SpectatingManager : MonoBehaviour
	{
		public enum DataType
		{
			UserState,
			SongInfo,
			FrameData,
			TootData,
			NoteData,
			SpectatorInfo
		}

		public enum UserState
		{
			None,
			SelectingSong,
			Paused,
			Playing,
			Restarting,
			Quitting,
			PointScene,
			GettingReady
		}

		public enum SceneType
		{
			None,
			LevelSelect,
			GameController,
			HomeController
		}

		public interface ISocketMessage
		{
			string dataType { get; }
		}

		public struct SocketUserState : ISocketMessage
		{
			public int userState { get; set; }

			public string dataType => DataType.UserState.ToString();
		}

		public struct SocketFrameData : ISocketMessage
		{
			public float time { get; set; }

			public float noteHolder { get; set; }

			public float pointerPosition { get; set; }

			public string dataType => DataType.FrameData.ToString();
		}

		public struct SocketTootData : ISocketMessage
		{
			public float time { get; set; }

			public float noteHolder { get; set; }

			public bool isTooting { get; set; }

			public string dataType => DataType.TootData.ToString();
		}

		public struct SocketSongInfo : ISocketMessage
		{
			public string trackRef { get; set; }

			public int songID { get; set; }

			public float gameSpeed { get; set; }

			public float scrollSpeed { get; set; }

			public string gamemodifiers { get; set; }

			public string dataType => DataType.SongInfo.ToString();
		}

		public struct SocketNoteData : ISocketMessage
		{
			public int noteID { get; set; }

			public double noteScoreAverage { get; set; }

			public bool champMode { get; set; }

			public int multiplier { get; set; }

			public int totalScore { get; set; }

			public bool releasedButtonBetweenNotes { get; set; }

			public float health { get; set; }

			public int highestCombo { get; set; }

			public string dataType => DataType.NoteData.ToString();
		}

		public struct SocketSpectatorInfo : ISocketMessage
		{
			public string hostName { get; set; }

			public int count { get; set; }

			public List<string> spectators { get; set; }

			public string dataType => DataType.SpectatorInfo.ToString();
		}

		public class SocketDataConverter : JsonConverter
		{
			public override bool CanWrite => false;

			public override bool CanConvert(Type objectType)
			{
				return objectType == typeof(ISocketMessage);
			}

			public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
			{
				JObject val = JObject.Load(reader);
				object obj = Enum.Parse(typeof(DataType), Extensions.Value<string>((IEnumerable<JToken>)val["dataType"]));
				if (1 == 0)
				{
				}
				if (!(obj is DataType))
				{
					goto IL_00be;
				}
				switch ((DataType)obj)
				{
				case DataType.UserState:
					break;
				case DataType.FrameData:
					goto IL_006e;
				case DataType.TootData:
					goto IL_007e;
				case DataType.SongInfo:
					goto IL_008e;
				case DataType.NoteData:
					goto IL_009e;
				case DataType.SpectatorInfo:
					goto IL_00ae;
				default:
					goto IL_00be;
				}
				object result = ((JToken)val).ToObject<SocketUserState>(serializer);
				goto IL_00c2;
				IL_00c2:
				if (1 == 0)
				{
				}
				return result;
				IL_00be:
				result = null;
				goto IL_00c2;
				IL_00ae:
				result = ((JToken)val).ToObject<SocketSpectatorInfo>(serializer);
				goto IL_00c2;
				IL_009e:
				result = ((JToken)val).ToObject<SocketNoteData>(serializer);
				goto IL_00c2;
				IL_007e:
				result = ((JToken)val).ToObject<SocketTootData>(serializer);
				goto IL_00c2;
				IL_006e:
				result = ((JToken)val).ToObject<SocketFrameData>(serializer);
				goto IL_00c2;
				IL_008e:
				result = ((JToken)val).ToObject<SocketSongInfo>(serializer);
				goto IL_00c2;
			}

			public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
			{
				throw new NotImplementedException();
			}
		}

		public static class SpectatingManagerPatches
		{
			private static LevelSelectController _levelSelectControllerInstance;

			private static GameController _gameControllerInstance;

			private static PointSceneController _pointSceneControllerInstance;

			private static UserState _lastSpecState = UserState.None;

			private static UserState _currentSpecState = UserState.None;

			private static List<SocketFrameData> _frameData = new List<SocketFrameData>();

			private static List<SocketTootData> _tootData = new List<SocketTootData>();

			private static List<SocketNoteData> _noteData = new List<SocketNoteData>();

			private static SocketFrameData _lastFrame;

			private static SocketFrameData _currentFrame;

			private static SocketTootData _currentTootData;

			private static SocketNoteData _currentNoteData;

			private static SocketSongInfo _lastSongInfo;

			private static SocketSongInfo _currentSongInfo;

			private static TromboneTrack _lastTrackData;

			private static bool _isTooting;

			private static int _frameIndex;

			private static int _tootIndex;

			private static bool _waitingToSync;

			private static bool _spectatingStarting;

			private static bool _wasSpectating;

			private const float SYNC_BUFFER = 1f;

			private static bool _lastIsTooting;

			private static float _specTracktime;

			private static float _elapsedTime;

			private static bool _isQuickRestarting;

			private static readonly float _targetFramerate = ((Application.targetFrameRate > 60 || Application.targetFrameRate < 1) ? 60 : Application.targetFrameRate);

			private static SocketSongInfo _lastHostSongInfo;

			private static UserState _currentHostState;

			private static UserState _lastHostState;

			[HarmonyPatch(typeof(HomeController), "Start")]
			[HarmonyPostfix]
			public static void InitOverlay()
			{
				SpectatingOverlay.Initialize();
			}

			[HarmonyPatch(typeof(LevelSelectController), "Start")]
			[HarmonyPostfix]
			public static void SetLevelSelectUserStatusOnAdvanceSongs(LevelSelectController __instance)
			{
				_gameControllerInstance = null;
				_pointSceneControllerInstance = null;
				_levelSelectControllerInstance = __instance;
				_spectatingStarting = false;
				_lastSongInfo.trackRef = "";
				_wasSpectating = false;
				if (IsHosting)
				{
					SetCurrentUserState(UserState.SelectingSong);
				}
				else if (IsSpectating)
				{
					SpectatingOverlay.SetCurrentUserState(UserState.SelectingSong);
				}
				else if (Plugin.Instance.AllowSpectate.Value && TootTallyUser.userInfo.id != 0)
				{
					CreateUniqueSpectatingConnection(TootTallyUser.userInfo.id, TootTallyUser.userInfo.username);
					SpectatingOverlay.HideViewerIcon();
				}
				SpectatingOverlay.HidePauseText();
				SpectatingOverlay.HideMarquee();
			}

			[HarmonyPatch(typeof(GameController), "Start")]
			[HarmonyPostfix]
			public static void OnGameControllerStart(GameController __instance)
			{
				if (IsHosting && _currentHostState != UserState.GettingReady)
				{
					if (_lastHostSongInfo.trackRef != "")
					{
						hostedSpectatingSystem.SendSongInfoToSocket(_lastHostSongInfo);
					}
					SetCurrentUserState(UserState.GettingReady);
				}
				_isQuickRestarting = false;
				_pointSceneControllerInstance = null;
				_levelSelectControllerInstance = null;
				_gameControllerInstance = __instance;
				_waitingToSync = (_wasSpectating = IsSpectating);
				if (IsSpectating)
				{
					_frameIndex = 0;
					_tootIndex = 0;
					_lastFrame.time = 0f;
					float num2 = (_currentFrame.pointerPosition = 0f);
					float time = (_currentFrame.noteHolder = num2);
					_currentFrame.time = time;
					time = (_currentFrame.noteHolder = 0f);
					_currentTootData.time = time;
					_currentTootData.isTooting = false;
					_isTooting = false;
					_elapsedTime = 0f;
					if (_lastTrackData != null)
					{
						SpectatingOverlay.ShowMarquee(_spectatingSystemList.Last().spectatorName, _lastTrackData.trackname_short, _lastSongInfo.gameSpeed, _lastSongInfo.gamemodifiers);
					}
				}
			}

			[HarmonyPatch(typeof(GameController), "playsong")]
			[HarmonyPrefix]
			public static bool OverwriteStartSongIfSyncRequired(GameController __instance)
			{
				if (!IsSpectating || IsHosting)
				{
					return true;
				}
				if (ShouldWaitForSync(out _waitingToSync))
				{
					TootTallyNotifManager.DisplayNotif("Waiting to sync with host...", 6f);
				}
				return !_waitingToSync;
			}

			private static bool ShouldWaitForSync(out bool waitForSync)
			{
				waitForSync = true;
				if (_frameData != null && _frameData.Count > 0 && _frameData.Last().time >= 1f && _currentSpecState != UserState.GettingReady && _currentSpecState != UserState.Restarting)
				{
					SpectatingOverlay.SetCurrentUserState(UserState.Playing);
					waitForSync = false;
				}
				return waitForSync;
			}

			[HarmonyPatch(typeof(GameController), "playsong")]
			[HarmonyPostfix]
			public static void SetPlayingUserStatus(GameController __instance)
			{
				//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				if (IsHosting)
				{
					SetCurrentUserState(UserState.Playing);
				}
				if (IsSpectating && _frameData != null && _frameData.Count > 0 && _frameData[_frameIndex].time - __instance.musictrack.time >= 1f)
				{
					Plugin.LogInfo("Syncing track with replay data...");
					float specTracktime = (__instance.musictrack.time = _frameData[_frameIndex].time);
					_specTracktime = specTracktime;
					__instance.noteholderr.anchoredPosition = new Vector2(_frameData[_frameIndex].noteHolder, __instance.noteholderr.anchoredPosition.y);
				}
			}

			[HarmonyPatch(typeof(GameController), "isNoteButtonPressed")]
			[HarmonyPostfix]
			public static void GameControllerIsNoteButtonPressedPostfixPatch(GameController __instance, ref bool __result)
			{
				//IL_006b: Unknown result type (might be due to invalid IL or missing references)
				if (IsSpectating)
				{
					__result = _isTooting;
				}
				else if (IsHosting && _lastIsTooting != __result && !__instance.paused && !__instance.retrying && !__instance.quitting)
				{
					hostedSpectatingSystem.SendTootData(__instance.musictrack.time - (float)GlobalVariables.localsettings.latencyadjust / 1000f, __instance.noteholderr.anchoredPosition.x, __result);
				}
				_lastIsTooting = __result;
			}

			[HarmonyPatch(typeof(PointSceneController), "Start")]
			[HarmonyPostfix]
			public static void OnPointSceneControllerStartSetInstance(PointSceneController __instance)
			{
				_levelSelectControllerInstance = null;
				_gameControllerInstance = null;
				_pointSceneControllerInstance = __instance;
				if (IsHosting)
				{
					SetCurrentUserState(UserState.PointScene);
				}
				SpectatingOverlay.HideViewerIcon();
				SpectatingOverlay.HideStopSpectatingButton();
				SpectatingOverlay.HideMarquee();
			}

			[HarmonyPatch(typeof(PointSceneController), "Update")]
			[HarmonyPostfix]
			public static void GetOutOfPointSceneIfHostLeft(PointSceneController __instance)
			{
				if (IsSpectating && _lastSpecState == UserState.PointScene && !__instance.clickedleave)
				{
					if (_currentSpecState == UserState.SelectingSong)
					{
						BackToLevelSelect();
					}
					else if (_currentSpecState == UserState.Playing)
					{
						RetryFromPointScene();
					}
				}
			}

			public static void PlaybackSpectatingData(GameController __instance, float time)
			{
				Cursor.visible = true;
				if (_frameData != null && _tootData != null)
				{
					if (!__instance.controllermode)
					{
						__instance.controllermode = true;
					}
					if (_frameData.Count > 0)
					{
						PlaybackFrameData(time, __instance);
					}
					if (_tootData.Count > 0)
					{
						PlaybackTootData(time);
					}
					if (_frameData.Count > _frameIndex)
					{
						InterpolateCursorPosition(time, __instance);
					}
				}
			}

			private static void InterpolateCursorPosition(float trackTime, GameController __instance)
			{
				if (_currentFrame.time - _lastFrame.time > 0f)
				{
					float num = (trackTime - _lastFrame.time) / (_currentFrame.time - _lastFrame.time);
					float num2 = Mathf.Clamp(EasingHelper.Lerp(_lastFrame.pointerPosition, _currentFrame.pointerPosition, num), -300f, 300f);
					SetCursorPosition(__instance, num2);
					__instance.puppet_humanc.doPuppetControl((0f - num2) / 225f);
				}
				else
				{
					SetCursorPosition(__instance, _currentFrame.pointerPosition);
				}
			}

			private static void PlaybackFrameData(float trackTime, GameController __instance)
			{
				if (_lastFrame.time != _currentFrame.time && trackTime >= _currentFrame.time)
				{
					_lastFrame = _currentFrame;
				}
				if (_frameData.Count > _frameIndex && (_currentFrame.time == -1f || trackTime >= _currentFrame.time))
				{
					_frameIndex = _frameData.FindIndex((_frameIndex > 1) ? (_frameIndex - 1) : 0, (SocketFrameData x) => trackTime < x.time);
					if (_frameData.Count > _frameIndex && _frameIndex != -1)
					{
						_currentFrame = _frameData[_frameIndex];
					}
				}
			}

			private static void SetCursorPosition(GameController __instance, float newPosition)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				Vector3 localPosition = __instance.pointer.transform.localPosition;
				localPosition.y = newPosition;
				__instance.pointer.transform.localPosition = localPosition;
			}

			public static void OnFrameDataReceived(SocketFrameData frameData)
			{
				_frameData?.Add(frameData);
			}

			public static void OnTootDataReceived(SocketTootData tootData)
			{
				_tootData?.Add(tootData);
			}

			public static void OnNoteDataReceived(SocketNoteData noteData)
			{
				_noteData?.Add(noteData);
			}

			public static void OnSpectatorDataReceived(SocketSpectatorInfo specData)
			{
				SpectatingOverlay.UpdateViewerList(specData);
			}

			public static void PlaybackTootData(float trackTime)
			{
				if (trackTime >= _currentTootData.time && _isTooting != _currentTootData.isTooting)
				{
					_isTooting = _currentTootData.isTooting;
				}
				if (_tootData.Count > _tootIndex && trackTime >= _currentTootData.time)
				{
					_currentTootData = _tootData[_tootIndex++];
				}
			}

			public static void OnSongInfoReceived(SocketSongInfo info)
			{
				if (info.trackRef == "" || info.gameSpeed <= 0f)
				{
					Plugin.LogInfo("SongInfo went wrong.");
				}
				else
				{
					_lastSongInfo = info;
				}
			}

			public static void OnUserStateReceived(SocketUserState userState)
			{
				if (IsSpectating)
				{
					UserStateHandler((UserState)userState.userState);
				}
			}

			private static void UserStateHandler(UserState state)
			{
				_lastSpecState = _currentSpecState;
				_currentSpecState = state;
				switch (state)
				{
				case UserState.SelectingSong:
					if ((Object)(object)_pointSceneControllerInstance != (Object)null)
					{
						BackToLevelSelect();
					}
					break;
				case UserState.Playing:
					if ((Object)(object)_levelSelectControllerInstance != (Object)null)
					{
						TryStartSong();
					}
					else if ((Object)(object)_gameControllerInstance != (Object)null && _lastSpecState == UserState.Paused)
					{
						ResumeSong();
					}
					else if ((Object)(object)_pointSceneControllerInstance != (Object)null)
					{
						RetryFromPointScene();
					}
					break;
				case UserState.Paused:
					if ((Object)(object)_gameControllerInstance != (Object)null)
					{
						PauseSong();
					}
					break;
				case UserState.Quitting:
					if ((Object)(object)_gameControllerInstance != (Object)null && _waitingToSync)
					{
						ClearSpectatingData();
					}
					if ((Object)(object)_gameControllerInstance != (Object)null)
					{
						QuitSong();
					}
					break;
				case UserState.Restarting:
					if ((Object)(object)_gameControllerInstance != (Object)null && _waitingToSync)
					{
						ClearSpectatingData();
					}
					if ((Object)(object)_gameControllerInstance != (Object)null)
					{
						RestartSong();
					}
					break;
				case UserState.GettingReady:
					if ((Object)(object)_levelSelectControllerInstance != (Object)null)
					{
						TryStartSong();
					}
					else if ((Object)(object)_gameControllerInstance != (Object)null)
					{
						_waitingToSync = true;
					}
					break;
				case UserState.PointScene:
					break;
				}
			}

			private static void BackToLevelSelect()
			{
				_lastSongInfo.trackRef = "";
				_pointSceneControllerInstance.clickCont();
			}

			private static void RetryFromPointScene()
			{
				ClearSpectatingData();
				TootTallyGlobalVariables.gameSpeedMultiplier = _lastSongInfo.gameSpeed;
				GlobalVariables.gamescrollspeed = _lastSongInfo.scrollSpeed;
				Plugin.LogInfo("ScrollSpeed Set: " + _lastSongInfo.scrollSpeed);
				_pointSceneControllerInstance.clickRetry();
			}

			private static void ClearSpectatingData()
			{
				_frameData.Clear();
				_tootData.Clear();
				_noteData.Clear();
				_frameIndex = 0;
				_tootIndex = 0;
				_isTooting = false;
			}

			private static void TryStartSong()
			{
				if (_currentSpecState != UserState.SelectingSong)
				{
					if (_lastSongInfo.trackRef != null)
					{
						if (!FSharpOption<TromboneTrack>[TrackLookup.tryLookup(_lastSongInfo.trackRef)])
						{
							_lastTrackData = TrackLookup.lookup(_lastSongInfo.trackRef);
							_currentSongInfo = _lastSongInfo;
							_spectatingStarting = true;
							ClearSpectatingData();
							TootTallyGlobalVariables.gameSpeedMultiplier = _lastSongInfo.gameSpeed;
							GlobalVariables.gamescrollspeed = _lastSongInfo.scrollSpeed;
							Plugin.LogInfo("ScrollSpeed Set: " + _lastSongInfo.scrollSpeed);
							GameModifierManager.LoadModifiersFromString(_lastSongInfo.gamemodifiers);
							ClickPlay(_lastTrackData);
						}
						else
						{
							Plugin.LogInfo("Do not own the song " + _lastSongInfo.trackRef);
							TootTallyNotifManager.DisplayNotif("Do not own the song #" + _lastSongInfo.trackRef, 6f);
						}
					}
					else
					{
						TootTallyNotifManager.DisplayNotif("No SongInfo from host.", 6f);
					}
				}
				else
				{
					TootTallyNotifManager.DisplayNotif("Waiting for host to start a song.", 6f);
				}
			}

			private static void ClickPlay(TromboneTrack track)
			{
				_levelSelectControllerInstance.back_clicked = true;
				_levelSelectControllerInstance.bgmus.Stop();
				_levelSelectControllerInstance.clipPlayer.cancelCrossfades();
				_levelSelectControllerInstance.doSfx(_levelSelectControllerInstance.sfx_musend);
				LeanTween.moveX(_levelSelectControllerInstance.playbtnobj, 640f, 0.6f).setEaseInQuart();
				GlobalVariables.chosen_track = track.trackref;
				GlobalVariables.chosen_track_data = TrackLookup.toTrackData(track);
				_levelSelectControllerInstance.fadeOut("loader", 0.65f);
			}

			private static void ResumeSong()
			{
				_gameControllerInstance.pausecontroller.clickResume();
				SpectatingOverlay.HidePauseText();
			}

			private static void PauseSong()
			{
				if (!_gameControllerInstance.quitting && !_gameControllerInstance.level_finished && _gameControllerInstance.pausecontroller.done_animating && !_gameControllerInstance.freeplay)
				{
					_isTooting = false;
					_gameControllerInstance.notebuttonpressed = false;
					_gameControllerInstance.musictrack.Pause();
					_gameControllerInstance.sfxrefs.backfromfreeplay.Play();
					_gameControllerInstance.puppet_humanc.shaking = false;
					_gameControllerInstance.puppet_humanc.stopParticleEffects();
					_gameControllerInstance.puppet_humanc.playCameraRotationTween(false);
					_gameControllerInstance.paused = true;
					_gameControllerInstance.quitting = true;
					_gameControllerInstance.pausecanvas.SetActive(true);
					_gameControllerInstance.pausecontroller.showPausePanel();
					Cursor.visible = true;
					if (!_gameControllerInstance.track_is_pausable)
					{
						_gameControllerInstance.curtainc.closeCurtain(false);
					}
				}
			}

			public static void QuitSong()
			{
				_gameControllerInstance.quitting = true;
				ClearSpectatingData();
				_gameControllerInstance.pauseQuitLevel();
				SpectatingOverlay.HidePauseText();
				SpectatingOverlay.HideMarquee();
				SpectatingOverlay.HideStopSpectatingButton();
				SpectatingOverlay.HideViewerIcon();
			}

			private static void RestartSong()
			{
				ClearSpectatingData();
				_waitingToSync = IsSpectating;
				_gameControllerInstance.quitting = true;
				_gameControllerInstance.pauseRetryLevel();
				SpectatingOverlay.HidePauseText();
				SpectatingOverlay.HideMarquee();
				SpectatingOverlay.HideStopSpectatingButton();
				SpectatingOverlay.HideViewerIcon();
			}

			[HarmonyPatch(typeof(PauseCanvasController), "showPausePanel")]
			[HarmonyPrefix]
			public static void OnResumeSetUserStatus(PauseCanvasController __instance)
			{
				if (IsHosting)
				{
					SetCurrentUserState(UserState.Paused);
				}
				if (Input.GetKeyDown((KeyCode)27) && IsSpectating)
				{
					__instance.gc.quitting = true;
					__instance.gc.pauseQuitLevel();
					StopAllSpectator();
					TootTallyNotifManager.DisplayNotif("Stopped spectating.", 6f);
				}
				else if (IsSpectating)
				{
					__instance.panelobj.SetActive(false);
					SpectatingOverlay.ShowPauseText();
				}
			}

			[HarmonyPatch(typeof(PauseCanvasController), "clickResume")]
			[HarmonyPostfix]
			public static void OnPauseSetUserStatus()
			{
				if (IsHosting)
				{
					SetCurrentUserState(UserState.Playing);
				}
			}

			[HarmonyPatch(typeof(GameController), "pauseQuitLevel")]
			[HarmonyPostfix]
			public static void OnGameControllerUpdate()
			{
				if (IsHosting)
				{
					SetCurrentUserState(UserState.Quitting);
				}
				else
				{
					SpectatingOverlay.SetCurrentUserState(UserState.Quitting);
				}
			}

			[HarmonyPatch(typeof(GameController), "getScoreAverage")]
			[HarmonyPrefix]
			public static void OnGetScoreAveragePrefixSetCurrentNote(GameController __instance)
			{
				if (IsHosting)
				{
					hostedSpectatingSystem.SendNoteData(__instance.rainbowcontroller.champmode, __instance.multiplier, __instance.currentnoteindex, __instance.notescoreaverage, __instance.released_button_between_notes, __instance.totalscore, __instance.currenthealth, __instance.highestcombo_level);
				}
				else
				{
					if (!IsSpectating)
					{
						return;
					}
					if (_noteData.Count > 0 && _noteData.Last().noteID > __instance.currentnoteindex)
					{
						_currentNoteData = _noteData.Find((SocketNoteData x) => x.noteID == __instance.currentnoteindex);
					}
					if (_currentNoteData.noteID != -1)
					{
						__instance.rainbowcontroller.champmode = _currentNoteData.champMode;
						__instance.multiplier = _currentNoteData.multiplier;
						__instance.notescoreaverage = (float)_currentNoteData.noteScoreAverage;
						__instance.released_button_between_notes = _currentNoteData.releasedButtonBetweenNotes;
						if (__instance.currentscore < 0)
						{
							__instance.currentscore = _currentNoteData.totalScore;
						}
						__instance.totalscore = _currentNoteData.totalScore;
						__instance.currenthealth = _currentNoteData.health;
						__instance.highestcombo_level = _currentNoteData.highestCombo;
						_currentNoteData.noteID = -1;
					}
				}
			}

			[HarmonyPatch(typeof(GameController), "Update")]
			[HarmonyPostfix]
			public static void OnUpdatePlaybackSpectatingData(GameController __instance)
			{
				//IL_01db: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
				if (IsSpectating)
				{
					if (!__instance.quitting && !__instance.retrying && (_currentSpecState == UserState.SelectingSong || _lastSongInfo.trackRef == "" || _currentSongInfo.trackRef == "" || _lastSongInfo.trackRef != _currentSongInfo.trackRef))
					{
						QuitSong();
					}
					if (!_waitingToSync && !__instance.paused && !__instance.quitting && !__instance.retrying)
					{
						_specTracktime += Time.deltaTime * TootTallyGlobalVariables.gameSpeedMultiplier;
						if (Math.Abs(__instance.musictrack.time - _specTracktime) > 0.08f)
						{
							Plugin.LogInfo("Resynced track time...");
							_specTracktime = __instance.musictrack.time;
						}
						PlaybackSpectatingData(__instance, _specTracktime);
					}
					else if (_waitingToSync && __instance.curtainc.doneanimating && !ShouldWaitForSync(out _waitingToSync))
					{
						TootTallyNotifManager.DisplayNotif("Finished syncing with host.", 6f);
						__instance.startSong(false);
					}
				}
				else if (IsHosting && !__instance.paused && !__instance.quitting && !__instance.retrying)
				{
					_elapsedTime += Time.deltaTime;
					if (_elapsedTime >= 1f / _targetFramerate)
					{
						_elapsedTime = 0f;
						hostedSpectatingSystem.SendFrameData(__instance.musictrack.time - (float)GlobalVariables.localsettings.latencyadjust / 1000f, __instance.noteholderr.anchoredPosition.x, __instance.pointer.transform.localPosition.y);
					}
				}
				else if (IsHosting && __instance.restarttimer > 0.4f && !_isQuickRestarting)
				{
					_isQuickRestarting = true;
					SetCurrentUserState(UserState.Restarting);
				}
			}

			[HarmonyPatch(typeof(GameController), "pauseRetryLevel")]
			[HarmonyPostfix]
			public static void OnRetryingSetUserStatus()
			{
				if (IsHosting)
				{
					SetCurrentUserState(UserState.Restarting);
				}
			}

			[HarmonyPatch(typeof(LevelSelectController), "clickPlay")]
			[HarmonyPostfix]
			public static void OnLevelSelectControllerClickPlaySendToSocket(LevelSelectController __instance)
			{
				SocketSongInfo lastHostSongInfo = default(SocketSongInfo);
				lastHostSongInfo.trackRef = __instance.alltrackslist[__instance.songindex].trackref;
				lastHostSongInfo.songID = 0;
				lastHostSongInfo.gameSpeed = TootTallyGlobalVariables.gameSpeedMultiplier;
				lastHostSongInfo.scrollSpeed = GlobalVariables.gamescrollspeed;
				lastHostSongInfo.gamemodifiers = GameModifierManager.GetModifiersString();
				_lastHostSongInfo = lastHostSongInfo;
				if (!IsHosting && !IsSpectating && Plugin.Instance.AllowSpectate.Value && TootTallyUser.userInfo.id != 0)
				{
					CreateUniqueSpectatingConnection(TootTallyUser.userInfo.id, TootTallyUser.userInfo.username);
				}
				if (IsHosting)
				{
					hostedSpectatingSystem.SendSongInfoToSocket(_lastHostSongInfo);
					hostedSpectatingSystem.SendUserStateToSocket(UserState.GettingReady);
				}
				SpectatingOverlay.HideViewerIcon();
			}

			[HarmonyPatch(typeof(LevelSelectController), "clickPlay")]
			[HarmonyPrefix]
			public static bool OnLevelSelectControllerClickPlayOverwriteIfSpectating()
			{
				if (IsSpectating)
				{
					if (_spectatingStarting)
					{
						return true;
					}
					TryStartSong();
					return false;
				}
				return true;
			}

			[HarmonyPatch(typeof(LevelSelectController), "clickBack")]
			[HarmonyPostfix]
			public static void OnBackButtonClick()
			{
				SpectatingOverlay.HideStopSpectatingButton();
				SpectatingOverlay.HideViewerIcon();
				_levelSelectControllerInstance = null;
				if (IsHosting)
				{
					SetCurrentUserState(UserState.None);
				}
			}

			private static void SetCurrentUserState(UserState userState)
			{
				_lastHostState = _currentHostState;
				_currentHostState = userState;
				hostedSpectatingSystem.SendUserStateToSocket(userState);
				Plugin.LogInfo($"Current state changed from {_lastHostState} to {_currentHostState}");
				SpectatingOverlay.SetCurrentUserState(userState);
			}

			[HarmonyPatch(typeof(PointSceneController), "doCoins")]
			[HarmonyPostfix]
			public static void ReplayIndicator(PointSceneController __instance)
			{
				if (IsSpectating)
				{
					__instance.tootstext.text = "Spectating Done";
				}
			}

			public static void SendCurrentUserState()
			{
				hostedSpectatingSystem?.SendUserStateToSocket(_currentHostState);
			}
		}

		public static JsonConverter[] _dataConverter = (JsonConverter[])(object)new JsonConverter[1]
		{
			new SocketDataConverter()
		};

		private static List<SpectatingSystem> _spectatingSystemList;

		public static SpectatingSystem hostedSpectatingSystem;

		public static int[] currentSpectatorIDList;

		public static SocketSpectatorInfo nullSpecInfo = new SocketSpectatorInfo
		{
			count = 0
		};

		public static bool IsHosting => hostedSpectatingSystem != null && ((WebsocketManager)hostedSpectatingSystem).IsConnected && ((WebsocketManager)hostedSpectatingSystem).IsHost;

		public static bool IsSpectating => _spectatingSystemList != null && !IsHosting && _spectatingSystemList.Any((SpectatingSystem x) => ((WebsocketManager)x).IsConnected);

		public void Awake()
		{
			if (_spectatingSystemList == null)
			{
				_spectatingSystemList = new List<SpectatingSystem>();
			}
			if (Plugin.Instance.AllowSpectate.Value && TootTallyUser.userInfo != null && TootTallyUser.userInfo.id != 0)
			{
				CreateUniqueSpectatingConnection(TootTallyUser.userInfo.id, TootTallyUser.userInfo.username);
			}
			((MonoBehaviour)Plugin.Instance).StartCoroutine((IEnumerator)TootTallyAPIService.GetSpectatorIDList((Action<int[]>)delegate(int[] idList)
			{
				currentSpectatorIDList = idList;
			}));
		}

		public void Update()
		{
			_spectatingSystemList?.ForEach(delegate(SpectatingSystem s)
			{
				s.UpdateStacks();
			});
			if (Input.GetKey((KeyCode)304) && Input.GetKeyDown((KeyCode)27) && _spectatingSystemList.Count > 0 && !IsHosting)
			{
				SpectatingOverlay.UpdateViewerList(nullSpecInfo);
				SpectatingOverlay.HideStopSpectatingButton();
				SpectatingOverlay.HideViewerIcon();
				_spectatingSystemList.Last().RemoveFromManager();
			}
		}

		public static void StopAllSpectator()
		{
			if (_spectatingSystemList != null && _spectatingSystemList.Count > 0)
			{
				SpectatingOverlay.UpdateViewerList(nullSpecInfo);
				SpectatingOverlay.SetCurrentUserState(UserState.None);
				int num = 0;
				while (num < _spectatingSystemList.Count)
				{
					RemoveSpectator(_spectatingSystemList[num]);
				}
				if (hostedSpectatingSystem != null && ((WebsocketManager)hostedSpectatingSystem).IsConnected)
				{
					hostedSpectatingSystem = null;
				}
			}
			TootTallyGlobalVariables.isSpectating = IsSpectating;
		}

		public static void CancelPendingConnections()
		{
			IEnumerable<SpectatingSystem> source = _spectatingSystemList?.Where((SpectatingSystem x) => ((WebsocketManager)x).ConnectionPending);
			if (source.Count() > 0)
			{
				List<SpectatingSystem> canceledSpec = new List<SpectatingSystem>();
				source.ToList().ForEach(delegate(SpectatingSystem spec)
				{
					spec.CancelConnection();
					canceledSpec.Add(spec);
				});
				canceledSpec.ForEach(RemoveSpectator);
			}
		}

		public static SpectatingSystem CreateNewSpectatingConnection(int id, string name)
		{
			SpectatingSystem spectatingSystem = new SpectatingSystem(id, name);
			spectatingSystem.OnWebSocketOpenCallback = OnSpectatingConnect;
			_spectatingSystemList.Add(spectatingSystem);
			if (id == TootTallyUser.userInfo.id)
			{
				hostedSpectatingSystem = spectatingSystem;
			}
			return spectatingSystem;
		}

		public static void OnSpectatingConnect(SpectatingSystem sender)
		{
			if (!((WebsocketManager)sender).IsHost)
			{
				sender.OnSocketSongInfoReceived = SpectatingManagerPatches.OnSongInfoReceived;
				sender.OnSocketUserStateReceived = SpectatingManagerPatches.OnUserStateReceived;
				sender.OnSocketFrameDataReceived = SpectatingManagerPatches.OnFrameDataReceived;
				sender.OnSocketTootDataReceived = SpectatingManagerPatches.OnTootDataReceived;
				sender.OnSocketNoteDataReceived = SpectatingManagerPatches.OnNoteDataReceived;
				TootTallyNotifManager.DisplayNotif("Waiting for host to pick a song...", 6f);
				TootTallyGlobalVariables.isSpectating = true;
			}
			else
			{
				OnHostConnection();
				SpectatingManagerPatches.SendCurrentUserState();
			}
			sender.OnSocketSpecInfoReceived = SpectatingManagerPatches.OnSpectatorDataReceived;
		}

		public static void RemoveSpectator(SpectatingSystem spectator)
		{
			if (spectator != null)
			{
				if (((WebsocketManager)spectator).IsConnected)
				{
					spectator.Disconnect();
				}
				if (_spectatingSystemList.Contains(spectator))
				{
					_spectatingSystemList.Remove(spectator);
				}
				else
				{
					Plugin.LogInfo("Couldnt find websocket in list.");
				}
			}
		}

		public static SpectatingSystem CreateUniqueSpectatingConnection(int id, string name)
		{
			StopAllSpectator();
			return CreateNewSpectatingConnection(id, name);
		}

		public static void OnAllowHostConfigChange(bool value)
		{
			if (value && hostedSpectatingSystem == null && TootTallyUser.userInfo.id != 0)
			{
				CreateUniqueSpectatingConnection(TootTallyUser.userInfo.id, TootTallyUser.userInfo.username);
			}
			else if (!value && hostedSpectatingSystem != null)
			{
				RemoveSpectator(hostedSpectatingSystem);
				hostedSpectatingSystem = null;
			}
		}

		public static void UpdateSpectatorIDList()
		{
			((MonoBehaviour)Plugin.Instance).StartCoroutine((IEnumerator)TootTallyAPIService.GetSpectatorIDList((Action<int[]>)delegate(int[] idList)
			{
				currentSpectatorIDList = idList;
			}));
		}

		public static void OnHostConnection()
		{
		}

		public static bool IsAnyConnectionPending()
		{
			return _spectatingSystemList.Any((SpectatingSystem x) => ((WebsocketManager)x).ConnectionPending);
		}

		public static void OnSpectateButtonPress(int id, string name)
		{
			if (!IsAnyConnectionPending() && (TootTallyUser.userInfo.id != id || !IsHosting))
			{
				CreateUniqueSpectatingConnection(id, name);
				TrombuddiesManager.UpdateUsers();
			}
		}
	}
	public static class SpectatingOverlay
	{
		private static GameObject _overlayCanvas;

		private static GameObject _pauseTextHolder;

		private static TootTallyAnimation _pauseTextHolderAnimation;

		private static TootTallyAnimation _marqueeAnimation;

		private static SpectatingViewerIcon _viewerIcon;

		private static bool _isInitialized;

		private static SpectatingManager.SocketSpectatorInfo _spectatorInfo;

		private static SpectatingManager.UserState _currentUserState;

		private static CustomButton _stopSpectatingButton;

		private static Vector2 _marqueeStartPosition;

		private static TMP_Text _marqueeText;

		private static bool IsInGameController => _currentUserState == SpectatingManager.UserState.Playing || _currentUserState == SpectatingManager.UserState.Paused;

		private static bool IsInLevelSelect => _currentUserState == SpectatingManager.UserState.SelectingSong;

		public static void Initialize()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			_overlayCanvas = new GameObject("TootTallySpectatorOverlayCanvas");
			_overlayCanvas.SetActive(true);
			Object.DontDestroyOnLoad((Object)(object)_overlayCanvas);
			Canvas val = _overlayCanvas.AddComponent<Canvas>();
			val.renderMode = (RenderMode)0;
			val.overrideSorting = true;
			val.sortingOrder = 0;
			GraphicRaycaster val2 = _overlayCanvas.AddComponent<GraphicRaycaster>();
			CanvasScaler val3 = _overlayCanvas.AddComponent<CanvasScaler>();
			val3.referenceResolution = new Vector2(1920f, 1080f);
			val3.uiScaleMode = (ScaleMode)1;
			_stopSpectatingButton = GameObjectFactory.CreateCustomButton(_overlayCanvas.transform, Vector2.zero, new Vector2(200f, 60f), "Stop\nSpectating", "StopSpectatingButton", (Action)OnStopSpectatingButtonClick);
			((Component)_stopSpectatingButton).gameObject.SetActive(false);
			_marqueeText = GameObjectFactory.CreateSingleText(_overlayCanvas.transform, "SpectatorMarqueeText", "PlaceHolder", new Color(1f, 1f, 1f, 0.75f), (TextFont)0);
			_marqueeText.fontSize = 36f;
			_marqueeText.rectTransform.anchoredPosition = (_marqueeStartPosition = new Vector2(1300f, 0f));
			RectTransform rectTransform = _marqueeText.rectTransform;
			RectTransform rectTransform2 = _marqueeText.rectTransform;
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(0f, 0.2f);
			rectTransform2.anchorMax = val4;
			rectTransform.anchorMin = val4;
			_marqueeText.rectTransform.pivot = new Vector2(0f, 0.5f);
			((Component)_marqueeText).gameObject.SetActive(false);
			_viewerIcon = new SpectatingViewerIcon(_overlayCanvas.transform, Vector2.zero, Vector2.one * 80f, "ViewerIcon");
			_pauseTextHolder = GameObjectFactory.CreateOverlayPanel(_overlayCanvas.transform, Vector2.zero, new Vector2(1000f, 350f), 8f, "PauseTextOverlay");
			_pauseTextHolder.SetActive(false);
			((Behaviour)((Component)_pauseTextHolder.transform.Find("FSLatencyPanel")).GetComponent<Image>()).enabled = false;
			RectTransform component = _pauseTextHolder.GetComponent<RectTransform>();
			Vector2 val6 = (component.pivot = Vector2.one / 2f);
			val4 = (component.anchorMax = (component.anchorMin = val6));
			GameObject gameObject = ((Component)_pauseTextHolder.transform.Find("FSLatencyPanel/LatencyFG")).gameObject;
			GameObjectFactory.DestroyFromParent(gameObject, "title");
			GameObjectFactory.DestroyFromParent(gameObject, "subtitle");
			GameObjectFactory.DestroyFromParent(gameObject, "MainPage");
			TMP_Text val9 = GameObjectFactory.CreateSingleText(gameObject.transform, "PauseText", "Host paused the song.\n Waiting for an action.", Color.white, (TextFont)0);
			val9.fontSize = 72f;
			val9.alignment = (TextAlignmentOptions)514;
			val9.rectTransform.pivot = new Vector2(0f, 0.5f);
			_isInitialized = true;
		}

		public static void UpdateViewerList(SpectatingManager.SocketSpectatorInfo spectatorInfo)
		{
			if (_isInitialized)
			{
				_spectatorInfo = spectatorInfo;
				_viewerIcon?.UpdateViewerList(spectatorInfo);
				UpdateViewIcon();
			}
		}

		public static void SetCurrentUserState(SpectatingManager.UserState newUserState)
		{
			if (_isInitialized)
			{
				_currentUserState = newUserState;
				UpdateViewIcon();
				UpdateStopSpectatingButton();
			}
		}

		public static void OnStopSpectatingButtonClick()
		{
			if (_isInitialized)
			{
				if (IsInGameController)
				{
					SpectatingManager.SpectatingManagerPatches.QuitSong();
				}
				SpectatingManager.StopAllSpectator();
			}
		}

		public static void UpdateViewIcon()
		{
			//IL_0077: 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)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			if (_viewerIcon != null && _isInitialized)
			{
				if (_spectatorInfo.count == 0 || _spectatorInfo.count < 1 || !Plugin.Instance.ShowSpectatorCount.Value)
				{
					_viewerIcon.Hide();
				}
				else if (IsInLevelSelect)
				{
					_viewerIcon.SetAnchorMinMax(new Vector2(0.92f, 0.97f));
					_viewerIcon.SetBubblePivot(Vector2.one);
					_viewerIcon.Show();
				}
				else if (IsInGameController)
				{
					_viewerIcon.SetAnchorMinMax(new Vector2(0.03f, 0.07f));
					_viewerIcon.SetBubblePivot(Vector2.zero);
					_viewerIcon.Show();
				}
				else
				{
					_viewerIcon.Hide();
				}
			}
		}

		public static void UpdateStopSpectatingButton()
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			if (_isInitialized)
			{
				Vector2 val = default(Vector2);
				if (IsInLevelSelect && SpectatingManager.IsSpectating)
				{
					((Component)_stopSpectatingButton).gameObject.SetActive(true);
					RectTransform component = ((Component)_stopSpectatingButton).GetComponent<RectTransform>();
					RectTransform component2 = ((Component)_stopSpectatingButton).GetComponent<RectTransform>();
					((Vector2)(ref val))..ctor(0.14f, 0.88f);
					component2.anchorMax = val;
					component.anchorMin = val;
				}
				else if (IsInGameController && SpectatingManager.IsSpectating)
				{
					((Component)_stopSpectatingButton).gameObject.SetActive(true);
					RectTransform component3 = ((Component)_stopSpectatingButton).GetComponent<RectTransform>();
					RectTransform component4 = ((Component)_stopSpectatingButton).GetComponent<RectTransform>();
					((Vector2)(ref val))..ctor(0.2f, 0.09f);
					component4.anchorMax = val;
					component3.anchorMin = val;
				}
				else
				{
					((Component)_stopSpectatingButton).gameObject.SetActive(false);
				}
			}
		}

		public static void ShowViewerIcon()
		{
			_viewerIcon?.Show();
		}

		public static void HideViewerIcon()
		{
			_viewerIcon?.Hide();
		}

		public static void ShowPauseText()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			if (!((Object)(object)_pauseTextHolder == (Object)null))
			{
				_pauseTextHolder.GetComponent<RectTransform>().anchoredPosition = new Vector2(0f, -1500f);
				_pauseTextHolder.SetActive(true);
				TootTallyAnimation pauseTextHolderAnimation = _pauseTextHolderAnimation;
				if (pauseTextHolderAnimation != null)
				{
					pauseTextHolderAnimation.Dispose();
				}
				_pauseTextHolderAnimation = TootTallyAnimationManager.AddNewPositionAnimation(_pauseTextHolder, Vector2.op_Implicit(Vector2.zero), 0.8f, new SecondDegreeDynamicsAnimation(2.25f, 0.94f, 1.15f), (Action<GameObject>)null);
			}
		}

		public static void HidePauseText()
		{
			if (!((Object)(object)_pauseTextHolder == (Object)null))
			{
				TootTallyAnimation pauseTextHolderAnimation = _pauseTextHolderAnimation;
				if (pauseTextHolderAnimation != null)
				{
					pauseTextHolderAnimation.Dispose();
				}
				_pauseTextHolder.SetActive(false);
			}
		}

		public static void ShowStopSpectatingButton()
		{
			CustomButton stopSpectatingButton = _stopSpectatingButton;
			if (stopSpectatingButton != null)
			{
				((Component)stopSpectatingButton).gameObject.SetActive(true);
			}
		}

		public static void HideStopSpectatingButton()
		{
			CustomButton stopSpectatingButton = _stopSpectatingButton;
			if (stopSpectatingButton != null)
			{
				((Component)stopSpectatingButton).gameObject.SetActive(false);
			}
		}

		public static void ShowMarquee(string playerName, string songName, float songSpeed, string modifiers)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			_marqueeText.rectTransform.anchoredPosition = _marqueeStartPosition;
			_marqueeText.text = "Currently Spectating " + playerName + "\nPlaying " + songName;
			if (songSpeed != 1f)
			{
				TMP_Text marqueeText = _marqueeText;
				marqueeText.text += $" [{songSpeed:0.00}x]";
			}
			if (modifiers != null && modifiers != "None")
			{
				TMP_Text marqueeText2 = _marqueeText;
				marqueeText2.text = marqueeText2.text + " [" + modifiers + "]";
			}
			AnimateMarquee();
			((Component)_marqueeText).gameObject.SetActive(true);
		}

		public static void AnimateMarquee()
		{
			//IL_000b: 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_001a: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			_marqueeAnimation = TootTallyAnimationManager.AddNewPositionAnimation(((Component)_marqueeText).gameObject, Vector2.op_Implicit(-_marqueeStartPosition * 1.2f), 30f, new SecondDegreeDynamicsAnimation(0.009f, 0f, 1f), (Action<GameObject>)delegate
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				_marqueeText.rectTransform.anchoredPosition = _marqueeStartPosition;
				AnimateMarquee();
			});
		}

		public static void HideMarquee()
		{
			if (!((Object)(object)_marqueeText == (Object)null))
			{
				TootTallyAnimation marqueeAnimation = _marqueeAnimation;
				if (marqueeAnimation != null)
				{
					marqueeAnimation.Dispose();
				}
				((Component)_marqueeText).gameObject.SetActive(false);
			}
		}
	}
	public class SpectatingSystem : WebsocketManager
	{
		private ConcurrentQueue<SpectatingManager.SocketFrameData> _receivedFrameDataQueue;

		private ConcurrentQueue<SpectatingManager.SocketTootData> _receivedTootDataQueue;

		private ConcurrentQueue<SpectatingManager.SocketNoteData> _receivedNoteDataQueue;

		private ConcurrentQueue<SpectatingManager.SocketSongInfo> _receivedSongInfoQueue;

		private ConcurrentQueue<SpectatingManager.SocketUserState> _receivedUserStateQueue;

		private ConcurrentQueue<SpectatingManager.SocketSpectatorInfo> _receivedSpecInfoQueue;

		public Action<SpectatingManager.SocketFrameData> OnSocketFrameDataReceived;

		public Action<SpectatingManager.SocketTootData> OnSocketTootDataReceived;

		public Action<SpectatingManager.SocketNoteData> OnSocketNoteDataReceived;

		public Action<SpectatingManager.SocketUserState> OnSocketUserStateReceived;

		public Action<SpectatingManager.SocketSongInfo> OnSocketSongInfoReceived;

		public Action<SpectatingManager.SocketSpectatorInfo> OnSocketSpecInfoReceived;

		public Action<SpectatingSystem> OnWebSocketOpenCallback;

		public string spectatorName;

		public string GetSpectatorUserId => ((WebsocketManager)this)._id;

		public SpectatingSystem(int id, string name)
			: base(id.ToString(), "wss://spec.toottally.com:443/spec/", "1.3.0")
		{
			spectatorName = name;
			_receivedFrameDataQueue = new ConcurrentQueue<SpectatingManager.SocketFrameData>();
			_receivedTootDataQueue = new ConcurrentQueue<SpectatingManager.SocketTootData>();
			_receivedNoteDataQueue = new ConcurrentQueue<SpectatingManager.SocketNoteData>();
			_receivedSongInfoQueue = new ConcurrentQueue<SpectatingManager.SocketSongInfo>();
			_receivedUserStateQueue = new ConcurrentQueue<SpectatingManager.SocketUserState>();
			_receivedSpecInfoQueue = new ConcurrentQueue<SpectatingManager.SocketSpectatorInfo>();
			((WebsocketManager)this).ConnectionPending = true;
			((WebsocketManager)this).ConnectToWebSocketServer(((WebsocketManager)this)._url + id, Plugin.GetAPIKey, id == TootTallyUser.userInfo.id);
		}

		public void SendSongInfoToSocket(string trackRef, int id, float gameSpeed, float scrollSpeed, string gamemodifiers)
		{
			SpectatingManager.SocketSongInfo socketSongInfo = default(SpectatingManager.SocketSongInfo);
			socketSongInfo.trackRef = trackRef;
			socketSongInfo.songID = id;
			socketSongInfo.gameSpeed = gameSpeed;
			socketSongInfo.scrollSpeed = scrollSpeed;
			socketSongInfo.gamemodifiers = gamemodifiers;
			string text = JsonConvert.SerializeObject((object)socketSongInfo);
			((WebsocketManager)this).SendToSocket(text);
		}

		public void SendSongInfoToSocket(SpectatingManager.SocketSongInfo songInfo)
		{
			((WebsocketManager)this).SendToSocket(JsonConvert.SerializeObject((object)songInfo));
		}

		public void SendUserStateToSocket(SpectatingManager.UserState userState)
		{
			SpectatingManager.SocketUserState socketUserState = default(SpectatingManager.SocketUserState);
			socketUserState.userState = (int)userState;
			string text = JsonConvert.SerializeObject((object)socketUserState);
			((WebsocketManager)this).SendToSocket(text);
		}

		public void SendNoteData(bool champMode, int multiplier, int noteID, double noteScoreAverage, bool releasedButtonBetweenNotes, int totalScore, float health, int highestCombo)
		{
			SpectatingManager.SocketNoteData socketNoteData = default(SpectatingManager.SocketNoteData);
			socketNoteData.champMode = champMode;
			socketNoteData.multiplier = multiplier;
			socketNoteData.noteID = noteID;
			socketNoteData.noteScoreAverage = noteScoreAverage;
			socketNoteData.releasedButtonBetweenNotes = releasedButtonBetweenNotes;
			socketNoteData.totalScore = totalScore;
			socketNoteData.health = health;
			socketNoteData.highestCombo = highestCombo;
			SpectatingManager.SocketNoteData socketNoteData2 = socketNoteData;
			string text = JsonConvert.SerializeObject((object)socketNoteData2);
			((WebsocketManager)this).SendToSocket(text);
		}

		public void SendFrameData(float time, float noteHolder, float pointerPosition)
		{
			SpectatingManager.SocketFrameData socketFrameData = default(SpectatingManager.SocketFrameData);
			socketFrameData.time = time;
			socketFrameData.noteHolder = noteHolder;
			socketFrameData.pointerPosition = pointerPosition;
			SpectatingManager.SocketFrameData socketFrameData2 = socketFrameData;
			string text = JsonConvert.SerializeObject((object)socketFrameData2);
			((WebsocketManager)this).SendToSocket(text);
		}

		public void SendTootData(float time, float noteHolder, bool isTooting)
		{
			SpectatingManager.SocketTootData socketTootData = default(SpectatingManager.SocketTootData);
			socketTootData.time = time;
			socketTootData.noteHolder = noteHolder;
			socketTootData.isTooting = isTooting;
			SpectatingManager.SocketTootData socketTootData2 = socketTootData;
			string text = JsonConvert.SerializeObject((object)socketTootData2);
			((WebsocketManager)this).SendToSocket(text);
		}

		protected override void OnDataReceived(object sender, MessageEventArgs e)
		{
			if (!e.IsText)
			{
				return;
			}
			SpectatingManager.ISocketMessage socketMessage;
			try
			{
				socketMessage = JsonConvert.DeserializeObject<SpectatingManager.ISocketMessage>(e.Data, SpectatingManager._dataConverter);
			}
			catch (Exception)
			{
				Plugin.LogInfo("Couldn't parse to data: " + e.Data);
				return;
			}
			if (!((WebsocketManager)this).IsHost)
			{
				if (socketMessage is SpectatingManager.SocketSongInfo item)
				{
					_receivedSongInfoQueue.Enqueue(item);
				}
				else if (socketMessage is SpectatingManager.SocketFrameData item2)
				{
					_receivedFrameDataQueue.Enqueue(item2);
				}
				else if (socketMessage is SpectatingManager.SocketTootData item3)
				{
					_receivedTootDataQueue.Enqueue(item3);
				}
				else if (socketMessage is SpectatingManager.SocketUserState item4)
				{
					_receivedUserStateQueue.Enqueue(item4);
				}
				else if (socketMessage is SpectatingManager.SocketNoteData item5)
				{
					_receivedNoteDataQueue.Enqueue(item5);
				}
			}
			if (socketMessage is SpectatingManager.SocketSpectatorInfo)
			{
				SpectatingManager.SocketSpectatorInfo item6 = (SpectatingManager.SocketSpectatorInfo)(object)socketMessage;
				if (true)
				{
					Plugin.LogInfo(e.Data);
					_receivedSpecInfoQueue.Enqueue(item6);
				}
			}
		}

		public void UpdateStacks()
		{
			if (!((WebsocketManager)this).ConnectionPending)
			{
				if (OnSocketFrameDataReceived != null && _receivedFrameDataQueue.TryDequeue(out var result))
				{
					OnSocketFrameDataReceived(result);
				}
				if (OnSocketTootDataReceived != null && _receivedTootDataQueue.TryDequeue(out var result2))
				{
					OnSocketTootDataReceived(result2);
				}
				if (OnSocketSongInfoReceived != null && _receivedSongInfoQueue.TryDequeue(out var result3))
				{
					OnSocketSongInfoReceived(result3);
				}
				if (OnSocketUserStateReceived != null && _receivedUserStateQueue.TryDequeue(out var result4))
				{
					OnSocketUserStateReceived(result4);
				}
				if (OnSocketNoteDataReceived != null && _receivedNoteDataQueue.TryDequeue(out var result5))
				{
					OnSocketNoteDataReceived(result5);
				}
				if (OnSocketSpecInfoReceived != null && _receivedSpecInfoQueue.TryDequeue(out var result6))
				{
					OnSocketSpecInfoReceived(result6);
				}
			}
		}

		protected override void OnWebSocketOpen(object sender, EventArgs e)
		{
			TootTallyNotifManager.DisplayNotif("Connected to spectating server.", 6f);
			OnWebSocketOpenCallback?.Invoke(this);
			((WebsocketManager)this).OnWebSocketOpen(sender, e);
		}

		protected override void OnWebSocketClose(object sender, EventArgs e)
		{
			if (!((WebsocketManager)this).IsHost)
			{
				TootTallyGlobalVariables.isSpectating = false;
			}
			((WebsocketManager)this).OnWebSocketClose(sender, e);
		}

		public void Disconnect()
		{
			if (!((WebsocketManager)this).IsHost)
			{
				TootTallyGlobalVariables.isSpectating = false;
				TootTallyNotifManager.DisplayNotif("Disconnected from Spectating server.", 6f);
			}
			if (((WebsocketManager)this).IsConnected)
			{
				((WebsocketManager)this).CloseWebsocket();
			}
		}

		public void CancelConnection()
		{
			((WebsocketManager)this).CloseWebsocket();
		}

		public void RemoveFromManager()
		{
			SpectatingManager.RemoveSpectator(this);
		}
	}
	public class SpectatingViewerIcon
	{
		private GameObject _imageHolder;

		private RectTransform _imageRect;

		private TMP_Text _text;

		private TootTallyAnimation _currentRotAnimation;

		private TootTallyAnimation _currentScaleAnimation;

		private GameObject _bubble;

		private TMP_Text _bubbleText;

		private bool _isActive;

		private int _lastCount;

		public bool isInitialized;

		public SpectatingViewerIcon(Transform canvasTransform, Vector2 position, Vector2 size, string name)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: 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_013c: Unknown result type (might be due to invalid IL or missing references)
			_imageHolder = GameObjectFactory.CreateImageHolder(canvasTransform, position, size, AssetManager.GetSprite("SpectatorIcon.png"), name);
			((Graphic)_imageHolder.GetComponent<Image>()).color = new Color(1f, 1f, 1f, 0.8f);
			_imageHolder.AddComponent<Outline>();
			_imageHolder.SetActive(false);
			BubblePopupHandler val = _imageHolder.AddComponent<BubblePopupHandler>();
			_bubble = GameObjectFactory.CreateBubble(new Vector2(300f, 300f), "ViewerListBubble", "PlaceHolder", new Vector2(0f, 0f), 6, true, 18);
			val.Initialize(_bubble, false);
			_bubbleText = ((Component)_bubble.transform.Find("Window Body/BubbleText")).GetComponent<TMP_Text>();
			_bubbleText.lineSpacing = 40f;
			_imageRect = _imageHolder.GetComponent<RectTransform>();
			SetAnchorMinMax(new Vector2(0.03f, 0.07f));
			_imageRect.anchoredPosition = new Vector2(7f, 0f);
			_text = GameObjectFactory.CreateSingleText(_imageHolder.transform, name, "0", Color.white, (TextFont)0);
			_text.fontSize = 24f;
			_isActive = false;
			_lastCount = 0;
		}

		public void UpdateViewerList(SpectatingManager.SocketSpectatorInfo specInfo)
		{
			if (specInfo.count == 0)
			{
				_lastCount = 0;
				return;
			}
			if (_lastCount != specInfo.count)
			{
				_lastCount = specInfo.count;
				_text.text = specInfo.count.ToString();
				OnViewerCountChange();
			}
			_bubbleText.text = GetBubbleStringFromSpecInfo(specInfo);
		}

		public void OnViewerCountChange()
		{
		}

		private string GetBubbleStringFromSpecInfo(SpectatingManager.SocketSpectatorInfo specInfo)
		{
			return string.Concat(specInfo.spectators.Select((string name) => name + "\n"));
		}

		public void SetAnchorMinMax(Vector2 anchor)
		{
			//IL_000c: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			RectTransform imageRect = _imageRect;
			Vector2 anchorMax = (_imageRect.anchorMin = anchor);
			imageRect.anchorMax = anchorMax;
		}

		public void SetBubblePivot(Vector2 pivot)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			_bubble.GetComponent<RectTransform>().pivot = pivot;
		}

		private void SetRectToDefault()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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)
			_imageHolder.transform.eulerAngles = new Vector3(0f, 0f, 260f);
			_imageHolder.transform.localScale = Vector2.op_Implicit(Vector2.zero);
		}

		private void SetRectToFinal()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			_imageHolder.transform.eulerAngles = new Vector3(0f, 0f, 0f);
			_imageHolder.transform.localScale = Vector3.one;
		}

		public void Show()
		{
			//IL_0065: 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)
			if (!_isActive)
			{
				_imageHolder.SetActive(true);
				TootTallyAnimation currentRotAnimation = _currentRotAnimation;
				if (currentRotAnimation != null)
				{
					currentRotAnimation.Dispose();
				}
				TootTallyAnimation currentScaleAnimation = _currentScaleAnimation;
				if (currentScaleAnimation != null)
				{
					currentScaleAnimation.Dispose();
				}
				SetRectToDefault();
				_isActive = true;
				_currentRotAnimation = TootTallyAnimationManager.AddNewEulerAngleAnimation(_imageHolder, new Vector3(0f, 0f, 0f), 1.5f, GetSecondDegreeAnimation(), (Action<GameObject>)null);
				_currentScaleAnimation = TootTallyAnimationManager.AddNewScaleAnimation(_imageHolder, Vector3.one, 1.5f, GetSecondDegreeAnimation(), (Action<GameObject>)delegate
				{
					OnFinishAnimating();
				});
			}
		}

		public void Hide()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (_isActive)
			{
				TootTallyAnimation currentRotAnimation = _currentRotAnimation;
				if (currentRotAnimation != null)
				{
					currentRotAnimation.Dispose();
				}
				TootTallyAnimation currentScaleAnimation = _currentScaleAnimation;
				if (currentScaleAnimation != null)
				{
					currentScaleAnimation.Dispose();
				}
				SetRectToFinal();
				_isActive = false;
				_currentRotAnimation = TootTallyAnimationManager.AddNewEulerAngleAnimation(_imageHolder, new Vector3(0f, 0f, -260f), 1.5f, GetSecondDegreeAnimation(), (Action<GameObject>)null);
				_currentScaleAnimation = TootTallyAnimationManager.AddNewScaleAnimation(_imageHolder, Vector2.zero, 1.5f, GetSecondDegreeAnimation(), (Action<GameObject>)delegate
				{
					OnFinishAnimating();
					_imageHolder.SetActive(false);
				});
			}
		}

		public void OnFinishAnimating()
		{
			_currentRotAnimation = null;
			_currentScaleAnimation = null;
		}

		public void ToggleShow()
		{
			_isActive = !_isActive;
			_imageHolder.SetActive(_isActive);
		}

		public SecondDegreeDynamicsAnimation GetSecondDegreeAnimation()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			return new SecondDegreeDynamicsAnimation(2.2f, 0.65f, 1f);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "TootTallySpectator";

		public const string PLUGIN_NAME = "TootTallySpectator";

		public const string PLUGIN_VERSION = "1.0.7";
	}
}