Decompiled source of TootTallyMultiplayer v1.0.3

plugins/TootTallyMultiplayer.dll

Decompiled 2 weeks 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 System.Text;
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 TootTallyLeaderboard;
using TootTallyLeaderboard.Replays;
using TootTallyMultiplayer.APIService;
using TootTallyMultiplayer.MultiplayerCore;
using TootTallyMultiplayer.MultiplayerCore.InputPrompts;
using TootTallyMultiplayer.MultiplayerCore.PointScore;
using TootTallyMultiplayer.MultiplayerPanels;
using TootTallySettings;
using TootTallySpectator;
using TootTallyWebsocketLibs;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Localization.Components;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using WebSocketSharp;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TootTallyMultiplayer")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("TootTally Multiplayer Module")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: AssemblyProduct("TootTallyMultiplayer")]
[assembly: AssemblyTitle("TootTallyMultiplayer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.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 TootTallyMultiplayer
{
	public class MultiplayerCard : MonoBehaviour
	{
		public MultSerializableClasses.MultiplayerUserInfo user;

		public TMP_Text textName;

		public TMP_Text textState;

		public TMP_Text textRank;

		public Image image;

		public Image containerImage;

		public Transform container;

		private Color _defaultColor;

		private Color _defaultContainerColor;

		public void InitTexts()
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			container = ((Component)this).transform.GetChild(0);
			textName = ((Component)container.Find("Name")).GetComponent<TMP_Text>();
			textState = ((Component)container.Find("State")).GetComponent<TMP_Text>();
			textRank = ((Component)container.Find("Rank")).GetComponent<TMP_Text>();
			image = ((Component)this).gameObject.GetComponent<Image>();
			_defaultColor = ((Graphic)image).color;
			containerImage = ((Component)container).GetComponent<Image>();
			_defaultContainerColor = ((Graphic)containerImage).color;
		}

		public void ResetImageColor()
		{
			//IL_0007: 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)
			((Graphic)image).color = _defaultColor;
			((Graphic)containerImage).color = _defaultContainerColor;
		}

		public void UpdateUserInfo(MultSerializableClasses.MultiplayerUserInfo user, string state = null)
		{
			this.user = user;
			ResetImageColor();
			SetTexts(user.username, state ?? user.state, user.rank);
		}

		public void SetTexts(string username, string state, int rank)
		{
			textName.text = username;
			textState.text = state;
			textRank.text = $"#{rank}";
		}
	}
	public class MultiplayerController
	{
		public enum MultiplayerState
		{
			None,
			Home,
			CreatingLobby,
			Lobby,
			Hosting,
			SelectSong,
			ExitScene,
			Playing,
			PointScene,
			Quitting
		}

		public enum MultiplayerUserState
		{
			Spectating = -1,
			NotReady,
			Ready,
			Loading,
			Playing
		}

		private static List<MultSerializableClasses.MultiplayerLobbyInfo> _lobbyInfoList;

		private static List<string> _newLobbyCodeList;

		private static MultSerializableClasses.MultiplayerLobbyInfo _currentLobby;

		private static MultiplayerSystem _multiConnection;

		private static MultiplayerLiveScoreController _multiLiveScoreController;

		private MultiplayerPanelBase _currentActivePanel;

		private MultiplayerPanelBase _lastPanel;

		public bool IsTransitioning;

		private bool _hasSong;

		private MultSerializableClasses.UserState _currentUserState;

		private static string _savedDownloadLink;

		private static string _savedTrackRef;

		private string _searchFilter;

		private MultiplayerMainPanel _multMainPanel;

		private MultiplayerLobbyPanel _multLobbyPanel;

		private MultiplayerCreatePanel _multCreatePanel;

		private float _startGameTimer;

		private float _startGameTimerMaxTime;

		private float _nextStartTimerTick;

		public bool IsUpdating;

		public bool IsConnectionPending;

		public bool IsDownloadPending;

		public bool IsTimerStarted;

		public static MultSerializableClasses.MultiplayerSongInfo savedSongInfo;

		public static SingleTrackData savedTrackData;

		public PlaytestAnims GetInstance => CurrentInstance;

		public static PlaytestAnims CurrentInstance { get; private set; }

		public bool IsConnected
		{
			get
			{
				if (_multiConnection != null)
				{
					return ((WebsocketManager)_multiConnection).IsConnected;
				}
				return false;
			}
		}

		public bool IsAnybodyLoading => _currentLobby.players.Where((MultSerializableClasses.MultiplayerUserInfo x) => x.id != TootTallyUser.userInfo.id).Any((MultSerializableClasses.MultiplayerUserInfo x) => x.state == "Loading");

		public bool IsRequestPending
		{
			get
			{
				if (!_multCreatePanel.IsRequestPending)
				{
					return IsConnectionPending;
				}
				return true;
			}
		}

		public bool IsDevMode
		{
			get
			{
				if (!TootTallyUser.userInfo.dev)
				{
					return TootTallyUser.userInfo.moderator;
				}
				return true;
			}
		}

		public MultiplayerController(PlaytestAnims __instance)
		{
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			CurrentInstance = __instance;
			((Component)CurrentInstance.factpanel).gameObject.SetActive(false);
			GameObject gameObject = GameObject.Find("Canvas-Window").gameObject;
			Transform val = gameObject.transform.Find("Panel");
			GameObject canvas = Object.Instantiate<GameObject>(AssetBundleManager.GetPrefab("multiplayercanvas"));
			try
			{
				_multMainPanel = new MultiplayerMainPanel(canvas, this);
			}
			catch (Exception ex)
			{
				Plugin.LogError(ex.Message);
				Plugin.LogError(ex.StackTrace);
			}
			try
			{
				_multLobbyPanel = new MultiplayerLobbyPanel(canvas, this);
			}
			catch (Exception ex2)
			{
				Plugin.LogError(ex2.Message);
				Plugin.LogError(ex2.StackTrace);
			}
			try
			{
				_multCreatePanel = new MultiplayerCreatePanel(canvas, this);
			}
			catch (Exception ex3)
			{
				Plugin.LogError(ex3.Message);
				Plugin.LogError(ex3.StackTrace);
			}
			if (_lobbyInfoList == null)
			{
				_lobbyInfoList = new List<MultSerializableClasses.MultiplayerLobbyInfo>();
			}
			_currentActivePanel = _multMainPanel;
			_searchFilter = "";
			if (IsConnected)
			{
				UpdateLobbySongDetails();
				_multiConnection.OnSocketOptionReceived = OnOptionInfoReceived;
				_multiConnection.OnSocketSongInfoReceived = OnSongInfoReceived;
				_multiConnection.OnSocketLobbyInfoReceived = OnLobbyInfoReceived;
				OnLobbyInfoReceived(_currentLobby);
			}
			TootTallyAnimationManager.AddNewScaleAnimation(_multMainPanel.panel, Vector3.one, 0.8f, GetSecondDegreeAnimation(1.5f), (Action<GameObject>)delegate
			{
				RefreshAllLobbyInfo();
				MultiplayerManager.AllowExit = true;
			});
		}

		public void OnGameControllerStartSetup()
		{
			_multiLiveScoreController = GameObject.Find("GameplayCanvas/UIHolder").AddComponent<MultiplayerLiveScoreController>();
			MultiplayerPointScoreController.ClearSavedScores();
		}

		public void OnGameControllerStartSongSendReadyState()
		{
			SendUserState(MultSerializableClasses.UserState.Playing);
		}

		public void OnSongQuit()
		{
			_multiConnection.SendUserState(MultSerializableClasses.UserState.NotReady);
		}

		public void InitializePointScore()
		{
			GameObject.Find("Canvas/FullPanel").AddComponent<MultiplayerPointScoreController>();
		}

		private IEnumerator<WaitForSeconds> DelayDisplayLobbyInfo(float delay, MultSerializableClasses.MultiplayerLobbyInfo lobby, Action<MultSerializableClasses.MultiplayerLobbyInfo> callback)
		{
			yield return new WaitForSeconds(delay);
			callback(lobby);
		}

		public void UpdateLobbyInfo()
		{
			List<MultSerializableClasses.MultiplayerLobbyInfo> list = _lobbyInfoList.Where((MultSerializableClasses.MultiplayerLobbyInfo x) => x.title.Contains(_searchFilter)).ToList();
			if (list.Count == 0)
			{
				_multMainPanel.ShowNoLobbyText();
				_multMainPanel.FinalizeLobbyDisplay();
				return;
			}
			_multMainPanel.SetupForLobbyDisplay();
			for (int i = 0; i < list.Count; i++)
			{
				if (_newLobbyCodeList.Contains(list[i].id))
				{
					((MonoBehaviour)Plugin.Instance).StartCoroutine((IEnumerator)DelayDisplayLobbyInfo((float)i * 0.1f, list[i], _multMainPanel.DisplayLobby));
				}
				else
				{
					_multMainPanel.DisplayLobby(list[i], shouldAnimate: false);
				}
			}
			_multMainPanel.FinalizeLobbyDisplay();
			_newLobbyCodeList.Clear();
			_multMainPanel.UpdateScrolling(list.Count);
		}

		public void ConnectToLobby(string code, string password = "", bool forceEntry = false)
		{
			RefreshAllLobbyInfo();
			if (_multiConnection == null || !((WebsocketManager)_multiConnection).ConnectionPending)
			{
				_multiConnection?.Disconnect();
				Plugin.LogInfo("Connecting to " + code);
				IsConnectionPending = true;
				if (forceEntry)
				{
					code += "?ForceEntry=true";
				}
				else if (password != "")
				{
					code = code + "?Password=" + password;
				}
				_multiConnection = new MultiplayerSystem(code, isHost: false)
				{
					OnWebSocketOpenCallback = delegate
					{
						_multMainPanel.OnLobbyConnectSuccess();
						_multiConnection.OnWebSocketCloseCallback = null;
						_multiConnection.OnSocketSongInfoReceived = OnSongInfoReceived;
						_multiConnection.OnSocketOptionReceived = OnOptionInfoReceived;
						_multiConnection.OnSocketLobbyInfoReceived = OnLobbyInfoReceived;
					},
					OnWebSocketCloseCallback = DisconnectFromLobby
				};
			}
		}

		public void DisconnectFromLobby()
		{
			if (((WebsocketManager)_multiConnection).IsConnected)
			{
				_multiConnection.Disconnect();
				_multLobbyPanel.ResetData();
			}
			else
			{
				_multMainPanel.OnLobbyDisconnectError();
			}
			StopTimer();
			_currentLobby.code = "";
			IsConnectionPending = false;
			MultiplayerManager.UpdateMultiplayerStateIfChanged(MultiplayerState.Home);
			MoveToMain();
			RefreshAllLobbyInfo();
		}

		public void Update()
		{
			if (!IsConnected || _multiConnection == null)
			{
				return;
			}
			if (IsConnectionPending)
			{
				UpdateConnection();
				return;
			}
			_multiConnection.UpdateStacks();
			if (IsTimerStarted)
			{
				UpdateTimer();
			}
		}

		public void UpdateConnection()
		{
			IsConnectionPending = false;
			string text = _multiConnection.GetServerID.Split(new char[1] { '?' })[0];
			TootTallyNotifManager.DisplayNotif("Connected to " + text, 6f);
			MultiplayerLogger.ClearLogs();
			MultiplayerLogger.ServerLog("Connected to " + text);
			MultiplayerManager.UpdateMultiplayerState(MultiplayerState.Lobby);
			OnLobbyConnectionSuccess();
		}

		public void OnLobbyConnectionSuccess()
		{
			MoveToLobby();
		}

		public void MoveToCreate()
		{
			TransitionToPanel(_multCreatePanel);
		}

		public void MoveToLobby()
		{
			TransitionToPanel(_multLobbyPanel);
		}

		public void MoveToMain()
		{
			TransitionToPanel(_multMainPanel);
		}

		public void ReturnToLastPanel()
		{
			TransitionToPanel(_lastPanel);
		}

		public void HidePanel()
		{
			_currentActivePanel.panel.SetActive(false);
		}

		public void ShowPanel()
		{
			_currentActivePanel.panel.SetActive(true);
		}

		public void RefreshAllLobbyInfo()
		{
			if (IsUpdating || (Object)(object)CurrentInstance == (Object)null)
			{
				return;
			}
			IsUpdating = true;
			((MonoBehaviour)Plugin.Instance).StartCoroutine((IEnumerator)MultiplayerAPIService.GetLobbyList(delegate(List<MultSerializableClasses.MultiplayerLobbyInfo> lobbyList)
			{
				_multMainPanel.ClearAllLobby();
				IEnumerable<string> idList = _lobbyInfoList.Select((MultSerializableClasses.MultiplayerLobbyInfo x) => x.id);
				_newLobbyCodeList = (from x in lobbyList
					select x.id into x
					where !idList.Contains(x)
					select x).ToList();
				_lobbyInfoList = lobbyList;
				UpdateLobbyInfo();
				IsUpdating = false;
				_multMainPanel.ShowRefreshLobbyButton();
			}));
		}

		public void RefreshCurrentLobbyInfo()
		{
			if (_currentLobby.code != "")
			{
				OnLobbyInfoReceived(_currentLobby);
			}
		}

		public void OnLobbyInfoReceived(MultSerializableClasses.MultiplayerLobbyInfo lobbyInfo)
		{
			_currentLobby = lobbyInfo;
			if ((Object)(object)CurrentInstance != (Object)null)
			{
				_currentUserState = (MultSerializableClasses.UserState)Enum.Parse(typeof(MultSerializableClasses.UserState), _currentLobby.players.Find((MultSerializableClasses.MultiplayerUserInfo x) => x.id == TootTallyUser.userInfo.id).state);
				_multLobbyPanel.DisplayAllUserInfo(_currentLobby.players);
				_multLobbyPanel.OnLobbyInfoReceived(lobbyInfo.title, lobbyInfo.players.Count, lobbyInfo.maxPlayerCount);
				OnSongInfoReceived(_currentLobby.songInfo);
			}
		}

		public void OnLobbyInfoReceived(MultiplayerSystem.SocketLobbyInfo socketLobbyInfo)
		{
			OnLobbyInfoReceived(socketLobbyInfo.lobbyInfo);
		}

		public void OnUserInfoReceived(MultSerializableClasses.MultiplayerUserInfo userInfo)
		{
			if (userInfo.id != 0)
			{
				int index = _currentLobby.players.FindIndex((MultSerializableClasses.MultiplayerUserInfo x) => x.id == userInfo.id);
				_currentLobby.players[index] = userInfo;
				if ((Object)(object)CurrentInstance != (Object)null)
				{
					_multLobbyPanel?.UpdateUserInfo(userInfo);
				}
			}
		}

		public void TransitionToPanel(MultiplayerPanelBase nextPanel)
		{
			//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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			if (_currentActivePanel != nextPanel && !IsTransitioning)
			{
				IsTransitioning = true;
				_lastPanel = _currentActivePanel;
				Vector2 positionOut = -nextPanel.GetPanelPosition;
				TootTallyAnimationManager.AddNewPositionAnimation(_currentActivePanel.panel, Vector2.op_Implicit(positionOut), 0.9f, new SecondDegreeDynamicsAnimation(1.5f, 0.89f, 1.1f), (Action<GameObject>)delegate
				{
					//IL_002c: Unknown result type (might be due to invalid IL or missing references)
					_lastPanel.panel.SetActive(false);
					_lastPanel.panel.GetComponent<RectTransform>().anchoredPosition = positionOut;
				});
				nextPanel.panel.SetActive(true);
				_currentActivePanel = nextPanel;
				TootTallyAnimationManager.AddNewPositionAnimation(nextPanel.panel, Vector2.op_Implicit(Vector2.zero), 0.9f, new SecondDegreeDynamicsAnimation(1.5f, 0.89f, 1.1f), (Action<GameObject>)delegate
				{
					IsTransitioning = false;
				});
			}
		}

		public void OnSongInfoReceived(MultiplayerSystem.SocketSongInfo socketSongInfo)
		{
			OnSongInfoReceived(socketSongInfo.songInfo);
		}

		public void OnSongInfoReceived(MultSerializableClasses.MultiplayerSongInfo songInfo)
		{
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: 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_01a3: Expected O, but got Unknown
			if ((savedSongInfo.trackRef == "" || savedSongInfo.trackRef != songInfo.trackRef) && songInfo.trackRef != "")
			{
				MultiplayerLogger.HostLog(_currentLobby.players[0].username, "Song \"" + songInfo.songName + "\" was selected.");
			}
			savedSongInfo = songInfo;
			TootTallyGlobalVariables.gameSpeedMultiplier = songInfo.gameSpeed;
			GameModifierManager.LoadModifiersFromString(songInfo.modifiers);
			float num = (int)((songInfo.gameSpeed - 0.5f) / 0.25f);
			float difficulty;
			if (num != 6f)
			{
				float num2 = num * 0.25f + 0.5f;
				float num3 = (num + 1f) * 0.25f + 0.5f;
				float num4 = (songInfo.gameSpeed - num2) / (num3 - num2);
				difficulty = EasingHelper.Lerp(songInfo.speed_diffs[(int)num], songInfo.speed_diffs[(int)num + 1], num4);
			}
			else
			{
				difficulty = songInfo.speed_diffs[(int)num];
			}
			UpdateLobbySongInfo(songInfo.songName, songInfo.gameSpeed, songInfo.modifiers, difficulty);
			FSharpOption<TromboneTrack> val = TrackLookup.tryLookup(songInfo.trackRef);
			_hasSong = songInfo.trackRef != "" && OptionModule.IsSome<TromboneTrack>(val);
			if (_hasSong)
			{
				SelectSongFromTrackref(val.Value.trackref);
				if (_currentUserState == MultSerializableClasses.UserState.NoSong)
				{
					SendUserState(MultSerializableClasses.UserState.NotReady);
				}
				_savedDownloadLink = null;
				_savedTrackRef = "";
			}
			else
			{
				_savedDownloadLink = FileHelper.GetDownloadLinkFromSongData(new SongDataFromDB
				{
					mirror = songInfo.mirror,
					download = songInfo.download
				});
				_savedTrackRef = songInfo.trackRef;
				SendUserState(MultSerializableClasses.UserState.NoSong);
				_multLobbyPanel.SetNullTrackDataDetails(_savedDownloadLink != null);
			}
		}

		public void DownloadSavedChart(ProgressBar bar)
		{
			if (_savedDownloadLink == null || IsDownloadPending)
			{
				return;
			}
			IsDownloadPending = true;
			((MonoBehaviour)Plugin.Instance).StartCoroutine((IEnumerator)TootTallyAPIService.DownloadZipFromServer(_savedDownloadLink, bar, (Action<byte[]>)delegate(byte[] data)
			{
				IsDownloadPending = false;
				if (data != null)
				{
					try
					{
						string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location), "Downloads/");
						string text2 = _savedDownloadLink.Split(new char[1] { '/' }).Last() ?? "";
						if (!Directory.Exists(text))
						{
							Directory.CreateDirectory(text);
						}
						FileHelper.WriteBytesToFile(text, text2, data);
						string text3 = Path.Combine(text, text2);
						string text4 = Path.Combine(Paths.BepInExRootPath, "CustomSongs/");
						FileHelper.ExtractZipToDirectory(text3, text4);
						FileHelper.DeleteFile(text, text2);
						Plugin.Instance.ReloadTracks();
						SelectSongFromTrackref(_savedTrackRef);
						SendUserState(MultSerializableClasses.UserState.NotReady);
						return;
					}
					catch (Exception)
					{
						_multLobbyPanel.OnUserStateChange(_currentUserState);
						TootTallyNotifManager.DisplayNotif("Download failed. Unexpected error occured.", 6f);
						return;
					}
				}
				_multLobbyPanel.OnUserStateChange(_currentUserState);
				TootTallyNotifManager.DisplayNotif("Download failed.", 6f);
			}));
		}

		public void SelectSongFromTrackref(string trackref)
		{
			FSharpOption<TromboneTrack> val = TrackLookup.tryLookup(trackref);
			savedTrackData = TrackLookup.toTrackData(val.Value);
			UpdateLobbySongDetails();
			GlobalVariables.levelselect_index = savedTrackData.trackindex;
			GlobalVariables.chosen_track = savedTrackData.trackref;
			GlobalVariables.chosen_track_data = savedTrackData;
			_hasSong = true;
			_savedDownloadLink = null;
			_savedTrackRef = null;
			Plugin.LogInfo("Selected: " + savedTrackData.trackref);
		}

		public void UpdateLobbySongInfo(string songName, float gamespeed, string modifiers, float difficulty)
		{
			_multLobbyPanel?.OnSongInfoChanged(songName, gamespeed, modifiers, difficulty);
		}

		public void UpdateLobbySongDetails()
		{
			if (!((Object)(object)CurrentInstance == (Object)null))
			{
				if (savedTrackData != null)
				{
					_multLobbyPanel?.SetTrackDataDetails(savedTrackData);
				}
				if (savedSongInfo.trackRef != "")
				{
					_multLobbyPanel?.OnSongInfoChanged(savedSongInfo);
				}
			}
		}

		public void StartLobbyGame()
		{
			if (IsTimerStarted)
			{
				_multiConnection.SendOptionInfo(MultiplayerSystem.OptionInfoType.AbortGame);
				return;
			}
			_multiConnection.SendOptionInfo(MultiplayerSystem.OptionInfoType.StartTimer, new object[1] { 5 });
		}

		public void SendStartGame()
		{
			_multiConnection.SendOptionInfo(MultiplayerSystem.OptionInfoType.StartGame);
		}

		public void StartGame()
		{
			if (_hasSong && (Object)(object)CurrentInstance != (Object)null && !IsTransitioning)
			{
				MultiplayerManager.UpdateMultiplayerState(MultiplayerState.Playing);
				Plugin.LogInfo("Starting Multiplayer for " + GlobalVariables.chosen_track_data.trackname_short + " - " + GlobalVariables.chosen_track_data.trackref);
				MultiplayerLogger.ServerLog("Song starting now.");
				IsTransitioning = true;
				CurrentInstance.sfx_ok.Play();
				((Component)CurrentInstance.fadepanel).gameObject.SetActive(true);
				MultiAudioController.PauseMusicSoft();
				LeanTween.alphaCanvas(CurrentInstance.fadepanel, 1f, 0.65f).setOnComplete((Action)LoadLoaderScene);
			}
			else
			{
				TootTallyNotifManager.DisplayNotif("Cannot start the game. " + ((!_hasSong) ? "Chart not owned." : ""), 6f);
			}
		}

		public void AbortTimer()
		{
			StopTimer();
			MultiplayerLogger.ServerLog("Song start aborted.");
		}

		public void StopTimer()
		{
			IsTimerStarted = false;
			_startGameTimer = (_startGameTimerMaxTime = _nextStartTimerTick);
			_multLobbyPanel.OnTimerAbort();
		}

		public void StartTimer(float time)
		{
			_startGameTimer = (_startGameTimerMaxTime = (_startGameTimer = (_nextStartTimerTick = time)));
			_multLobbyPanel.OnTimerStart();
			IsTimerStarted = true;
		}

		public void UpdateTimer()
		{
			_startGameTimer -= Time.deltaTime;
			if (_startGameTimer <= 0f)
			{
				StopTimer();
				if (_multLobbyPanel.IsHost)
				{
					SendStartGame();
				}
			}
			else if (_startGameTimer < _nextStartTimerTick)
			{
				MultiplayerLogger.ServerLog($"Song starting in {Mathf.RoundToInt(_startGameTimer)}s");
				_nextStartTimerTick -= 1f;
				CurrentInstance.sfx_hover.Play();
			}
		}

		public void UpdateSearchFilter(string filter)
		{
			_searchFilter = filter;
			RefreshAllLobbyInfo();
		}

		public void KickUserFromLobby(int userID)
		{
			_multiConnection.SendOptionInfo(MultiplayerSystem.OptionInfoType.KickFromLobby, new object[1] { userID });
		}

		public void GiveHostUser(int userID)
		{
			_multiConnection.SendOptionInfo(MultiplayerSystem.OptionInfoType.GiveHost, new object[1] { userID });
			_currentUserState = MultSerializableClasses.UserState.NoSong;
		}

		public void SendQuickChat(MultiplayerSystem.QuickChat chat)
		{
			_multiConnection.SendOptionInfo(MultiplayerSystem.OptionInfoType.QuickChat, new object[1] { (int)chat });
		}

		public void SendQuickChat(int chatId)
		{
			_multiConnection.SendOptionInfo(MultiplayerSystem.OptionInfoType.QuickChat, new object[1] { chatId });
		}

		public void OnQuickChatReceived(int userID, MultiplayerSystem.QuickChat chat)
		{
			_multLobbyPanel?.OnQuickChatReceived(userID, chat);
		}

		public void SendSetLobbySettings(string name, string description, string password, int maxPlayer)
		{
			_multiConnection.SendSetLobbyInfo(name, description, password, maxPlayer);
		}

		public void OpenSongLink()
		{
			if (!(savedSongInfo.trackRef == "") && savedSongInfo.songID != 0)
			{
				Application.OpenURL($"https://toottally.com/song/{savedSongInfo.songID}/");
			}
		}

		public void LoadLoaderScene()
		{
			CurrentInstance = null;
			_multLobbyPanel = null;
			IsTransitioning = false;
			SceneManager.LoadScene("loader");
		}

		public void TransitionToSongSelection()
		{
			MultiplayerManager.UpdateMultiplayerState(MultiplayerState.SelectSong);
		}

		public void OnOptionInfoReceived(MultiplayerSystem.SocketOptionInfo optionInfo)
		{
			object obj = Enum.Parse(typeof(MultiplayerSystem.OptionInfoType), optionInfo.optionType);
			if (!(obj is MultiplayerSystem.OptionInfoType))
			{
				return;
			}
			switch ((MultiplayerSystem.OptionInfoType)obj)
			{
			case MultiplayerSystem.OptionInfoType.StartGame:
				StartGame();
				break;
			case MultiplayerSystem.OptionInfoType.StartTimer:
				StartTimer((float)optionInfo.values[0]);
				break;
			case MultiplayerSystem.OptionInfoType.AbortGame:
				AbortTimer();
				break;
			case MultiplayerSystem.OptionInfoType.KickFromLobby:
				if (TootTallyUser.userInfo.id == (int)optionInfo.values[0])
				{
					DisconnectFromLobby();
				}
				break;
			case MultiplayerSystem.OptionInfoType.QuickChat:
				OnQuickChatReceived((int)optionInfo.values[0], (MultiplayerSystem.QuickChat)optionInfo.values[1]);
				break;
			case MultiplayerSystem.OptionInfoType.Refresh:
			case MultiplayerSystem.OptionInfoType.UpdateUserState:
			case MultiplayerSystem.OptionInfoType.GiveHost:
				RefreshAllLobbyInfo();
				break;
			case MultiplayerSystem.OptionInfoType.UpdateUserInfo:
				OnUserInfoReceived(optionInfo.values[0].ToObject<MultSerializableClasses.MultiplayerUserInfo>());
				break;
			case MultiplayerSystem.OptionInfoType.UpdateScore:
				_multiLiveScoreController?.UpdateLiveScore((int)optionInfo.values[0], (int)optionInfo.values[1], (int)optionInfo.values[2], (int)optionInfo.values[3]);
				break;
			case MultiplayerSystem.OptionInfoType.FinalScore:
				MultiplayerPointScoreController.AddScore((int)optionInfo.values[0], (int)optionInfo.values[1], (float)optionInfo.values[2], (int)optionInfo.values[3], optionInfo.values[4].ToObject<int[]>());
				break;
			case MultiplayerSystem.OptionInfoType.Quit:
				_multiLiveScoreController?.OnUserQuit((int)optionInfo.values[0]);
				break;
			case MultiplayerSystem.OptionInfoType.SongFinished:
				break;
			}
		}

		public static MultSerializableClasses.MultiplayerUserInfo GetUserFromLobby(int id)
		{
			return _currentLobby.players.Find((MultSerializableClasses.MultiplayerUserInfo x) => x.id == id);
		}

		public void SendSongFinishedToLobby()
		{
			_multiConnection?.SendOptionInfo(MultiplayerSystem.OptionInfoType.SongFinished);
		}

		public void SendQuitFlag()
		{
			_multiConnection?.SendOptionInfo(MultiplayerSystem.OptionInfoType.Quit);
		}

		public void SendSongHashToLobby(string songHash, float gamespeed, string modifiers)
		{
			_multiConnection?.SendSongHash(songHash, gamespeed, modifiers);
		}

		public void SendScoreDataToLobby(int score, int combo, int health, int tally)
		{
			_multiConnection?.SendUpdateScore(score, combo, health, tally);
			_multiLiveScoreController?.UpdateLiveScore(TootTallyUser.userInfo.id, score, combo, health);
		}

		public void SendUserState(MultSerializableClasses.UserState state)
		{
			if (_currentUserState != state)
			{
				_currentUserState = state;
				_multLobbyPanel?.OnUserStateChange(state);
				_multiConnection?.SendUserState(state);
			}
		}

		public static SecondDegreeDynamicsAnimation GetSecondDegreeAnimation(float speedMult = 1f)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			return new SecondDegreeDynamicsAnimation(speedMult, 0.75f, 1.15f);
		}

		public static SecondDegreeDynamicsAnimation GetSecondDegreeAnimationNoBounce(float speedMult = 1f)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			return new SecondDegreeDynamicsAnimation(speedMult, 1f, 1f);
		}

		public void Dispose()
		{
			CurrentInstance = null;
			_lobbyInfoList?.Clear();
			_multiConnection?.Disconnect();
		}

		public void DebugFakeLobby()
		{
			_multMainPanel?.DisplayLobbyDebug();
		}

		public void DebugFakeUser()
		{
			_multLobbyPanel?.DisplayUserInfoDebug();
		}
	}
	public static class MultiplayerGameObjectFactory
	{
		private static TMP_InputField _inputFieldPrefab;

		private static GameObject _userCardPrefab;

		private static GameObject _liveScorePrefab;

		private static GameObject _pointScorePrefab;

		private static bool _isInitialized;

		public static void Initialize()
		{
			if (!_isInitialized)
			{
				SetUserCardPrefab();
				SetInputFieldPrefab();
				SetLiveScorePrefab();
				SetPointScorePrefab();
				_isInitialized = true;
			}
		}

		private static void SetUserCardPrefab()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			_userCardPrefab = Object.Instantiate<GameObject>(GetBorderedHorizontalBox(new Vector2(700f, 72f), 3));
			GameObject gameObject = ((Component)_userCardPrefab.transform.GetChild(0)).gameObject;
			HorizontalLayoutGroup component = gameObject.GetComponent<HorizontalLayoutGroup>();
			((LayoutGroup)component).childAlignment = (TextAnchor)3;
			((HorizontalOrVerticalLayoutGroup)component).childControlWidth = false;
			bool childForceExpandHeight = (((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false);
			((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = childForceExpandHeight;
			TMP_Text val = GameObjectFactory.CreateSingleText(gameObject.transform, "Name", "", Vector2.one / 2f, new Vector2(190f, 75f), Theme.colors.leaderboard.text, (TextFont)0);
			val.alignment = (TextAlignmentOptions)513;
			TMP_Text val2 = GameObjectFactory.CreateSingleText(gameObject.transform, "State", "", Vector2.one / 2f, new Vector2(190f, 75f), Theme.colors.leaderboard.text, (TextFont)0);
			val2.alignment = (TextAlignmentOptions)516;
			TMP_Text val3 = GameObjectFactory.CreateSingleText(gameObject.transform, "Rank", "", Vector2.one / 2f, new Vector2(190f, 75f), Theme.colors.leaderboard.text, (TextFont)0);
			val3.alignment = (TextAlignmentOptions)516;
			Object.DontDestroyOnLoad((Object)(object)_userCardPrefab);
		}

		private static void SetLiveScorePrefab()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			_liveScorePrefab = GetBorderedHorizontalBox(new Vector2(160f, 28f), 2);
			CanvasGroup val = _liveScorePrefab.AddComponent<CanvasGroup>();
			val.alpha = 0.75f;
			GameObject gameObject = ((Component)_liveScorePrefab.transform.GetChild(0)).gameObject;
			HorizontalLayoutGroup component = gameObject.GetComponent<HorizontalLayoutGroup>();
			bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false);
			((HorizontalOrVerticalLayoutGroup)component).childControlWidth = childControlWidth;
			RectTransform component2 = _liveScorePrefab.GetComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(1f, 0f);
			component2.anchorMin = val2;
			Vector2 pivot = (component2.anchorMax = val2);
			component2.pivot = pivot;
			GameObject val4 = Object.Instantiate<GameObject>(gameObject, gameObject.transform);
			((Object)val4).name = "Mask";
			val4.AddComponent<LayoutElement>().ignoreLayout = true;
			val4.AddComponent<Mask>().showMaskGraphic = false;
			_liveScorePrefab.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_liveScorePrefab);
		}

		private static void SetPointScorePrefab()
		{
			//IL_000a: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			_pointScorePrefab = GetBorderedHorizontalBox(new Vector2(200f, 28f), 2);
			GameObject gameObject = ((Component)_pointScorePrefab.transform.GetChild(0)).gameObject;
			HorizontalLayoutGroup component = gameObject.GetComponent<HorizontalLayoutGroup>();
			bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false);
			((HorizontalOrVerticalLayoutGroup)component).childControlWidth = childControlWidth;
			RectTransform component2 = _pointScorePrefab.GetComponent<RectTransform>();
			component2.pivot = new Vector2(0f, 1f);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.04f, 0.926f);
			component2.anchorMin = val;
			component2.anchorMax = val;
			_pointScorePrefab.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_pointScorePrefab);
		}

		private static void SetInputFieldPrefab()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0074: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: 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)
			GameObject val = new GameObject("InputFieldHolder");
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchoredPosition = Vector2.zero;
			val2.sizeDelta = new Vector2(350f, 50f);
			GameObject val3 = Object.Instantiate<GameObject>(val, val.transform);
			GameObject val4 = Object.Instantiate<GameObject>(val3, val.transform);
			((Object)val3).name = "Image";
			((Object)val4).name = "Text";
			_inputFieldPrefab = val.AddComponent<TMP_InputField>();
			Vector2 anchorMax = (val2.anchorMin = Vector2.zero);
			val2.anchorMax = anchorMax;
			((Selectable)_inputFieldPrefab).image = val3.AddComponent<Image>();
			RectTransform component = val3.GetComponent<RectTransform>();
			Vector2 val5 = (component.pivot = Vector2.zero);
			anchorMax = (component.anchorMax = val5);
			component.anchorMin = anchorMax;
			component.anchoredPosition = new Vector2(0f, 4f);
			component.sizeDelta = new Vector2(350f, 2f);
			RectTransform component2 = val4.GetComponent<RectTransform>();
			Vector2 val7 = (component2.pivot = Vector2.zero);
			val5 = (component2.anchorMax = val7);
			anchorMax = (component2.anchorMin = val5);
			component2.anchoredPosition = anchorMax;
			component2.sizeDelta = new Vector2(350f, 50f);
			_inputFieldPrefab.textComponent = GameObjectFactory.CreateSingleText(val4.transform, "TextLabel", "", Theme.colors.leaderboard.text, (TextFont)0);
			_inputFieldPrefab.textComponent.rectTransform.pivot = new Vector2(0f, 0.5f);
			_inputFieldPrefab.textComponent.alignment = (TextAlignmentOptions)513;
			_inputFieldPrefab.textComponent.margin = new Vector4(5f, 0f, 0f, 0f);
			_inputFieldPrefab.textComponent.enableWordWrapping = true;
			_inputFieldPrefab.textViewport = _inputFieldPrefab.textComponent.rectTransform;
			Object.DontDestroyOnLoad((Object)(object)_inputFieldPrefab);
		}

		public static TMP_InputField CreateInputField(Transform canvasTransform, string name, Vector2 size, float fontSize, string text, bool isPassword)
		{
			//IL_0019: 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)
			//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)
			TMP_InputField val = Object.Instantiate<TMP_InputField>(_inputFieldPrefab, canvasTransform);
			((Object)val).name = name;
			((Component)val).GetComponent<RectTransform>().sizeDelta = size;
			((Component)((Component)val).transform.Find("Image")).GetComponent<RectTransform>().sizeDelta = new Vector2(size.x, 2f);
			((Component)((Component)val).transform.Find("Text")).GetComponent<RectTransform>().sizeDelta = size;
			((Component)val.textComponent).GetComponent<RectTransform>().sizeDelta = size;
			val.textComponent.fontSize = fontSize;
			val.textComponent.overflowMode = (TextOverflowModes)1;
			val.text = text;
			val.inputType = (InputType)(isPassword ? 2 : 0);
			return val;
		}

		public static GameObject CreateLiveScoreCard(Transform canvasTransform, Vector2 position, string name)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(_liveScorePrefab, canvasTransform);
			val.SetActive(true);
			val.GetComponent<RectTransform>().anchoredPosition = position;
			((Object)val).name = name;
			return val;
		}

		public static GameObject CreatePointScoreCard(Transform canvasTransform, Vector2 position, string name)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(_pointScorePrefab, canvasTransform);
			val.SetActive(true);
			val.GetComponent<RectTransform>().anchoredPosition = position;
			((Object)val).name = name;
			return val;
		}

		public static MultiplayerCard CreateUserCard(Transform canvasTransform)
		{
			MultiplayerCard multiplayerCard = Object.Instantiate<GameObject>(_userCardPrefab, canvasTransform).AddComponent<MultiplayerCard>();
			multiplayerCard.InitTexts();
			return multiplayerCard;
		}

		public static GameObject CreatePasswordInputPrompt(Transform canvasTransform, string titleText, Action<string> OnConfirm, Action OnCancel)
		{
			//IL_0017: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: 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_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			GameObject borderedVerticalBox = GetBorderedVerticalBox(new Vector2(520f, 235f), 4, canvasTransform);
			RectTransform component = borderedVerticalBox.GetComponent<RectTransform>();
			Vector2 val2 = (component.pivot = Vector2.one / 2f);
			Vector2 anchorMin = (component.anchorMax = val2);
			component.anchorMin = anchorMin;
			GameObject gameObject = ((Component)borderedVerticalBox.transform.GetChild(0)).gameObject;
			VerticalLayoutGroup component2 = gameObject.GetComponent<VerticalLayoutGroup>();
			((LayoutGroup)component2).childAlignment = (TextAnchor)1;
			((HorizontalOrVerticalLayoutGroup)component2).spacing = 21f;
			((LayoutGroup)component2).padding = new RectOffset(5, 5, 35, 5);
			TMP_Text val4 = GameObjectFactory.CreateSingleText(gameObject.transform, "TitleText", titleText, (TextFont)0);
			val4.fontSize = 26f;
			val4.fontStyle = (FontStyles)1;
			val4.rectTransform.sizeDelta = new Vector2(0f, 32f);
			GameObject horizontalBox = GetHorizontalBox(new Vector2(0f, 30f), gameObject.transform);
			HorizontalLayoutGroup component3 = horizontalBox.GetComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)component3).spacing = 8f;
			((LayoutGroup)component3).padding = new RectOffset(0, 0, 3, 0);
			TMP_Text val5 = GameObjectFactory.CreateSingleText(horizontalBox.transform, "InputFieldLabel", "Password:", (TextFont)0);
			val5.rectTransform.sizeDelta = new Vector2(115f, 30f);
			val5.alignment = (TextAlignmentOptions)1028;
			TMP_InputField inputField = CreateInputField(horizontalBox.transform, "InputField", new Vector2(275f, 30f), 24f, "", isPassword: true);
			GameObject horizontalBox2 = GetHorizontalBox(new Vector2(0f, 66f), gameObject.transform);
			HorizontalLayoutGroup component4 = horizontalBox2.GetComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)component4).spacing = 100f;
			bool childControlHeight = (((HorizontalOrVerticalLayoutGroup)component4).childForceExpandHeight = false);
			((HorizontalOrVerticalLayoutGroup)component4).childControlHeight = childControlHeight;
			CustomButton val6 = GameObjectFactory.CreateCustomButton(horizontalBox2.transform, Vector2.zero, new Vector2(170f, 65f), "Confirm", "ConfirmButton", (Action)delegate
			{
				OnConfirm?.Invoke(inputField.text);
			});
			CustomButton val7 = GameObjectFactory.CreateCustomButton(horizontalBox2.transform, Vector2.zero, new Vector2(170f, 65f), "Cancel", "CancelButton", OnCancel);
			return borderedVerticalBox;
		}

		public static LobbySettingsInputPrompt CreateLobbySettingsInputPrompt(Transform canvasTransform, Action<string, string, string, string> OnConfirm)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			LobbySettingsInputPrompt lobbySettingsInputPrompt = new LobbySettingsInputPrompt(canvasTransform, OnConfirm);
			lobbySettingsInputPrompt.gameObject.transform.localScale = new Vector3(0f, 0f, 1f);
			return lobbySettingsInputPrompt;
		}

		public static GameObject CreateQuickChatPopup(Transform canvasTransform, Action<MultiplayerSystem.QuickChat> OnBtnClickCallback, Action OnCloseBtnClick)
		{
			//IL_0017: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: 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)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			GameObject borderedVerticalBox = GetBorderedVerticalBox(new Vector2(450f, 700f), 4, canvasTransform);
			borderedVerticalBox.transform.localScale = Vector2.op_Implicit(Vector2.zero);
			GameObject gameObject = ((Component)borderedVerticalBox.transform.GetChild(0)).gameObject;
			((Graphic)gameObject.GetComponent<Image>()).color = (TMPro_ExtensionMethods.CompareRGB(Theme.colors.leaderboard.text, Color.black) ? new Color(1f, 1f, 1f, 1f) : new Color(0f, 0f, 0f, 1f));
			RectTransform component = borderedVerticalBox.GetComponent<RectTransform>();
			Vector2 val2 = (component.pivot = Vector2.one / 2f);
			Vector2 anchorMin = (component.anchorMax = val2);
			component.anchorMin = anchorMin;
			TMP_Text val4 = GameObjectFactory.CreateSingleText(gameObject.transform, "QuickChatTitle", "Quick Chat", (TextFont)0);
			val4.fontSize = 38f;
			val4.fontStyle = (FontStyles)1;
			val4.alignment = (TextAlignmentOptions)1026;
			val4.rectTransform.sizeDelta = new Vector2(450f, 60f);
			((Component)GameObjectFactory.CreateCustomButton(gameObject.transform, Vector2.one * -5f, new Vector2(32f, 32f), AssetManager.GetSprite("Close64.png"), "CloseQuickChatButton", OnCloseBtnClick)).gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
			Color normalColor = default(Color);
			((Color)(ref normalColor))..ctor(0f, 0f, 0f, 0f);
			for (int i = 0; i < 3; i++)
			{
				GameObject horizontalBox = GetHorizontalBox(new Vector2(0f, 205f), gameObject.transform);
				((HorizontalOrVerticalLayoutGroup)horizontalBox.GetComponent<HorizontalLayoutGroup>()).spacing = 20f;
				for (int j = 0; j < 2; j++)
				{
					GameObject verticalBox = GetVerticalBox(new Vector2(190f, 0f), horizontalBox.transform);
					VerticalLayoutGroup component2 = verticalBox.GetComponent<VerticalLayoutGroup>();
					((HorizontalOrVerticalLayoutGroup)component2).spacing = 1f;
					for (int k = 0; k < 4; k++)
					{
						int index = i * 8 + j * 4 + k;
						MultiplayerSystem.QuickChat quickChatOption = MultiplayerSystem.QuickChatToTextDic.ElementAt(index).Key;
						string text = string.Concat(from x in quickChatOption.ToString()
							select (!char.IsUpper(x)) ? x.ToString() : (" " + x)).TrimStart(new char[1] { ' ' });
						CustomButton val5 = GameObjectFactory.CreateCustomButton(verticalBox.transform, Vector2.zero, new Vector2(0f, 36f), text, $"QCButton{quickChatOption}", (Action)delegate
						{
							OnBtnClickCallback(quickChatOption);
						});
						ColorBlock colors = ((Selectable)val5.button).colors;
						((ColorBlock)(ref colors)).normalColor = normalColor;
						((Selectable)val5.button).colors = colors;
					}
				}
			}
			return borderedVerticalBox;
		}

		public static GameObject GetVerticalBox(Vector2 size, Transform parent = null)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(AssetBundleManager.GetPrefab("verticalbox"), parent);
			val.GetComponent<RectTransform>().sizeDelta = size;
			return val;
		}

		public static GameObject GetHorizontalBox(Vector2 size, Transform parent = null)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(AssetBundleManager.GetPrefab("horizontalbox"), parent);
			val.GetComponent<RectTransform>().sizeDelta = size;
			return val;
		}

		public static GameObject GetBorderedVerticalBox(Vector2 size, int bordersize, Transform parent = null)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_002b: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(AssetBundleManager.GetPrefab("borderedverticalbox"), parent);
			((LayoutGroup)val.GetComponent<VerticalLayoutGroup>()).padding = new RectOffset(bordersize, bordersize, bordersize, bordersize);
			val.GetComponent<RectTransform>().sizeDelta = size + Vector2.one * 2f * (float)bordersize;
			return val;
		}

		public static GameObject GetBorderedHorizontalBox(Vector2 size, int bordersize, Transform parent = null)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_002b: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(AssetBundleManager.GetPrefab("borderedhorizontalbox"), parent);
			((LayoutGroup)val.GetComponent<HorizontalLayoutGroup>()).padding = new RectOffset(bordersize, bordersize, bordersize, bordersize);
			val.GetComponent<RectTransform>().sizeDelta = size + Vector2.one * 2f * (float)bordersize;
			return val;
		}
	}
	public static class MultiplayerManager
	{
		public enum HomeScreenButtonIndexes
		{
			Play,
			Collect,
			Quit,
			Improv,
			Baboon,
			Credit,
			Settings,
			Advanced
		}

		public static readonly WaitForSeconds WAIT_TIME = new WaitForSeconds(5f);

		public static bool AllowExit;

		public const string PLAYTEST_SCENE_NAME = "zzz_playtest";

		public const string LEVELSELECT_SCENE_NAME = "levelselect";

		private static readonly string[] LETTER_GRADES = new string[7] { "F", "D", "C", "B", "A", "S", "SS" };

		private static PlaytestAnims _currentInstance;

		private static PointSceneController _currentPointSceneInstance;

		private static RectTransform _multiButtonOutlineRectTransform;

		private static TootTallyAnimation _multiBtnAnimation;

		private static TootTallyAnimation _multiTextAnimation;

		private static MultiplayerController.MultiplayerState _state;

		private static MultiplayerController.MultiplayerState _previousState;

		private static MultiplayerController _multiController;

		private static bool _isSceneActive;

		private static bool _multiButtonLoaded;

		private static bool _isLevelSelectInit;

		private static bool _attemptedInitLevelSelect;

		private static bool _isRecursiveRefreshRunning;

		private static bool _wasAutotootUsed;

		private static bool _isSyncing = true;

		private static float _syncTimeoutTimer;

		public static MultiplayerController GetMultiplayerController => _multiController;

		public static bool IsConnectedToMultiplayer
		{
			get
			{
				if (_multiController != null)
				{
					return _multiController.IsConnected;
				}
				return false;
			}
		}

		public static bool IsPlayingMultiplayer
		{
			get
			{
				if (_multiController != null)
				{
					return _state == MultiplayerController.MultiplayerState.Playing;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(PlaytestAnims), "Start")]
		[HarmonyPrefix]
		public static bool OnStartPrefixLoadLevelSelectIfNotInit(PlaytestAnims __instance)
		{
			GlobalVariables.scene_destination = "levelselect";
			if (SpectatingManager.IsSpectating)
			{
				SpectatingManager.StopAllSpectator();
			}
			_currentInstance = __instance;
			if (!_isLevelSelectInit)
			{
				_attemptedInitLevelSelect = true;
				__instance.fadepanel.alpha = 1f;
				((Component)__instance.fadepanel).gameObject.SetActive(true);
				SceneManager.LoadScene("levelselect", (LoadSceneMode)1);
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(PlaytestAnims), "Start")]
		[HarmonyPostfix]
		public static void ChangePlayTestToMultiplayerScreen(PlaytestAnims __instance)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (!_isLevelSelectInit)
			{
				return;
			}
			((Component)Camera.main).transform.localPosition = Vector3.zero;
			((Component)__instance.logo_trect).gameObject.SetActive(false);
			((Component)__instance.logo_crect).gameObject.SetActive(false);
			GameModifierManager.ClearAllModifiers();
			MultiplayerGameObjectFactory.Initialize();
			MultiAudioController.InitMusic();
			if (!MultiAudioController.IsMusicLoaded)
			{
				MultiAudioController.LoadMusic("MultiplayerMusic.mp3", delegate
				{
					MultiAudioController.PlayMusicSoft();
				});
			}
			else if (MultiAudioController.IsPaused)
			{
				MultiAudioController.ResumeMusicSoft();
			}
			else
			{
				MultiAudioController.PlayMusicSoft();
			}
			_multiController = new MultiplayerController(__instance);
			_isSceneActive = true;
			AllowExit = false;
			if ((_multiController.IsConnected && _state == MultiplayerController.MultiplayerState.SelectSong) || _state == MultiplayerController.MultiplayerState.PointScene || _state == MultiplayerController.MultiplayerState.Quitting)
			{
				UpdateMultiplayerState(MultiplayerController.MultiplayerState.Lobby);
				_multiController.UpdateLobbySongDetails();
			}
			else
			{
				_previousState = MultiplayerController.MultiplayerState.None;
				UpdateMultiplayerState(MultiplayerController.MultiplayerState.Home);
				StartRecursiveRefresh();
			}
		}

		[HarmonyPatch(typeof(Plugin), "Update")]
		[HarmonyPostfix]
		public static void Update()
		{
			if (!_isSceneActive)
			{
				return;
			}
			if (Input.GetKeyDown((KeyCode)27) && CanPressEscape())
			{
				if (_state == MultiplayerController.MultiplayerState.Home)
				{
					ExitMultiplayer();
				}
				else if (_state == MultiplayerController.MultiplayerState.Lobby)
				{
					_multiController.DisconnectFromLobby();
				}
				else
				{
					_multiController.ReturnToLastPanel();
					RollbackMultiplayerState();
				}
			}
			else if (Input.GetKeyDown((KeyCode)45) && _state != MultiplayerController.MultiplayerState.Playing)
			{
				MultiAudioController.ChangeVolume(-0.01f);
			}
			else if (Input.GetKeyDown((KeyCode)61) && _state != MultiplayerController.MultiplayerState.Playing)
			{
				MultiAudioController.ChangeVolume(0.01f);
			}
			_multiController?.Update();
		}

		private static bool CanPressEscape()
		{
			if (!_multiController.IsTransitioning && !_multiController.IsRequestPending && _state != MultiplayerController.MultiplayerState.ExitScene && _state != MultiplayerController.MultiplayerState.SelectSong && _state != MultiplayerController.MultiplayerState.Playing && _state != MultiplayerController.MultiplayerState.PointScene)
			{
				return _state != MultiplayerController.MultiplayerState.Quitting;
			}
			return false;
		}

		[HarmonyPatch(typeof(PlaytestAnims), "nextScene")]
		[HarmonyPrefix]
		public static bool OverwriteNextScene()
		{
			Plugin.LogInfo("exiting multi");
			_isSceneActive = false;
			SceneManager.LoadScene("saveslot");
			return false;
		}

		[HarmonyPatch(typeof(HomeController), "Start")]
		[HarmonyPostfix]
		public static void OnHomeControllerStartPostFixAddMultiplayerButton(HomeController __instance)
		{
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Expected O, but got Unknown
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Expected O, but got Unknown
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Expected O, but got Unknown
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_038e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0420: Unknown result type (might be due to invalid IL or missing references)
			//IL_0436: Unknown result type (might be due to invalid IL or missing references)
			//IL_0456: Unknown result type (might be due to invalid IL or missing references)
			//IL_0475: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04da: Unknown result type (might be due to invalid IL or missing references)
			//IL_04df: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0515: Unknown result type (might be due to invalid IL or missing references)
			//IL_0530: Unknown result type (might be due to invalid IL or missing references)
			//IL_0566: Unknown result type (might be due to invalid IL or missing references)
			//IL_057c: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_05de: Unknown result type (might be due to invalid IL or missing references)
			GameObject gameObject = GameObject.Find("MainCanvas").gameObject;
			GameObject gameObject2 = ((Component)gameObject.transform.Find("MainMenu")).gameObject;
			GameObject multiplayerButton = Object.Instantiate<GameObject>(__instance.btncontainers[1], gameObject2.transform);
			GameObject val = Object.Instantiate<GameObject>(((Component)gameObject2.transform.Find("Button1Collect")).gameObject, gameObject2.transform);
			GameObject multiplayerText = Object.Instantiate<GameObject>(__instance.paneltxts[1], gameObject2.transform);
			((Object)multiplayerButton).name = "MULTIContainer";
			((Object)val).name = "MULTIButton";
			((Object)multiplayerText).name = "MULTIText";
			Object.DestroyImmediate((Object)(object)((Component)multiplayerText.transform.GetChild(1)).GetComponent<LocalizeStringEvent>());
			Text component = ((Component)multiplayerText.transform.GetChild(1)).GetComponent<Text>();
			string text2 = (((Component)multiplayerText.transform.GetChild(1).GetChild(0)).GetComponent<Text>().text = "<i>MULTI</i>");
			component.text = text2;
			ThemeManager.OverwriteGameObjectSpriteAndColor(((Component)multiplayerButton.transform.Find("FG")).gameObject, "MultiplayerButtonV2.png", Color.white);
			multiplayerButton.transform.SetSiblingIndex(0);
			multiplayerText.transform.SetSiblingIndex(21);
			val.transform.SetSiblingIndex(22);
			RectTransform component2 = multiplayerText.GetComponent<RectTransform>();
			component2.anchoredPosition = new Vector2(100f, 100f);
			component2.sizeDelta = new Vector2(334f, 87f);
			_multiButtonOutlineRectTransform = ((Component)multiplayerButton.transform.Find("outline")).GetComponent<RectTransform>();
			((UnityEvent)val.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				//IL_008d: Unknown result type (might be due to invalid IL or missing references)
				if (__instance.waitforclick == 0f)
				{
					__instance.addWaitForClick();
					__instance.playSfx(3);
					if (TootTallyUser.userInfo == null || TootTallyUser.userInfo.id == 0)
					{
						TootTallyNotifManager.DisplayError("Please login on TootTally to play online.", 6f);
					}
					else
					{
						Plugin.LogInfo("Entering Multiplayer...");
						__instance.musobj.Stop();
						__instance.quickFlash(2);
						LeanTween.scale(__instance.fullcanvas, new Vector3(0.001f, 0.001f, 1f), 0.5f).setEaseInQuart();
						__instance.screenfade.alpha = 0f;
						LeanTween.alphaCanvas(__instance.screenfade, 1f, 0.45f).setDelay(0.25f).setOnComplete((Action)LoadPlayTestScene);
					}
				}
			});
			EventTrigger component3 = val.GetComponent<EventTrigger>();
			component3.triggers.Clear();
			Entry val2 = new Entry();
			val2.eventID = (EventTriggerType)0;
			((UnityEvent<BaseEventData>)(object)val2.callback).AddListener((UnityAction<BaseEventData>)delegate
			{
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Expected O, but got Unknown
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b5: Expected O, but got Unknown
				//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_0100: Unknown result type (might be due to invalid IL or missing references)
				TootTallyAnimation multiBtnAnimation2 = _multiBtnAnimation;
				if (multiBtnAnimation2 != null)
				{
					multiBtnAnimation2.Dispose();
				}
				_multiBtnAnimation = TootTallyAnimationManager.AddNewScaleAnimation(((Component)multiplayerButton.transform.Find("outline")).gameObject, new Vector2(1.01f, 1.01f), 0.5f, new SecondDegreeDynamicsAnimation(3.75f, 0.8f, 1.05f), (Action<GameObject>)null);
				_multiBtnAnimation.SetStartVector(((Transform)_multiButtonOutlineRectTransform).localScale);
				TootTallyAnimation multiTextAnimation2 = _multiTextAnimation;
				if (multiTextAnimation2 != null)
				{
					multiTextAnimation2.Dispose();
				}
				_multiTextAnimation = TootTallyAnimationManager.AddNewScaleAnimation(multiplayerText, new Vector2(1f, 1f), 0.5f, new SecondDegreeDynamicsAnimation(3.5f, 0.65f, 1.15f), (Action<GameObject>)null);
				_multiTextAnimation.SetStartVector(((Transform)multiplayerText.GetComponent<RectTransform>()).localScale);
				__instance.playSfx(2);
				RectTransform component11 = multiplayerButton.GetComponent<RectTransform>();
				component11.anchoredPosition += new Vector2(-2f, 0f);
			});
			component3.triggers.Add(val2);
			Entry val3 = new Entry();
			val3.eventID = (EventTriggerType)1;
			((UnityEvent<BaseEventData>)(object)val3.callback).AddListener((UnityAction<BaseEventData>)delegate
			{
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Expected O, but got Unknown
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b5: Expected O, but got Unknown
				//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
				TootTallyAnimation multiBtnAnimation = _multiBtnAnimation;
				if (multiBtnAnimation != null)
				{
					multiBtnAnimation.Dispose();
				}
				_multiBtnAnimation = TootTallyAnimationManager.AddNewScaleAnimation(((Component)multiplayerButton.transform.Find("outline")).gameObject, new Vector2(0.4f, 0.4f), 0.5f, new SecondDegreeDynamicsAnimation(1.5f, 0.8f, 1f), (Action<GameObject>)null);
				_multiBtnAnimation.SetStartVector(((Transform)_multiButtonOutlineRectTransform).localScale);
				TootTallyAnimation multiTextAnimation = _multiTextAnimation;
				if (multiTextAnimation != null)
				{
					multiTextAnimation.Dispose();
				}
				_multiTextAnimation = TootTallyAnimationManager.AddNewScaleAnimation(multiplayerText, new Vector2(0.8f, 0.8f), 0.5f, new SecondDegreeDynamicsAnimation(3.5f, 0.65f, 1.15f), (Action<GameObject>)null);
				_multiTextAnimation.SetStartVector(((Transform)multiplayerText.GetComponent<RectTransform>()).localScale);
				RectTransform component10 = multiplayerButton.GetComponent<RectTransform>();
				component10.anchoredPosition += new Vector2(2f, 0f);
			});
			component3.triggers.Add(val3);
			_multiButtonLoaded = true;
			GameObject val4 = __instance.btncontainers[1];
			ThemeManager.OverwriteGameObjectSpriteAndColor(((Component)val4.transform.Find("FG")).gameObject, "CollectButtonV2.png", Color.white);
			GameObject gameObject3 = ((Component)val4.transform.Find("FG")).gameObject;
			RectTransform component4 = gameObject3.GetComponent<RectTransform>();
			val4.GetComponent<RectTransform>().anchoredPosition = new Vector2(900f, 475.2f);
			val4.GetComponent<RectTransform>().sizeDelta = new Vector2(320f, 190f);
			component4.sizeDelta = new Vector2(320f, 190f);
			GameObject val5 = __instance.allbtnoutlines[1];
			ThemeManager.OverwriteGameObjectSpriteAndColor(val5, "CollectButtonOutline.png", Color.white);
			RectTransform component5 = val5.GetComponent<RectTransform>();
			component5.sizeDelta = new Vector2(351f, 217.2f);
			GameObject val6 = __instance.paneltxts[1];
			val6.transform.GetChild(1).localScale = Vector3.one * 0.6f;
			val6.GetComponent<RectTransform>().anchoredPosition = new Vector2(790f, 430f);
			val6.GetComponent<RectTransform>().sizeDelta = new Vector2(285f, 48f);
			val6.GetComponent<RectTransform>().pivot = Vector2.one / 2f;
			GameObject val7 = __instance.btncontainers[3];
			GameObject gameObject4 = ((Component)val7.transform.Find("FG")).gameObject;
			ThemeManager.OverwriteGameObjectSpriteAndColor(gameObject4, "ImprovButtonV2.png", Color.white);
			RectTransform component6 = gameObject4.GetComponent<RectTransform>();
			val7.GetComponent<RectTransform>().anchoredPosition = new Vector2(-150f, 156f);
			component6.sizeDelta = new Vector2(450f, 190f);
			GameObject val8 = __instance.allbtnoutlines[3];
			ThemeManager.OverwriteGameObjectSpriteAndColor(val8, "ImprovButtonOutline.png", Color.white);
			RectTransform component7 = val8.GetComponent<RectTransform>();
			component7.sizeDelta = new Vector2(480f, 220f);
			GameObject val9 = __instance.paneltxts[3];
			Transform child = val9.transform.GetChild(0);
			Transform child2 = val9.transform.GetChild(1);
			Vector3 localScale = (child2.localScale = Vector3.one * 0.6f);
			child.localScale = localScale;
			child.localPosition = Vector2.op_Implicit(new Vector2(-196f, 0f));
			child2.localPosition = Vector2.op_Implicit(new Vector2(42f, 25f));
			val9.GetComponent<RectTransform>().anchoredPosition = new Vector2(305f, 385f);
			val9.GetComponent<RectTransform>().sizeDelta = new Vector2(426f, 54f);
			GameObject gameObject5 = ((Component)gameObject2.transform.Find("Button1Collect")).gameObject;
			RectTransform component8 = gameObject5.GetComponent<RectTransform>();
			component8.anchoredPosition = new Vector2(739f, 380f);
			component8.sizeDelta = new Vector2(320f, 190f);
			((Transform)component8).Rotate(0f, 0f, 15f);
			GameObject gameObject6 = ((Component)gameObject2.transform.Find("Button3")).gameObject;
			RectTransform component9 = gameObject6.GetComponent<RectTransform>();
			component9.anchoredPosition = new Vector2(310f, 383f);
			component9.sizeDelta = new Vector2(450f, 195f);
		}

		private static void LoadPlayTestScene()
		{
			SceneManager.LoadScene("zzz_playtest");
		}

		[HarmonyPatch(typeof(HomeController), "Update")]
		[HarmonyPostfix]
		public static void AnimateMultiButton(HomeController __instance)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (_multiButtonLoaded)
			{
				((Component)((Component)((Component)_multiButtonOutlineRectTransform).transform.parent).transform.Find("FG/texholder")).GetComponent<CanvasGroup>().alpha = (((Transform)_multiButtonOutlineRectTransform).localScale.y - 0.4f) / 1.5f;
			}
		}

		[HarmonyPatch(typeof(GlobalLeaderboardManager), "OnLevelSelectControllerStartPostfix")]
		[HarmonyPrefix]
		public static bool PreventGlobalLeaderboardFromLoadingWhenInitMulti()
		{
			if (_attemptedInitLevelSelect)
			{
				_attemptedInitLevelSelect = false;
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(GameObjectFactory), "OnLevelSelectControllerInitialize")]
		[HarmonyPostfix]
		public static void ArtificiallyInitLevelSelectOnFirstMultiplayerEnter(LevelSelectController levelSelectController)
		{
			if (!_isLevelSelectInit)
			{
				Plugin.LogInfo("levelselect init success");
				_isLevelSelectInit = true;
				if ((Object)(object)_currentInstance != (Object)null)
				{
					LeanTween.cancelAll();
					SceneManager.UnloadSceneAsync("levelselect");
					_currentInstance.Start();
				}
			}
		}

		[HarmonyPatch(typeof(ReplaySystemManager), "ResolveLoadReplay")]
		[HarmonyPrefix]
		public static bool SkipResolveReplayIfInMulti()
		{
			if (_state != MultiplayerController.MultiplayerState.SelectSong)
			{
				return true;
			}
			TootTallyNotifManager.DisplayNotif("Cannot watch replays in multiplayer.", 6f);
			return false;
		}

		[HarmonyPatch(typeof(LevelSelectController), "Start")]
		[HarmonyPostfix]
		public static void HideBackButton(LevelSelectController __instance)
		{
			if (IsConnectedToMultiplayer)
			{
				_currentInstance.hidefade();
				_multiController.SendUserState(MultSerializableClasses.UserState.SelectingSong);
			}
		}

		[HarmonyPatch(typeof(LevelSelectController), "clickBack")]
		[HarmonyPrefix]
		public static bool ClickBackButtonMultiplayerSelectSong(LevelSelectController __instance)
		{
			if (!IsConnectedToMultiplayer)
			{
				return true;
			}
			OnMultiplayerSelectSongExit(__instance);
			return false;
		}

		[HarmonyPatch(typeof(LevelSelectController), "clickPlay")]
		[HarmonyPrefix]
		public static bool ClickPlayButtonMultiplayerSelectSong(LevelSelectController __instance)
		{
			if (__instance.back_clicked)
			{
				return false;
			}
			if (!IsConnectedToMultiplayer)
			{
				return true;
			}
			SingleTrackData val = (MultiplayerController.savedTrackData = __instance.alltrackslist[__instance.songindex]);
			string trackref = val.trackref;
			TromboneTrack val2 = TrackLookup.lookup(trackref);
			string songHash = SongDataHelper.GetSongHash(val2);
			GlobalVariables.levelselect_index = val.trackindex;
			GlobalVariables.chosen_track = val.trackref;
			GlobalVariables.chosen_track_data = val;
			_multiController.SendSongHashToLobby(songHash, TootTallyGlobalVariables.gameSpeedMultiplier, GameModifierManager.GetModifiersString());
			OnMultiplayerSelectSongExit(__instance);
			return false;
		}

		private static void OnMultiplayerSelectSongExit(LevelSelectController __instance)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			_multiController.SendUserState(MultSerializableClasses.UserState.Ready);
			UpdateMultiplayerState(MultiplayerController.MultiplayerState.Lobby);
			__instance.back_clicked = true;
			__instance.bgmus.Stop();
			__instance.doSfx(__instance.sfx_click);
			__instance.fader.SetActive(true);
			__instance.fader.transform.localScale = new Vector3(9.9f, 0.001f, 1f);
			LeanTween.cancelAll();
			LeanTween.scaleY(__instance.fader, 9.75f, 0.25f).setEaseInQuart().setOnComplete((Action)delegate
			{
				//IL_009a: Unknown result type (might be due to invalid IL or missing references)
				_multiController.ShowPanel();
				SceneManager.UnloadSceneAsync("levelselect");
				MultiAudioController.ResumeMusicSoft();
				_currentInstance.startBGAnims();
				_currentInstance.fadepanel.alpha = 1f;
				((Component)_currentInstance.fadepanel).gameObject.SetActive(true);
				LeanTween.alphaCanvas(_currentInstance.fadepanel, 0f, 1f).setOnComplete((Action)_currentInstance.hidefade);
				_currentInstance.factpanel.anchoredPosition3D = new Vector3(0f, -600f, 0f);
			});
		}

		[HarmonyPatch(typeof(LevelSelectController), "hoverPlay")]
		[HarmonyPrefix]
		public static bool PreventHoverPlayLeanTweenWhenTransitioning(LevelSelectController __instance)
		{
			if (IsConnectedToMultiplayer)
			{
				return !__instance.back_clicked;
			}
			return false;
		}

		[HarmonyPatch(typeof(LevelSelectController), "unHoverPlay")]
		[HarmonyPrefix]
		public static bool PreventUnhoverPlayLeanTweenWhenTransitioning(LevelSelectController __instance)
		{
			if (IsConnectedToMultiplayer)
			{
				return !__instance.back_clicked;
			}
			return false;
		}

		[HarmonyPatch(typeof(PointSceneController), "Start")]
		[HarmonyPostfix]
		private static void OnPointSceneControllerStart(PointSceneController __instance)
		{
			if (_state == MultiplayerController.MultiplayerState.Playing)
			{
				_currentPointSceneInstance = __instance;
				_multiController.SendSongFinishedToLobby();
				_multiController.InitializePointScore();
				UpdateMultiplayerState(MultiplayerController.MultiplayerState.PointScene);
				__instance.btn_retry_obj.SetActive(false);
				__instance.btn_nav_cards.SetActive(false);
				__instance.btn_nav_baboon.SetActive(false);
				__instance.btn_leaderboard.SetActive(false);
			}
		}

		public static void OnMultiplayerPointScoreClick(int score, float percent, int maxCombo, int[] noteTally)
		{
			if (!((Object)(object)_currentPointSceneInstance == (Object)null))
			{
				_currentPointSceneInstance.txt_score.text = $"{score}:n0 {percent:0.00}%";
				_currentPointSceneInstance.txt_perfectos.text = noteTally[4].ToString("n0");
				_currentPointSceneInstance.txt_nices.text = noteTally[3].ToString("n0");
				_currentPointSceneInstance.txt_okays.text = noteTally[2].ToString("n0");
				_currentPointSceneInstance.txt_mehs.text = noteTally[1].ToString("n0");
				_currentPointSceneInstance.txt_nasties.text = noteTally[0].ToString("n0");
				_currentPointSceneInstance.scorepercentage = Mathf.Max((float)(score / GlobalVariables.gameplay_absolute_max_score), 0.01f);
				_currentPointSceneInstance.scoreindex = (int)Mathf.Clamp(_currentPointSceneInstance.scorepercentage / 0.2f - 1f, -1f, 5f);
				_currentPointSceneInstance.letterscore = LETTER_GRADES[_currentPointSceneInstance.scoreindex + 1];
				_currentPointSceneInstance.popScoreAnim(_currentPointSceneInstance.scoreindex + 1);
			}
		}

		[HarmonyPatch(typeof(PointSceneController), "clickCont")]
		[HarmonyPostfix]
		private static void OnClickContReturnToMulti(PointSceneController __instance)
		{
			if (_state == MultiplayerController.MultiplayerState.PointScene)
			{
				__instance.scenetarget = "zzz_playtest";
			}
		}

		[HarmonyPatch(typeof(PointSceneController), "clickRetry")]
		[HarmonyPrefix]
		private static bool OnRetryClickPreventRetry()
		{
			if (_state == MultiplayerController.MultiplayerState.PointScene)
			{
				TootTallyNotifManager.DisplayNotif("Can't retry in multiplayer.", 6f);
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(GameController), "pauseRetryLevel")]
		[HarmonyPrefix]
		private static bool PreventRetryInMultiplayer()
		{
			if (_multiController != null && _state == MultiplayerController.MultiplayerState.Quitting)
			{
				TootTallyNotifManager.DisplayNotif("Can't quick retry in multiplayer.", 6f);
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(GameController), "pauseQuitLevel")]
		[HarmonyPrefix]
		private static bool PreventQuickQuittingInMultiplayer()
		{
			if (_multiController != null && _state == MultiplayerController.MultiplayerState.Quitting)
			{
				TootTallyNotifManager.DisplayNotif("Can't fast quit in multiplayer.", 6f);
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(GameController), "doScoreText")]
		[HarmonyPostfix]
		private static void OnDoScoreTextSendScoreToLobby(int whichtext, GameController __instance)
		{
			if (IsPlayingMultiplayer && IsConnectedToMultiplayer && !_wasAutotootUsed)
			{
				if (!_wasAutotootUsed && __instance.controllermode)
				{
					_wasAutotootUsed = true;
					_multiController.SendQuitFlag();
				}
				else
				{
					_multiController.SendScoreDataToLobby(__instance.totalscore, __instance.highestcombocounter, (int)__instance.currenthealth, whichtext);
				}
			}
		}

		[HarmonyPatch(typeof(GameController), "Start")]
		[HarmonyPostfix]
		private static void OnMultiplayerGameStart(GameController __instance)
		{
			_wasAutotootUsed = false;
			if (IsPlayingMultiplayer)
			{
				_multiController.OnGameControllerStartSetup();
			}
		}

		[HarmonyPatch(typeof(GameController), "Update")]
		[HarmonyPostfix]
		private static void OnMultiplayerUpdateWaitForSync(GameController __instance)
		{
			if (_isSyncing)
			{
				if (IsPlayingMultiplayer && (!_multiController.IsAnybodyLoading || _syncTimeoutTimer > 10f))
				{
					__instance.startSong(true);
				}
				else
				{
					_syncTimeoutTimer += Time.deltaTime;
				}
			}
			if (IsPlayingMultiplayer)
			{
				__instance.restarttimer = 0.01f;
			}
		}

		[HarmonyPatch(typeof(GameController), "startSong")]
		[HarmonyPrefix]
		private static bool OnMultiplayerWaitForSync()
		{
			if (IsPlayingMultiplayer && _multiController.IsAnybodyLoading && _syncTimeoutTimer < 10f)
			{
				_isSyncing = true;
				_syncTimeoutTimer = 0f;
				_multiController.OnGameControllerStartSongSendReadyState();
				TootTallyNotifManager.DisplayNotif("Waiting for all players to load...", 6f);
				return false;
			}
			_syncTimeoutTimer = 0f;
			_isSyncing = false;
			return true;
		}

		[HarmonyPatch(typeof(PauseCanvasController), "showPausePanel")]
		[HarmonyPrefix]
		public static bool OnGamePause(PauseCanvasController __instance)
		{
			if (!IsPlayingMultiplayer)
			{
				return true;
			}
			UpdateMultiplayerState(MultiplayerController.MultiplayerState.Quitting);
			GameController gc = __instance.gc;
			gc.paused = true;
			gc.quitting = true;
			gc.sfxrefs.backfromfreeplay.Play();
			gc.pausecanvas.SetActive(false);
			gc.curtainc.closeCurtain(false);
			LeanTween.alphaCanvas(__instance.curtaincontroller.fullfadeblack, 1f, 0.5f).setDelay(0.6f).setEaseInOutQuint()
				.setOnComplete((Action)delegate
				{
					__instance.curtaincontroller.unloadAssets();
					SceneManager.LoadScene("zzz_playtest");
				});
			return false;
		}

		[HarmonyPatch(typeof(SpectatingManager), "OnSpectateButtonPress")]
		[HarmonyPrefix]
		public static bool PreventSpectatingWhileInMultiplayer()
		{
			if (IsPlayingMultiplayer || IsConnectedToMultiplayer || _isSceneActive)
			{
				TootTallyNotifManager.DisplayNotif("Cannot spectate someone while in multiplayer.", 6f);
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(GameModifierManager), "Toggle")]
		[HarmonyPrefix]
		public static bool PreventBrutalAndInstaFailToggles(ModifierType modifierType)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Invalid comparison between Unknown and I4
			if (IsPlayingMultiplayer || IsConnectedToMultiplayer || _isSceneActive)
			{
				if ((int)modifierType == 2)
				{
					TootTallyNotifManager.DisplayNotif("Cannot enable Brutal Mode in multiplayer.", 6f);
					return false;
				}
				if ((int)modifierType == 3)
				{
					TootTallyNotifManager.DisplayNotif("Cannot enable InstaFail in multiplayer.", 6f);
					return false;
				}
			}
			return true;
		}

		public static void ExitMultiplayer()
		{
			if (AllowExit)
			{
				UpdateMultiplayerState(MultiplayerController.MultiplayerState.ExitScene);
			}
		}

		public static void UpdateMultiplayerStateIfChanged(MultiplayerController.MultiplayerState newState)
		{
			if (_state != newState)
			{
				_previousState = _state;
				_state = newState;
				ResolveMultiplayerState();
			}
		}

		public static void UpdateMultiplayerState(MultiplayerController.MultiplayerState newState)
		{
			_previousState = _state;
			_state = newState;
			ResolveMultiplayerState();
		}

		private static void ResolveMultiplayerState()
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			Plugin.LogInfo($"Multiplayer state changed from {_previousState} to {_state}");
			switch (_state)
			{
			case MultiplayerController.MultiplayerState.Lobby:
				_multiController.OnLobbyConnectionSuccess();
				break;
			case MultiplayerController.MultiplayerState.SelectSong:
				_currentInstance.fadepanel.alpha = 0f;
				((Component)_currentInstance.fadepanel).gameObject.SetActive(true);
				MultiAudioController.PauseMusicSoft();
				LeanTween.alphaCanvas(_currentInstance.fadepanel, 1f, 0.25f).setOnComplete((Action)delegate
				{
					SceneManager.LoadScene("levelselect", (LoadSceneMode)1);
					_multiController.HidePanel();
				});
				_currentInstance.factpanel.anchoredPosition3D = new Vector3(0f, -600f, 0f);
				break;
			case MultiplayerController.MultiplayerState.ExitScene:
				LeanTween.cancel(((Component)_currentInstance.fadepanel).gameObject);
				MultiAudioController.StopMusicSoft();
				StopRecursiveRefresh();
				_multiController.Dispose();
				LeaveScene();
				break;
			case MultiplayerController.MultiplayerState.Playing:
				StopRecursiveRefresh();
				break;
			case MultiplayerController.MultiplayerState.Quitting:
				if (!_wasAutotootUsed)
				{
					_multiController.SendQuitFlag();
				}
				_multiController.OnSongQuit();
				break;
			case MultiplayerController.MultiplayerState.Home:
			case MultiplayerController.MultiplayerState.CreatingLobby:
			case MultiplayerController.MultiplayerState.Hosting:
			case MultiplayerController.MultiplayerState.PointScene:
				break;
			}
		}

		private static void LeaveScene()
		{
			if (TootTallyUser.userInfo.id == 8)
			{
				_currentInstance.nextScene();
				return;
			}
			_currentInstance.sfx_ok.Play();
			((Component)_currentInstance.fadepanel).gameObject.SetActive(true);
			LeanTween.alphaCanvas(_currentInstance.fadepanel, 1f, 0.4f).setOnComplete((Action)_currentInstance.nextScene);
		}

		public static void RollbackMultiplayerState()
		{
			MultiplayerController.MultiplayerState state = _state;
			_state = _previousState;
			_previousState = state;
			ResolveMultiplayerState();
		}

		public static void StartRecursiveRefresh()
		{
			_isRecursiveRefreshRunning = true;
			((MonoBehaviour)_currentInstance).StartCoroutine((IEnumerator)RecursiveLobbyRefresh());
		}

		public static void StopRecursiveRefresh()
		{
			_isRecursiveRefreshRunning = false;
			((MonoBehaviour)_currentInstance).StopCoroutine((IEnumerator)RecursiveLobbyRefresh());
		}

		private static IEnumerator<WaitForSeconds> RecursiveLobbyRefresh()
		{
			yield return WAIT_TIME;
			if ((Object)(object)_currentInstance != (Object)null && _isRecursiveRefreshRunning)
			{
				_multiController.RefreshAllLobbyInfo();
				((MonoBehaviour)_currentInstance).StartCoroutine((IEnumerator)RecursiveLobbyRefresh());
			}
		}

		public static void DebugFakeLobby()
		{
			_multiController?.DebugFakeLobby();
		}

		public static void DebugFakeUser()
		{
			_multiController?.DebugFakeUser();
		}
	}
	[BepInPlugin("TootTallyMultiplayer", "TootTallyMultiplayer", "1.0.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("Tooter")]
	public class Plugin : BaseUnityPlugin, ITootTallyModule
	{
		public static Plugin Instance;

		private const string CONFIG_NAME = "Multiplayer.cfg";

		private Harmony _harmony;

		public static TootTallySettingPage settingPage;

		public bool IsConfigInitialized { get; set; }

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

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

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

		public ConfigEntry<string> SavedLobbyTitle { get; set; }

		public ConfigEntry<string> SavedLobbyDesc { get; set; }

		public ConfigEntry<int> SavedLobbyMaxPlayer { get; 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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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);
			}
		}

		public void Update()
		{
		}

		private void TryInitialize()
		{
			ModuleConfigEnabled = ((BaseUnityPlugin)Plugin.Instance).Config.Bind<bool>("Modules", "Multiplayer", true, "Enable TootTally's Multiplayer module");
			TootTallyModuleManager.AddModule((ITootTallyModule)(object)this);
			Plugin.Instance.AddModuleToSettingPage((ITootTallyModule)(object)this);
		}

		public void LoadModule()
		{
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
			ShowLiveScore = ((BaseUnityPlugin)this).Config.Bind<bool>("Gameplay", "ShowLiveScore", true, "Show the live score while playing in multiplayer.");
			SavedLobbyTitle = ((BaseUnityPlugin)this).Config.Bind<string>("General", "SavedLobbyTitle", "", "Last lobby creation name used.");
			SavedLobbyDesc = ((BaseUnityPlugin)this).Config.Bind<string>("General", "SavedLobbyDesc", "Welcome to my lobby!", "Last lobby creation description used.");
			SavedLobbyMaxPlayer = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SavedLobbyMaxPlayer", 16, "Last lobby creation lobby max player used.");
			string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location), "multiplayerassetbundle");
			AssetBundleManager.LoadAssets(text);
			string text2 = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location), "Assets");
			AssetManager.LoadAssets(text2);
			_harmony.PatchAll(typeof(MultiplayerManager));
			LogInfo("Module loaded!");
		}

		public void UnloadModule()
		{
			_harmony.UnpatchSelf();
			LogInfo("Module unloaded!");
		}
	}
	public class MultiplayerSystem : WebsocketManager
	{
		public enum DataType
		{
			SongInfo,
			LobbyInfo,
			OptionInfo,
			SetSong,
			SetLobbyInfo
		}

		public enum QuickChat
		{
			Hello,
			Welcome,
			Bye,
			Leave,
			Wait,
			ImReady,
			ReadyUp,
			GoodLuck,
			GoodChart,
			BadChart,
			Yes,
			No,
			TooFast,
			TooSlow,
			TooHard,
			TooEasy,
			NicePlay,
			GoodGame,
			CloseOne,
			Rematch,
			Laugh,
			Enjoy,
			WantHost,
			GiveHost
		}

		public enum OptionInfoType
		{
			Refresh,
			UpdateUserInfo,
			UpdateUserState,
			UpdateScore,
			SongFinished,
			FinalScore,
			QuickChat,
			Quit,
			GiveHost,
			KickFromLobby,
			StartTimer,
			StartGame,
			AbortGame,
			LobbyInfoChanged
		}

		public interface ISocketMessage
		{
			string dataType { get; }
		}

		public struct SocketSetSongByHash : ISocketMessage
		{
			public string filehash { get; set; }

			public float gamespeed { get; set; }

			public string modifiers { get; set; }

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

		public struct SocketSetLobbyInfo : ISocketMessage
		{
			public string name;

			public string description;

			public string password;

			public int maxPlayer;

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

		public struct SocketOptionInfo : ISocketMessage
		{
			public string optionType { get; set; }

			public dynamic[] values { get; set; }

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

		public struct SocketLobbyInfo : ISocketMessage
		{
			public MultSerializableClasses.MultiplayerLobbyInfo lobbyInfo { get; set; }

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

		public struct SocketSongInfo : ISocketMessage
		{
			public MultSerializableClasses.MultiplayerSongInfo songInfo { get; set; }

			public string dataType => DataType.SongInfo.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 (obj is DataType)
				{
					switch ((DataType)obj)
					{
					case DataType.SongInfo:
						return ((JToken)val).ToObject<SocketSongInfo>(serializer);
					case DataType.LobbyInfo:
						return ((JToken)val).ToObject<SocketLobbyInfo>(serializer);
					case DataType.OptionInfo:
						return ((JToken)val).ToObject<SocketOptionInfo>(serializer);
					}
				}
				return null;
			}

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

		public Action OnWebSocketOpenCallback;

		public Action OnWebSocketCloseCallback;

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

		public ConcurrentQueue<SocketSongInfo> _receivedSongInfo;

		public ConcurrentQueue<SocketLobbyInfo> _receivedLobbyInfo;

		public ConcurrentQueue<SocketOptionInfo> _receivedSocketOptionInfo;

		public Action<SocketSongInfo> OnSocketSongInfoReceived;

		public Action<SocketLobbyInfo> OnSocketLobbyInfoReceived;

		public Action<SocketOptionInfo> OnSocketOptionReceived;

		public static readonly Dictionary<QuickChat, string> QuickChatToTextDic = new Dictionary<QuickChat, string>
		{
			{
				QuickChat.Hello,
				"Hello!"
			},
			{
				QuickChat.Welcome,
				"Welcome!"
			},
			{
				QuickChat.Bye,
				"Bye bye!"
			},
			{
				QuickChat.Leave,
				"I have to leave."
			},
			{
				QuickChat.Wait,
				"Wait for me."
			},
			{
				QuickChat.ImReady,
				"I'm ready!"
			},
			{
				QuickChat.ReadyUp,
				"Ready up!"
			},
			{
				QuickChat.GoodLuck,
				"Good luck!"
			},
			{
				QuickChat.GoodChart,
				"I like this chart."
			},
			{
				QuickChat.BadChart,
				"I don't like this chart."
			},
			{
				QuickChat.Yes,
				"Yes."
			},
			{
				QuickChat.No,
				"No."
			},
			{
				QuickChat.TooFast,
				"I think the game speed is too fast."
			},
			{
				QuickChat.TooSlow,
				"I think the game speed is too slow."
			},
			{
				QuickChat.TooHard,
				"I think the song is too hard."
			},
			{
				QuickChat.TooEasy,
				"I think the song is too easy."
			},
			{
				QuickChat.NicePlay,
				"Nice play!"
			},
			{
				QuickChat.GoodGame,
				"Good game."
			},
			{
				QuickChat.CloseOne,
				"That was a close match!"
			},
			{
				QuickChat.Rematch,
				"Rematch!"
			},
			{
				QuickChat.Laugh,
				"Ahah!"
			},
			{
				QuickChat.Enjoy,
				"Im enjoying this lobby!"
			},
			{
				QuickChat.WantHost,
				"Can I have host?"
			},
			{
				QuickChat.GiveHost,
				"Who want to be host?"
			}
		};

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

		public MultiplayerSystem(string serverID, bool isHost)
			: base(serverID, "wss://spec.toottally.com/mp/join/", "1.0.3")
		{
			((WebsocketManager)this).ConnectionPending = true;
			_receivedSongInfo = new ConcurrentQueue<SocketSongInfo>();
			_receivedLobbyInfo = new ConcurrentQueue<SocketLobbyInfo>();
			_receivedSocketOptionInfo = new ConcurrentQueue<SocketOptionInfo>();
			((WebsocketManager)this).ConnectToWebSocketServer(((WebsocketManager)this)._url + serverID, Plugin.GetAPIKey, isHost);
		}

		public void SendSongHash(string filehash, float gamespeed, string modifiers)
		{
			SocketSetSongByHash socketSetSongByHash = default(SocketSetSongByHash);
			socketSetSongByHash.filehash = filehash;
			socketSetSongByHash.gamespeed = gamespeed;
			socketSetSongByHash.modifiers = modifiers;
			SocketSetSongByHash socketSetSongByHash2 = socketSetSongByHash;
			SendSongHash(socketSetSongByHash2);
		}

		public void SendSetLobbyInfo(string name, string description, string password, int maxPlayer)
		{
			SocketSetLobbyInfo socketSetLobbyInfo = default(SocketSetLobbyInfo);
			socketSetLobbyInfo.name = name;
			socketSetLobbyInfo.description = description;
			socketSetLobbyInfo.password = password;
			socketSetLobbyInfo.maxPlayer = maxPlayer;
			SocketSetLobbyInfo socketSetLobbyInfo2 = socketSetLobbyInfo;
			string text = JsonConvert.SerializeObject((object)socketSetLobbyInfo2);
			((WebsocketManager)this).SendToSocket(text);
		}

		public void SendSongHash(SocketSetSongByHash socketSetSongByHash)
		{
			string text = JsonConvert.SerializeObject((object)socketSetSongByHash);
			((WebsocketManager)this).SendToSocket(text);
		}

		public void SendOptionInfo(OptionInfoType optionType, dynamic[] values = null)
		{
			SocketOptionInfo socketOptionInfo = default(SocketOptionInfo);
			socketOptionInfo.optionType = optionType.ToString();
			socketOptionInfo.values = values;
			SocketOptionInfo socketOptionInfo2 = socketOptionInfo;
			string text = JsonConvert.SerializeObject((object)socketOptionInfo2);
			((WebsocketManager)this).SendToSocket(text);
		}

		public void SendUserState(MultSerializableClasses.UserState state)
		{
			SendOptionInfo(OptionInfoType.UpdateUserState, new object[1] { state.ToString() });
		}

		public void SendUpdateScore(int score, int combo, int health, int tally)
		{
			SendOptionInfo(OptionInfoType.UpdateScore, new object[4] { score, combo, health, tally });
		}

		public void UpdateStacks()
		{
			if (OnSocketSongInfoReceived != null && _receivedSongInfo.TryDequeue(out var result))
			{
				OnSocketSongInfoReceived(result);
			}
			if (OnSocketLobbyInfoReceived != null && _receivedLobbyInfo.TryDequeue(out var result2))
			{
				OnSocketLobbyInfoReceived(result2);
			}
			if (OnSocketOptionReceived != null && _receivedSocketOptionInfo.TryDequeue(out var result3))
			{
				OnSocketOptionReceived(result3);
			}
		}

		protected override void OnDataReceived(object sender, MessageEventArgs e)
		{
			if (e.IsText)
			{
				ISocketMessage socketMessage;
				try
				{
					socketMessage = JsonConvert.DeserializeObject<ISocketMessage>(e.Data, _dataConverter);
				}
				catch (Exception)
				{
					return;
				}
				if (socketMessage is SocketSongInfo item && !((WebsocketManager)this).IsHost)
				{
					_receivedSongInfo.Enqueue(item);
				}
				else if (socketMessage is SocketOptionInfo item2)
				{
					_receivedSocketOptionInfo.Enqueue(item2);
				}
				else if (socketMessage is SocketLobbyInfo)
				{
					SocketLobbyInfo item3 = (SocketLobbyInfo)(object)socketMessage;
					_receivedLobbyInfo.Enqueue(item3);
				}
			}
		}

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

		protected overr