Decompiled source of ProCon v1.0.0

Mods/ProCon.dll

Decompiled a week ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using HarmonyLib;
using Il2CppCS.CorePlatform;
using Il2CppCS.CorePlatform.Utils.Data;
using Il2CppCoreNet;
using Il2CppCoreNet.Components.Client;
using Il2CppCoreNet.Contexts;
using Il2CppCostumes;
using Il2CppFemur;
using Il2CppGB.Core.Loading;
using Il2CppGB.NetworkedInput;
using Il2CppGB.Platform.Lobby;
using Il2CppGB.Platform.Lobby.Messages;
using Il2CppGB.Platform.Lobby.Utils;
using Il2CppGB.UI.Beasts;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Reflection;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using ProCon;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(ProConMod), "ProCon", "1.0.0", "AxisGames", null)]
[assembly: MelonGame("Boneloaf", "Gang Beasts")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ProCon
{
	internal static class Badge
	{
		private static GameObject badge;

		private static TMP_Text text;

		public static void Tick()
		{
			string text = Session.BadgeLine();
			if ((Object)(object)badge == (Object)null)
			{
				if (text == null || (Object)(object)MenuFont.Font == (Object)null)
				{
					return;
				}
				Build();
			}
			badge.SetActive(text != null);
			if (text != null)
			{
				Badge.text.text = text;
			}
		}

		private static void Build()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0097: 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_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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)
			GameObject val = new GameObject("ProCon Badge");
			Object.DontDestroyOnLoad((Object)(object)val);
			Canvas obj = val.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 5000;
			CanvasScaler obj2 = val.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = new Vector2(1920f, 1080f);
			obj2.matchWidthOrHeight = 0.5f;
			badge = new GameObject("Badge");
			badge.transform.SetParent(val.transform, false);
			RectTransform obj3 = badge.AddComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 1f);
			obj3.pivot = val2;
			Vector2 anchorMin = (obj3.anchorMax = val2);
			obj3.anchorMin = anchorMin;
			obj3.anchoredPosition = new Vector2(28f, -22f);
			obj3.sizeDelta = new Vector2(700f, 70f);
			text = (TMP_Text)(object)badge.AddComponent<TextMeshProUGUI>();
			text.font = MenuFont.Font;
			text.fontSharedMaterial = MenuFont.OutlinedMaterial;
			text.fontSize = 40f;
			((Graphic)text).color = new Color(1f, 0.78f, 0.05f);
			text.alignment = (TextAlignmentOptions)513;
			text.enableWordWrapping = false;
			((Graphic)text).raycastTarget = false;
		}
	}
	internal static class Clipboard
	{
		private const uint CF_UNICODETEXT = 13u;

		[DllImport("user32.dll")]
		private static extern bool OpenClipboard(IntPtr owner);

		[DllImport("user32.dll")]
		private static extern bool CloseClipboard();

		[DllImport("user32.dll")]
		private static extern IntPtr GetClipboardData(uint format);

		[DllImport("user32.dll")]
		private static extern bool EmptyClipboard();

		[DllImport("user32.dll")]
		private static extern IntPtr SetClipboardData(uint format, IntPtr h);

		[DllImport("kernel32.dll")]
		private static extern IntPtr GlobalLock(IntPtr h);

		[DllImport("kernel32.dll")]
		private static extern bool GlobalUnlock(IntPtr h);

		[DllImport("kernel32.dll")]
		private static extern IntPtr GlobalAlloc(uint flags, UIntPtr bytes);

		public static string Get()
		{
			if (!OpenClipboard(IntPtr.Zero))
			{
				return "";
			}
			try
			{
				IntPtr clipboardData = GetClipboardData(13u);
				if (clipboardData == IntPtr.Zero)
				{
					return "";
				}
				IntPtr ptr = GlobalLock(clipboardData);
				try
				{
					return Marshal.PtrToStringUni(ptr) ?? "";
				}
				finally
				{
					GlobalUnlock(clipboardData);
				}
			}
			finally
			{
				CloseClipboard();
			}
		}

		public static void Set(string text)
		{
			if (!OpenClipboard(IntPtr.Zero))
			{
				return;
			}
			try
			{
				EmptyClipboard();
				IntPtr h = GlobalAlloc(2u, (UIntPtr)(ulong)((text.Length + 1) * 2));
				IntPtr destination = GlobalLock(h);
				Marshal.Copy((text + "\0").ToCharArray(), 0, destination, text.Length + 1);
				GlobalUnlock(h);
				SetClipboardData(13u, h);
			}
			finally
			{
				CloseClipboard();
			}
		}
	}
	internal static class ConsoleFix
	{
		private const int STD_INPUT_HANDLE = -10;

		private const uint ENABLE_QUICK_EDIT_MODE = 64u;

		private const uint ENABLE_EXTENDED_FLAGS = 128u;

		[DllImport("kernel32.dll")]
		private static extern IntPtr GetStdHandle(int handle);

		[DllImport("kernel32.dll")]
		private static extern bool GetConsoleMode(IntPtr h, out uint mode);

		[DllImport("kernel32.dll")]
		private static extern bool SetConsoleMode(IntPtr h, uint mode);

		public static void DisableQuickEdit()
		{
			try
			{
				IntPtr stdHandle = GetStdHandle(-10);
				if (GetConsoleMode(stdHandle, out var mode))
				{
					SetConsoleMode(stdHandle, (mode & 0xFFFFFFBFu) | 0x80);
				}
			}
			catch
			{
			}
		}
	}
	internal static class LobbySync
	{
		private class Remote
		{
			public BaseUserInfo User;

			public int Master;

			public readonly Dictionary<int, byte[]> Beasts = new Dictionary<int, byte[]>();

			public readonly HashSet<int> Spawned = new HashSet<int>();
		}

		public const byte Message = 6;

		public static readonly ConcurrentQueue<(ulong from, byte[] data)> Incoming = new ConcurrentQueue<(ulong, byte[])>();

		private static readonly Dictionary<ulong, Remote> remotes = new Dictionary<ulong, Remote>();

		private static readonly Dictionary<ulong, List<(int ctrl, byte[] data)>> latest = new Dictionary<ulong, List<(int, byte[])>>();

		private static float sendTimer;

		private static int nextMaster;

		public static bool Active;

		public static void Tick(bool hosting)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			bool flag = ((Scene)(ref activeScene)).name == "Menu";
			if (!Active || !flag)
			{
				if (remotes.Count > 0)
				{
					Clear();
				}
				DrainIncoming(apply: false);
				return;
			}
			DrainIncoming(apply: true);
			sendTimer -= Time.unscaledDeltaTime;
			if (sendTimer > 0f)
			{
				return;
			}
			sendTimer = 0.4f;
			List<(int, byte[])> list = LocalBeasts();
			if (hosting)
			{
				foreach (ulong item in Relay.FriendIds())
				{
					List<(ulong, int, byte[])> list2 = new List<(ulong, int, byte[])>();
					foreach (var item2 in list)
					{
						list2.Add((SteamNet.MySteamId, item2.Item1, item2.Item2));
					}
					foreach (KeyValuePair<ulong, List<(int, byte[])>> item3 in latest)
					{
						if (item3.Key != item)
						{
							foreach (var item4 in item3.Value)
							{
								list2.Add((item3.Key, item4.Item1, item4.Item2));
							}
						}
					}
					Relay.SendLobby(item, Pack(list2));
				}
				return;
			}
			List<(ulong, int, byte[])> list3 = new List<(ulong, int, byte[])>();
			foreach (var item5 in list)
			{
				list3.Add((SteamNet.MySteamId, item5.Item1, item5.Item2));
			}
			Relay.SendLobby(0uL, Pack(list3));
		}

		private static List<(int ctrl, byte[] data)> LocalBeasts()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			List<(int, byte[])> list = new List<(int, byte[])>();
			LobbyManager instance = LobbyManager.Instance;
			LocalBeastSetupTracker val = ((instance != null) ? instance.LocalBeasts : null);
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				LocalBeastMessageSender beastMessageSender = instance.BeastMessageSender;
				obj = ((beastMessageSender != null) ? beastMessageSender._beastMessages : null);
			}
			Il2CppReferenceArray<BeastInfoMesssage> val2 = (Il2CppReferenceArray<BeastInfoMesssage>)obj;
			if ((Object)(object)val == (Object)null || val2 == null)
			{
				return list;
			}
			BeastMenuState state = Object.FindObjectOfType<BeastMenuState>();
			for (int i = 0; i < ((Il2CppArrayBase<BeastInfoMesssage>)(object)val2).Length && i < ((Il2CppArrayBase<LocalPlayerInfomationCache>)(object)val.PlayerInfo).Length; i++)
			{
				LocalPlayerInfomationCache val3 = ((Il2CppArrayBase<LocalPlayerInfomationCache>)(object)val.PlayerInfo)[i];
				if (val3 == null || (int)val3.CurrentState == 0)
				{
					continue;
				}
				BeastInfoMesssage obj2 = ((Il2CppArrayBase<BeastInfoMesssage>)(object)val2)[i];
				BeastInfo val4 = ((obj2 != null) ? obj2.Info : null);
				if (val4 != null)
				{
					BeastInfo val5 = new BeastInfo();
					val5.Copy(val4);
					val5.SetControllerID(i);
					val5.SetState(val3.CurrentState);
					if (val3.ColourIndex >= 0)
					{
						val5.SetColour(val3.ColourIndex);
					}
					val5.SetTeam(val3.TeamIndex);
					CostumeSaveEntry val6 = ShownCostume(state, i);
					if (val6 != null)
					{
						val5.SetCostume(val6);
					}
					list.Add((i, ToBytes(val5)));
				}
			}
			return list;
		}

		private static CostumeSaveEntry ShownCostume(BeastMenuState state, int playerID)
		{
			try
			{
				Il2CppReferenceArray<PointState> val = ((state != null) ? state._pointStates : null);
				if (val == null)
				{
					return null;
				}
				foreach (PointState item in (Il2CppArrayBase<PointState>)(object)val)
				{
					if (((item != null) ? item._linkedLocal : null) != null && item._linkedLocal.PlayerID == playerID)
					{
						return item._costumeCache;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static void DrainIncoming(bool apply)
		{
			(ulong, byte[]) result;
			while (Incoming.TryDequeue(out result))
			{
				if (apply)
				{
					try
					{
						Apply(result.Item1, result.Item2);
					}
					catch (Exception ex)
					{
						MelonLogger.Warning("Lobby sync: " + ex.Message);
					}
				}
			}
		}

		private static void Apply(ulong from, byte[] data)
		{
			if (data.Length == 0)
			{
				FriendLeft(from);
				return;
			}
			List<(ulong, int, byte[])> list = Unpack(data);
			ulong mySteamId = SteamNet.MySteamId;
			if (Session.IsHosting)
			{
				List<(int, byte[])> list2 = new List<(int, byte[])>();
				foreach (var item in list)
				{
					if (item.Item1 == from)
					{
						list2.Add((item.Item2, item.Item3));
					}
				}
				latest[from] = list2;
				SyncOwner(from, list2);
				return;
			}
			Dictionary<ulong, List<(int, byte[])>> dictionary = new Dictionary<ulong, List<(int, byte[])>>();
			foreach (var item2 in list)
			{
				if (item2.Item1 != mySteamId)
				{
					if (!dictionary.TryGetValue(item2.Item1, out var value))
					{
						value = (dictionary[item2.Item1] = new List<(int, byte[])>());
					}
					value.Add((item2.Item2, item2.Item3));
				}
			}
			foreach (KeyValuePair<ulong, List<(int, byte[])>> item3 in dictionary)
			{
				SyncOwner(item3.Key, item3.Value);
			}
			foreach (ulong item4 in new List<ulong>(remotes.Keys))
			{
				if (!dictionary.ContainsKey(item4))
				{
					RemoveOwner(item4);
				}
			}
		}

		public static void FriendLeft(ulong id)
		{
			latest.Remove(id);
			if (remotes.ContainsKey(id))
			{
				RemoveOwner(id);
			}
		}

		private static void SyncOwner(ulong owner, List<(int ctrl, byte[] data)> beasts)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Invalid comparison between Unknown and I4
			LobbyManager instance = LobbyManager.Instance;
			OnlineBeastSetupTracker val = ((instance != null) ? instance.OnlineBeasts : null);
			BeastMenuManager val2 = Object.FindObjectOfType<BeastMenuManager>();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if (!remotes.TryGetValue(owner, out var value))
			{
				value = new Remote
				{
					User = new BaseUserInfo(owner, (PlatformKeys)1, SteamNet.Name(owner)),
					Master = BeastUtils.PlayerID_Lobby + 8 * (nextMaster++ % 24)
				};
				val.OnPlayerJoined(value.User, value.Master);
				remotes[owner] = value;
				MelonLogger.Msg("Lobby: " + value.User.userName + " is in the lobby.");
			}
			HashSet<int> hashSet = new HashSet<int>();
			foreach (var (num, array) in beasts)
			{
				hashSet.Add(num);
				if (value.Beasts.TryGetValue(num, out var value2) && Same(value2, array))
				{
					continue;
				}
				value.Beasts[num] = array;
				BeastInfo val3 = new BeastInfo();
				if (!val3.Deserialize(new DataReader(Il2CppStructArray<byte>.op_Implicit(array), true)))
				{
					continue;
				}
				val3.SetControllerID(num);
				val.ForceUpdateBeast(value.User, val3);
				int num2 = value.Master + num;
				bool flag = (int)val3.State > 0;
				if (flag && value.Spawned.Add(num))
				{
					if (val2 != null)
					{
						val2.SpawnBeast(num2, Math.Max(0, val3.Team));
					}
				}
				else if (!flag && value.Spawned.Remove(num) && val2 != null)
				{
					val2.DespawnBeast(num2);
				}
			}
			foreach (int item in new List<int>(value.Beasts.Keys))
			{
				if (!hashSet.Contains(item))
				{
					value.Beasts.Remove(item);
					Hide(value, item, val, val2);
				}
			}
		}

		private static void Hide(Remote r, int ctrl, OnlineBeastSetupTracker tracker, BeastMenuManager menu)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			BeastInfo val = new BeastInfo();
			val.SetControllerID(ctrl);
			val.SetState((PlayerState)0);
			try
			{
				if (tracker != null)
				{
					tracker.ForceUpdateBeast(r.User, val);
				}
			}
			catch
			{
			}
			if (r.Spawned.Remove(ctrl) && menu != null)
			{
				menu.DespawnBeast(r.Master + ctrl);
			}
		}

		private static void RemoveOwner(ulong owner)
		{
			if (!remotes.TryGetValue(owner, out var value))
			{
				return;
			}
			LobbyManager instance = LobbyManager.Instance;
			OnlineBeastSetupTracker val = ((instance != null) ? instance.OnlineBeasts : null);
			BeastMenuManager val2 = Object.FindObjectOfType<BeastMenuManager>();
			foreach (int item in new List<int>(value.Spawned))
			{
				try
				{
					if (val2 != null)
					{
						val2.DespawnBeast(value.Master + item);
					}
				}
				catch
				{
				}
			}
			try
			{
				if (val != null)
				{
					val.OnPlayerLeft(value.User);
				}
			}
			catch
			{
			}
			remotes.Remove(owner);
			MelonLogger.Msg("Lobby: " + value.User.userName + " left the lobby.");
		}

		public static void Clear()
		{
			foreach (ulong item in new List<ulong>(remotes.Keys))
			{
				RemoveOwner(item);
			}
			remotes.Clear();
			latest.Clear();
			nextMaster = 0;
		}

		private static byte[] Pack(List<(ulong owner, int ctrl, byte[] data)> beasts)
		{
			int num = 2;
			foreach (var beast in beasts)
			{
				num += 11 + beast.data.Length;
			}
			byte[] array = new byte[num];
			array[0] = 6;
			array[1] = (byte)beasts.Count;
			int num2 = 2;
			foreach (var (value, num3, array2) in beasts)
			{
				BitConverter.GetBytes(value).CopyTo(array, num2);
				num2 += 8;
				array[num2++] = (byte)num3;
				BitConverter.GetBytes((ushort)array2.Length).CopyTo(array, num2);
				num2 += 2;
				array2.CopyTo(array, num2);
				num2 += array2.Length;
			}
			return array;
		}

		private static List<(ulong owner, int ctrl, byte[] data)> Unpack(byte[] buf)
		{
			List<(ulong, int, byte[])> list = new List<(ulong, int, byte[])>();
			if (buf.Length < 2 || buf[0] != 6)
			{
				return list;
			}
			int num = buf[1];
			int num2 = 2;
			for (int i = 0; i < num; i++)
			{
				if (num2 + 11 > buf.Length)
				{
					break;
				}
				ulong item = BitConverter.ToUInt64(buf, num2);
				num2 += 8;
				int item2 = buf[num2++];
				int num3 = BitConverter.ToUInt16(buf, num2);
				num2 += 2;
				if (num2 + num3 > buf.Length)
				{
					break;
				}
				byte[] array = new byte[num3];
				Array.Copy(buf, num2, array, 0, num3);
				num2 += num3;
				list.Add((item, item2, array));
			}
			return list;
		}

		private static byte[] ToBytes(BeastInfo info)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			DataWriter val = new DataWriter(2048);
			info.Serialize(val);
			Il2CppStructArray<byte> dataBuffer = ((DataStream)val).DataBuffer;
			byte[] array = new byte[Math.Min(((DataStream)val).DataSize, ((Il2CppArrayBase<byte>)(object)dataBuffer).Length)];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = ((Il2CppArrayBase<byte>)(object)dataBuffer)[i];
			}
			return array;
		}

		private static bool Same(byte[] a, byte[] b)
		{
			if (a.Length != b.Length)
			{
				return false;
			}
			for (int i = 0; i < a.Length; i++)
			{
				if (a[i] != b[i])
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class MainMenu
	{
		private class Anim
		{
			public Button Button;

			public RectTransform Rt;

			public TMP_Text Text;

			public float Scale = 1f;

			public float Vel;

			public float Hover;

			public float Enter = 1f;

			public float Delay;

			public float Punch;

			public float Seed;
		}

		private const string SectionPath = "Managers/Menu/Main Menu/Canvas/Main Menu/TextSection";

		private static GameObject section;

		private static GameObject template;

		private static GameObject pcHome;

		private static GameObject pcJoin;

		private static GameObject current;

		private static TMP_Text codeLabel;

		private static TMP_Text hintLabel;

		private static string typed = "";

		private static float caretTimer;

		private static readonly List<Anim> anims = new List<Anim>();

		private static bool wasVisible;

		private static Vector2 lastMouse;

		public static bool InProCon
		{
			get
			{
				if ((Object)(object)current != (Object)null)
				{
					return (Object)(object)current != (Object)(object)section;
				}
				return false;
			}
		}

		public static bool OnMainList
		{
			get
			{
				if ((Object)(object)section != (Object)null && (Object)(object)current == (Object)(object)section)
				{
					return section.activeInHierarchy;
				}
				return false;
			}
		}

		public static void Tick()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).name != "Menu")
			{
				section = null;
				return;
			}
			if ((Object)(object)section == (Object)null)
			{
				section = Util.FindAny("Managers/Menu/Main Menu/Canvas/Main Menu/TextSection");
				if ((Object)(object)section == (Object)null)
				{
					return;
				}
				pcHome = (pcJoin = null);
				current = section;
				anims.Clear();
			}
			if ((Object)(object)pcHome == (Object)null && !Build())
			{
				return;
			}
			if (InProCon && !((Component)section.transform.parent).gameObject.activeInHierarchy)
			{
				Show(section);
			}
			int num;
			if ((Object)(object)current != (Object)null)
			{
				num = (current.activeInHierarchy ? 1 : 0);
				if (num != 0 && !wasVisible)
				{
					PlayEntrance();
				}
			}
			else
			{
				num = 0;
			}
			wasVisible = (byte)num != 0;
			if (num != 0)
			{
				if (InProCon)
				{
					ProConInput();
				}
				Animate();
			}
		}

		private static bool Build()
		{
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)MenuFont.Font == (Object)null)
			{
				return false;
			}
			MenuFont.Apply(((Component)section.transform.parent).gameObject);
			Transform val = section.transform.Find("Online");
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			template = ((Component)val).gameObject;
			MakeButton(section.transform, "ProCon", delegate
			{
				Show(pcHome);
			}).transform.SetSiblingIndex(val.GetSiblingIndex() + 1);
			Navigate(section);
			pcHome = MakePage("ProCon Home");
			MakeButton(pcHome.transform, "Host", OnHost);
			MakeButton(pcHome.transform, "Join", delegate
			{
				Show(pcJoin);
			});
			MakeButton(pcHome.transform, "Back", delegate
			{
				Show(section);
			});
			hintLabel = MakeLabel(pcHome.transform, Session.StatusLine(), 0.5f);
			Navigate(pcHome);
			pcJoin = MakePage("ProCon Join");
			MakeLabel(pcJoin.transform, "Enter code", 0.6f);
			codeLabel = MakeLabel(pcJoin.transform, "", 1.25f);
			((Graphic)codeLabel).color = Color.white;
			codeLabel.characterSpacing = 14f;
			MakeButton(pcJoin.transform, "Connect", OnConnect);
			MakeButton(pcJoin.transform, "Back", delegate
			{
				Show(pcHome);
			});
			Navigate(pcJoin);
			MelonLogger.Msg("ProCon added to main menu.");
			return true;
		}

		private static GameObject MakePage(string name)
		{
			GameObject val = Object.Instantiate<GameObject>(section, section.transform.parent);
			((Object)val).name = name;
			for (int num = val.transform.childCount - 1; num >= 0; num--)
			{
				Object.DestroyImmediate((Object)(object)((Component)val.transform.GetChild(num)).gameObject);
			}
			val.SetActive(false);
			return val;
		}

		private static GameObject Clone(Transform parent, string name, string text)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(template, parent);
			((Object)val).name = name;
			val.SetActive(true);
			foreach (Component component in val.GetComponents<Component>())
			{
				if (!((Object)(object)component == (Object)null))
				{
					switch (((MemberInfo)((Object)component).GetIl2CppType()).Name)
					{
					case "LocalizeStringEvent":
					case "DisabledIfPlatform":
					case "MainUserInteractOnly":
						Object.DestroyImmediate((Object)(object)component);
						break;
					}
				}
			}
			val.transform.localScale = Vector3.one;
			val.GetComponent<TMP_Text>().text = text;
			return val;
		}

		private static GameObject MakeButton(Transform parent, string text, Action onClick)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			GameObject obj = Clone(parent, text, text);
			Button component = obj.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			((UnityEvent)component.onClick).AddListener(UnityAction.op_Implicit(onClick));
			return obj;
		}

		private static TMP_Text MakeLabel(Transform parent, string text, float sizeMul)
		{
			GameObject val = Clone(parent, "Label", text);
			foreach (Component component2 in val.GetComponents<Component>())
			{
				if (!((Object)(object)component2 == (Object)null))
				{
					string name = ((MemberInfo)((Object)component2).GetIl2CppType()).Name;
					if (name == "ButtonNavHandler" || name == "UIButtonSFX")
					{
						Object.DestroyImmediate((Object)(object)component2);
					}
				}
			}
			Object.DestroyImmediate((Object)(object)val.GetComponent<Button>());
			TMP_Text component = val.GetComponent<TMP_Text>();
			component.fontSize *= sizeMul;
			((Graphic)component).raycastTarget = false;
			return component;
		}

		private static void Navigate(GameObject page)
		{
			//IL_0056: 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_0062: 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_009a: Expected O, but got Unknown
			List<Button> list = new List<Button>();
			for (int i = 0; i < page.transform.childCount; i++)
			{
				Button component = ((Component)page.transform.GetChild(i)).GetComponent<Button>();
				if ((Object)(object)component != (Object)null && ((Component)component).gameObject.activeSelf)
				{
					list.Add(component);
				}
			}
			for (int j = 0; j < list.Count; j++)
			{
				((Selectable)list[j]).navigation = new Navigation
				{
					mode = (Mode)4,
					selectOnUp = (Selectable)(object)list[(j - 1 + list.Count) % list.Count],
					selectOnDown = (Selectable)(object)list[(j + 1) % list.Count]
				};
			}
		}

		private static void Show(GameObject page)
		{
			GameObject val = current;
			section.SetActive((Object)(object)page == (Object)(object)section);
			pcHome.SetActive((Object)(object)page == (Object)(object)pcHome);
			pcJoin.SetActive((Object)(object)page == (Object)(object)pcJoin);
			current = page;
			if ((Object)(object)page == (Object)(object)pcJoin)
			{
				typed = "";
				RefreshCode();
			}
			if ((Object)(object)page == (Object)(object)pcHome)
			{
				hintLabel.text = Session.StatusLine();
			}
			GameObject val2 = null;
			if ((Object)(object)page == (Object)(object)section && (Object)(object)val != (Object)(object)section)
			{
				Transform obj = section.transform.Find("ProCon");
				val2 = ((obj != null) ? ((Component)obj).gameObject : null);
			}
			if ((Object)(object)val2 == (Object)null)
			{
				Button componentInChildren = page.GetComponentInChildren<Button>();
				val2 = ((componentInChildren != null) ? ((Component)componentInChildren).gameObject : null);
			}
			if ((Object)(object)val2 != (Object)null && (Object)(object)EventSystem.current != (Object)null)
			{
				EventSystem.current.SetSelectedGameObject(val2);
			}
			PlayEntrance();
		}

		private static void OnHost()
		{
			Transform obj = section.transform.Find("Local");
			Button val = ((obj != null) ? ((Component)obj).GetComponent<Button>() : null);
			Show(section);
			if ((Object)(object)val != (Object)null)
			{
				Session.Host(val);
			}
		}

		private static void OnConnect()
		{
			if (!JoinCode.IsValid(typed))
			{
				codeLabel.text = "<color=#ff7a9c>????????</color>";
				typed = "";
				return;
			}
			string joinCode = typed;
			Transform obj = section.transform.Find("Local");
			Button val = ((obj != null) ? ((Component)obj).GetComponent<Button>() : null);
			Show(section);
			if ((Object)(object)val != (Object)null)
			{
				Session.Join(joinCode, val);
			}
		}

		private static void ProConInput()
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Invalid comparison between Unknown and I4
			//IL_00bd: 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)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: 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_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Invalid comparison between Unknown and I4
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Invalid comparison between Unknown and I4
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			Keyboard val = Keyboard.current;
			if (val == null || !Application.isFocused)
			{
				return;
			}
			if (((ButtonControl)val.escapeKey).wasPressedThisFrame || (((ButtonControl)val.backspaceKey).wasPressedThisFrame && (Object)(object)current == (Object)(object)pcHome))
			{
				Show(((Object)(object)current == (Object)(object)pcJoin) ? pcHome : section);
			}
			else
			{
				if ((Object)(object)current != (Object)(object)pcJoin)
				{
					return;
				}
				bool flag = ((ButtonControl)val.leftCtrlKey).isPressed || ((ButtonControl)val.rightCtrlKey).isPressed;
				if (flag && ((ButtonControl)val.vKey).wasPressedThisFrame)
				{
					typed = JoinCode.Clean(Clipboard.Get());
				}
				else if (!flag)
				{
					for (Key val2 = (Key)15; (int)val2 <= 40; val2 = (Key)(val2 + 1))
					{
						if (((ButtonControl)val[val2]).wasPressedThisFrame)
						{
							typed += (char)(65 + (val2 - 15));
						}
					}
					for (Key val3 = (Key)41; (int)val3 <= 50; val3 = (Key)(val3 + 1))
					{
						if (((ButtonControl)val[val3]).wasPressedThisFrame)
						{
							typed += (((int)val3 == 50) ? '0' : ((char)(49 + (val3 - 41))));
						}
					}
					if (((ButtonControl)val.backspaceKey).wasPressedThisFrame && typed.Length > 0)
					{
						string text = typed;
						typed = text.Substring(0, text.Length - 1);
					}
					typed = JoinCode.Clean(typed);
				}
				if (typed.Length > 8)
				{
					typed = typed.Substring(0, 8);
				}
				RefreshCode();
			}
		}

		private static void RefreshCode()
		{
			caretTimer += Time.unscaledDeltaTime;
			string text = ((caretTimer % 1f < 0.5f) ? "<color=#ffc40d>_</color>" : "<color=#00000000>_</color>");
			string text2 = ((typed.Length < 8) ? (typed + text) : typed);
			if (!codeLabel.text.StartsWith("<color=#ff7a9c>") || typed.Length > 0)
			{
				codeLabel.text = text2;
			}
		}

		private static void CollectAnims()
		{
			anims.Clear();
			if ((Object)(object)current == (Object)null)
			{
				return;
			}
			int num = 0;
			for (int i = 0; i < current.transform.childCount; i++)
			{
				Transform child = current.transform.GetChild(i);
				if (((Component)child).gameObject.activeSelf)
				{
					anims.Add(new Anim
					{
						Button = ((Component)child).GetComponent<Button>(),
						Rt = ((Il2CppObjectBase)child).Cast<RectTransform>(),
						Text = ((Component)child).GetComponent<TMP_Text>(),
						Delay = (float)num++ * 0.05f,
						Seed = (float)i * 1.7f
					});
				}
			}
		}

		private static void PlayEntrance()
		{
			CollectAnims();
			foreach (Anim anim in anims)
			{
				anim.Enter = 0f;
				anim.Scale = 0.6f;
			}
		}

		private static void Animate()
		{
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			if (anims.Count == 0 || (Object)(object)anims[0].Rt == (Object)null)
			{
				CollectAnims();
			}
			float num = Mathf.Min(Time.unscaledDeltaTime, 0.05f);
			float unscaledTime = Time.unscaledTime;
			EventSystem val = EventSystem.current;
			GameObject val2 = (((Object)(object)val != (Object)null) ? val.currentSelectedGameObject : null);
			GameObject val3 = HoveredButton();
			if ((Object)(object)val3 != (Object)null && (Object)(object)val3 != (Object)(object)val2 && (Object)(object)val != (Object)null && !Notice.IsOpen)
			{
				val.SetSelectedGameObject(val3);
				val2 = val3;
			}
			foreach (Anim anim in anims)
			{
				if ((Object)(object)anim.Rt == (Object)null)
				{
					continue;
				}
				bool flag = (Object)(object)anim.Button != (Object)null && (Object)(object)((Component)anim.Button).gameObject == (Object)(object)val2;
				if (flag && WasPressed())
				{
					anim.Punch = 1f;
				}
				anim.Hover = Mathf.MoveTowards(anim.Hover, flag ? 1f : 0f, num * 6f);
				if (anim.Delay > 0f)
				{
					anim.Delay -= num;
					((Transform)anim.Rt).localScale = Vector3.zero;
					continue;
				}
				anim.Enter = Mathf.MoveTowards(anim.Enter, 1f, num * 3.5f);
				float num2 = Mathf.Lerp(1f, 1.28f, Ease(anim.Hover)) - anim.Punch * 0.18f;
				anim.Vel += (num2 - anim.Scale) * 220f * num - anim.Vel * 16f * num;
				anim.Scale += anim.Vel * num;
				anim.Punch = Mathf.MoveTowards(anim.Punch, 0f, num * 5f);
				float num3 = 1f + Mathf.Sin(unscaledTime * 4f + anim.Seed) * 0.025f * anim.Hover;
				float num4 = anim.Scale * num3 * Mathf.Lerp(0.6f, 1f, Ease(anim.Enter));
				((Transform)anim.Rt).localScale = new Vector3(num4, num4, 1f);
				float num5 = Mathf.Sin(unscaledTime * 6f + anim.Seed) * 3.5f * anim.Hover;
				float num6 = (1f - Ease(anim.Enter)) * -25f;
				((Transform)anim.Rt).localRotation = Quaternion.Euler(0f, 0f, num5 + num6);
				if ((Object)(object)anim.Text != (Object)null)
				{
					anim.Text.characterSpacing = (((Object)(object)anim.Text == (Object)(object)codeLabel) ? 14f : Mathf.Lerp(0f, 6f, Ease(anim.Hover)));
				}
			}
		}

		private static float Ease(float x)
		{
			return 1f - Mathf.Pow(1f - Mathf.Clamp01(x), 3f);
		}

		private static bool WasPressed()
		{
			if (!Application.isFocused)
			{
				return false;
			}
			Keyboard val = Keyboard.current;
			Mouse val2 = Mouse.current;
			Gamepad val3 = Gamepad.current;
			if ((val == null || (!((ButtonControl)val.enterKey).wasPressedThisFrame && !((ButtonControl)val.spaceKey).wasPressedThisFrame)) && (val2 == null || !val2.leftButton.wasPressedThisFrame))
			{
				if (val3 != null)
				{
					return val3.buttonSouth.wasPressedThisFrame;
				}
				return false;
			}
			return true;
		}

		private static GameObject HoveredButton()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: 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_00f6: 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_0105: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			Mouse val = Mouse.current;
			if (val == null || !Application.isFocused)
			{
				return null;
			}
			Vector2 val2 = ((InputControl<Vector2>)(object)((Pointer)val).position).ReadValue();
			Vector2 val3 = val2 - lastMouse;
			bool num = ((Vector2)(ref val3)).sqrMagnitude > 1f;
			lastMouse = val2;
			if (!num)
			{
				return null;
			}
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return null;
			}
			foreach (Anim anim in anims)
			{
				if (!((Object)(object)anim.Button == (Object)null) && !((Object)(object)anim.Text == (Object)null))
				{
					Bounds textBounds = anim.Text.textBounds;
					Vector2 val4 = Vector2.op_Implicit(main.WorldToScreenPoint(((Transform)anim.Rt).TransformPoint(((Bounds)(ref textBounds)).min - new Vector3(20f, 8f, 0f))));
					Vector2 val5 = Vector2.op_Implicit(main.WorldToScreenPoint(((Transform)anim.Rt).TransformPoint(((Bounds)(ref textBounds)).max + new Vector3(20f, 8f, 0f))));
					if (val2.x >= Mathf.Min(val4.x, val5.x) && val2.x <= Mathf.Max(val4.x, val5.x) && val2.y >= Mathf.Min(val4.y, val5.y) && val2.y <= Mathf.Max(val4.y, val5.y))
					{
						return ((Component)anim.Button).gameObject;
					}
				}
			}
			return null;
		}
	}
	internal static class MenuFont
	{
		private const string MenuRoot = "Managers/Menu/Main Menu";

		private static TMP_FontAsset font;

		private static bool loadFailed;

		private static float timer;

		private static readonly Dictionary<IntPtr, Material> materials = new Dictionary<IntPtr, Material>();

		private static readonly HashSet<IntPtr> logged = new HashSet<IntPtr>();

		private static Material outlined;

		public static TMP_FontAsset Font
		{
			get
			{
				if ((Object)(object)font == (Object)null && !loadFailed)
				{
					Load();
				}
				return font;
			}
		}

		public static Material OutlinedMaterial
		{
			get
			{
				//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_003f: Expected O, but got Unknown
				//IL_0080: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)outlined != (Object)null || (Object)(object)Font == (Object)null)
				{
					return outlined;
				}
				outlined = new Material(((TMP_Asset)font).material)
				{
					name = "Yellow Banana Outlined"
				};
				outlined.EnableKeyword("OUTLINE_ON");
				outlined.SetFloat("_OutlineWidth", 0.28f);
				outlined.SetColor("_OutlineColor", new Color(0.1f, 0.06f, 0.02f, 1f));
				outlined.EnableKeyword("UNDERLAY_ON");
				outlined.SetColor("_UnderlayColor", new Color(0f, 0f, 0f, 0.6f));
				outlined.SetFloat("_UnderlayOffsetX", 0.6f);
				outlined.SetFloat("_UnderlayOffsetY", -0.6f);
				Object.DontDestroyOnLoad((Object)(object)outlined);
				return outlined;
			}
		}

		private static void Load()
		{
			try
			{
				string text = Path.Combine(MelonEnvironment.UserDataDirectory, "ProCon");
				Directory.CreateDirectory(text);
				string text2 = Path.Combine(text, "YellowBanana.otf");
				using (Stream stream = typeof(MenuFont).Assembly.GetManifestResourceStream("YellowBanana.otf"))
				{
					using MemoryStream memoryStream = new MemoryStream();
					stream.CopyTo(memoryStream);
					byte[] array = memoryStream.ToArray();
					if (!File.Exists(text2) || new FileInfo(text2).Length != array.Length)
					{
						File.WriteAllBytes(text2, array);
					}
				}
				font = TMP_FontAsset.CreateFontAsset(text2, 0, 90, 9, (GlyphRenderMode)4165, 2048, 2048, (AtlasPopulationMode)1, false);
				if ((Object)(object)font == (Object)null)
				{
					throw new Exception("CreateFontAsset returned null");
				}
				((Object)font).name = "Yellow Banana SDF";
				Object.DontDestroyOnLoad((Object)(object)font);
				font.TryAddCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,:;!?'\"-+/()&%#", false);
				MelonLogger.Msg("Yellow Banana font loaded.");
			}
			catch (Exception ex)
			{
				loadFailed = true;
				MelonLogger.Error("Couldn't load Yellow Banana font: " + ex);
			}
		}

		public static void Tick()
		{
			//IL_0027: 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)
			timer -= Time.unscaledDeltaTime;
			if (timer > 0f)
			{
				return;
			}
			timer = 0.5f;
			Scene activeScene = SceneManager.GetActiveScene();
			if (!(((Scene)(ref activeScene)).name != "Menu"))
			{
				GameObject val = GameObject.Find("Managers/Menu/Main Menu");
				if (!((Object)(object)val == (Object)null) && !((Object)(object)Font == (Object)null))
				{
					Apply(val);
				}
			}
		}

		public static void Apply(GameObject root)
		{
			Il2CppArrayBase<TMP_Text> componentsInChildren = root.GetComponentsInChildren<TMP_Text>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				TMP_Text val = componentsInChildren[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.font == (Object)(object)font))
				{
					Material fontSharedMaterial = val.fontSharedMaterial;
					val.font = font;
					val.fontSharedMaterial = StyledMaterial(fontSharedMaterial);
					if (logged.Add(((Il2CppObjectBase)val).Pointer))
					{
						Util.Debug($"font -> '{val.text}' ({((Object)((Component)val).gameObject).name})");
					}
				}
			}
		}

		private static Material StyledMaterial(Material orig)
		{
			//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_0050: Expected O, but got Unknown
			if ((Object)(object)orig == (Object)null)
			{
				return ((TMP_Asset)font).material;
			}
			if (materials.TryGetValue(((Il2CppObjectBase)orig).Pointer, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = new Material(orig)
			{
				name = ((Object)orig).name + " (Yellow Banana)"
			};
			value.SetTexture("_MainTex", (Texture)(object)font.atlasTexture);
			value.SetFloat("_TextureWidth", (float)font.atlasWidth);
			value.SetFloat("_TextureHeight", (float)font.atlasHeight);
			value.SetFloat("_GradientScale", (float)(font.atlasPadding + 1));
			Object.DontDestroyOnLoad((Object)(object)value);
			materials[((Il2CppObjectBase)orig).Pointer] = value;
			return value;
		}
	}
	internal static class Notice
	{
		private const string Text = "ProCon is still a bit janky!\n\nIf your friend doesn't show up in the lobby, just start the round at the same time.";

		private static bool shown;

		private static GameObject root;

		private static RectTransform panel;

		private static GameObject okButton;

		private static GameObject previousSelection;

		private static float anim;

		private static float pressedAt = -1f;

		private static readonly Color Yellow = new Color(1f, 0.78f, 0.05f);

		private static readonly Color Pink = new Color(0.96f, 0.45f, 0.66f);

		private static readonly Color Ink = new Color(0.08f, 0.06f, 0.1f);

		public static bool IsOpen
		{
			get
			{
				if ((Object)(object)root != (Object)null)
				{
					return root.activeSelf;
				}
				return false;
			}
		}

		public static void Tick()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			if (!shown)
			{
				Scene activeScene = SceneManager.GetActiveScene();
				if (!(((Scene)(ref activeScene)).name != "Menu") && MainMenu.OnMainList && !((Object)(object)MenuFont.Font == (Object)null))
				{
					shown = true;
					Show();
				}
			}
			else if (!((Object)(object)root == (Object)null) && root.activeSelf)
			{
				float num = Mathf.Min(Time.unscaledDeltaTime, 0.05f);
				anim = Mathf.MoveTowards(anim, 1f, num * 4f);
				float num2 = 1f - Mathf.Pow(1f - anim, 3f);
				((Transform)panel).localScale = Vector3.one * Mathf.Lerp(0.7f, 1f, num2) * (1f + Mathf.Sin(anim * (float)Math.PI) * 0.05f);
				((Transform)panel).localRotation = Quaternion.Euler(0f, 0f, (1f - num2) * -6f);
				EventSystem current = EventSystem.current;
				if ((Object)(object)current != (Object)null && (Object)(object)current.currentSelectedGameObject != (Object)(object)okButton)
				{
					current.SetSelectedGameObject(okButton);
				}
				RectTransform component = okButton.GetComponent<RectTransform>();
				float num3 = ((pressedAt > 0f) ? Mathf.Clamp01(1f - (Time.unscaledTime - pressedAt) * 6f) : 0f);
				((Transform)component).localScale = Vector3.one * (1.06f + Mathf.Sin(Time.unscaledTime * 4f) * 0.03f - num3 * 0.12f);
				((Transform)component).localRotation = Quaternion.Euler(0f, 0f, Mathf.Sin(Time.unscaledTime * 6f) * 2f);
			}
		}

		private static void Close()
		{
			pressedAt = Time.unscaledTime;
			root.SetActive(false);
			EventSystem current = EventSystem.current;
			if ((Object)(object)current != (Object)null && (Object)(object)previousSelection != (Object)null)
			{
				current.SetSelectedGameObject(previousSelection);
			}
		}

		private static void Show()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_006f: 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)
			//IL_00bb: 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_00d6: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			previousSelection = (((Object)(object)EventSystem.current != (Object)null) ? EventSystem.current.currentSelectedGameObject : null);
			root = new GameObject("ProCon Notice");
			Object.DontDestroyOnLoad((Object)(object)root);
			Canvas obj = root.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 6000;
			CanvasScaler obj2 = root.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = new Vector2(1920f, 1080f);
			obj2.matchWidthOrHeight = 0.5f;
			root.AddComponent<GraphicRaycaster>();
			RectTransform component = Rect(root.transform, "Dim", Vector2.zero, Vector2.zero, new Color(0.05f, 0.02f, 0.08f, 0.55f)).GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			Vector2 offsetMin = (component.offsetMax = Vector2.zero);
			component.offsetMin = offsetMin;
			GameObject obj3 = Rect(root.transform, "Panel", Vector2.zero, new Vector2(840f, 600f), Ink);
			panel = obj3.GetComponent<RectTransform>();
			Outline obj4 = obj3.AddComponent<Outline>();
			((Shadow)obj4).effectColor = Pink;
			((Shadow)obj4).effectDistance = new Vector2(8f, -8f);
			Label(obj3.transform, "Heads up!", 80f, Yellow, new Vector2(0f, 210f), new Vector2(760f, 120f));
			TMP_Text obj5 = Label(obj3.transform, "ProCon is still a bit janky!\n\nIf your friend doesn't show up in the lobby, just start the round at the same time.", 36f, Color.white, new Vector2(0f, 30f), new Vector2(720f, 230f));
			obj5.enableWordWrapping = true;
			obj5.alignment = (TextAlignmentOptions)514;
			obj5.overflowMode = (TextOverflowModes)0;
			okButton = Rect(obj3.transform, "OK", new Vector2(0f, -205f), new Vector2(260f, 92f), Yellow);
			Shadow obj6 = okButton.AddComponent<Shadow>();
			obj6.effectColor = new Color(0f, 0f, 0f, 0.6f);
			obj6.effectDistance = new Vector2(0f, -6f);
			Button obj7 = okButton.AddComponent<Button>();
			((Selectable)obj7).targetGraphic = (Graphic)(object)okButton.GetComponent<Image>();
			((UnityEvent)obj7.onClick).AddListener(UnityAction.op_Implicit((Action)Close));
			Label(okButton.transform, "OK", 60f, Ink, Vector2.zero, new Vector2(260f, 92f));
			anim = 0f;
		}

		private static GameObject Rect(Transform parent, string name, Vector2 pos, Vector2 size, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_005c: Expected O, but got Unknown
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			obj.pivot = val2;
			Vector2 anchorMin = (obj.anchorMax = val2);
			obj.anchorMin = anchorMin;
			obj.anchoredPosition = pos;
			obj.sizeDelta = size;
			((Graphic)val.AddComponent<Image>()).color = color;
			return val;
		}

		private static TMP_Text Label(Transform parent, string text, float size, Color color, Vector2 pos, Vector2 box)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0044: 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)
			GameObject val = new GameObject("Text");
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			obj.anchorMax = val2;
			obj.anchorMin = val2;
			obj.anchoredPosition = pos;
			obj.sizeDelta = box;
			TextMeshProUGUI obj2 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj2).font = MenuFont.Font;
			((TMP_Text)obj2).fontSharedMaterial = MenuFont.OutlinedMaterial;
			((TMP_Text)obj2).text = text;
			((TMP_Text)obj2).fontSize = size;
			((Graphic)obj2).color = color;
			((TMP_Text)obj2).alignment = (TextAlignmentOptions)514;
			((TMP_Text)obj2).enableWordWrapping = false;
			((Graphic)obj2).raycastTarget = false;
			return (TMP_Text)(object)obj2;
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "LaunchHost")]
	internal static class LaunchHostToClient
	{
		[HarmonyPriority(800)]
		private static bool Prefix(NetworkManager __instance)
		{
			return !Session.InterceptLaunchHost(__instance);
		}
	}
	[HarmonyPatch(typeof(LoadScreenSystem), "HideLoadingScreen")]
	internal static class NoticeLoadScreenHidden
	{
		private static void Prefix()
		{
			Session.OnHideLoadingScreen();
		}
	}
	public class ProConMod : MelonMod
	{
		public override void OnInitializeMelon()
		{
			((MelonBase)this).LoggerInstance.Msg("ProCon loaded. Main menu -> ProCon to host or join.");
			ConsoleFix.DisableQuickEdit();
		}

		public override void OnUpdate()
		{
			try
			{
				MenuFont.Tick();
				Session.Tick();
				Badge.Tick();
				Notice.Tick();
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("Update: " + ex);
			}
		}

		public override void OnLateUpdate()
		{
			try
			{
				MainMenu.Tick();
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("MainMenu: " + ex);
			}
		}
	}
	internal static class Relay
	{
		private const int Data = 0;

		private const int Control = 1;

		private const byte Hello = 1;

		private const byte Welcome = 2;

		private const byte WrongCode = 3;

		private const byte Bye = 4;

		private const byte NotYet = 5;

		public static volatile bool HostReady;

		public static volatile bool Welcomed;

		private static Thread thread;

		private static volatile bool running;

		private static readonly byte[] buf = new byte[65536];

		private static bool hosting;

		private static byte session;

		private static int gamePort;

		private static readonly Dictionary<ulong, UdpClient> peers = new Dictionary<ulong, UdpClient>();

		private static readonly object peersLock = new object();

		private static ulong hostId;

		private static UdpClient local;

		private static IPEndPoint gameEndpoint;

		public static volatile string ClientStatus = "";

		private static readonly Dictionary<ulong, long> waiting = new Dictionary<ulong, long>();

		private static readonly object waitingLock = new object();

		public static int PeerCount
		{
			get
			{
				lock (peersLock)
				{
					return peers.Count;
				}
			}
		}

		public static int LocalPort { get; private set; }

		public static void StartHost(byte sessionByte, int port)
		{
			Stop();
			hosting = true;
			session = sessionByte;
			gamePort = port;
			Steam();
			SteamNet.SessionRequested += AcceptAnyone;
			Begin(HostLoop);
			MelonLogger.Msg($"Relay hosting (session {session}) -> 127.0.0.1:{gamePort}");
		}

		public static void StartClient(ulong host, byte sessionByte)
		{
			Stop();
			hosting = false;
			hostId = host;
			session = sessionByte;
			Welcomed = false;
			Steam();
			SteamNet.SessionRequested += AcceptHost;
			local = Udp();
			LocalPort = ((IPEndPoint)local.Client.LocalEndPoint).Port;
			ClientStatus = "Connecting through Steam...";
			Begin(ClientLoop);
			MelonLogger.Msg($"Relay client -> {host}, local port {LocalPort}");
		}

		public static void Stop()
		{
			if (!running && thread == null)
			{
				return;
			}
			running = false;
			thread?.Join(500);
			thread = null;
			SteamNet.SessionRequested -= AcceptAnyone;
			SteamNet.SessionRequested -= AcceptHost;
			lock (peersLock)
			{
				foreach (KeyValuePair<ulong, UdpClient> peer in peers)
				{
					try
					{
						peer.Value.Close();
					}
					catch
					{
					}
					SteamNet.Close(peer.Key);
				}
				peers.Clear();
			}
			lock (waitingLock)
			{
				waiting.Clear();
			}
			if (local != null)
			{
				try
				{
					SteamNet.SendReliable(hostId, new byte[1] { 4 }, 1);
				}
				catch
				{
				}
				SteamNet.Close(hostId);
				local.Close();
				local = null;
			}
			gameEndpoint = null;
			MelonLogger.Msg("Relay stopped.");
		}

		private static void Steam()
		{
			SteamNet.AllowRelay();
			SteamNet.RegisterSessionRequest();
		}

		private static void AcceptAnyone(ulong id)
		{
			if (hosting)
			{
				SteamNet.Accept(id);
			}
		}

		private static void AcceptHost(ulong id)
		{
			if (id == hostId)
			{
				SteamNet.Accept(id);
			}
		}

		private static void Begin(ThreadStart loop)
		{
			running = true;
			thread = new Thread(loop)
			{
				IsBackground = true,
				Name = "ProCon relay"
			};
			thread.Start();
		}

		private static UdpClient Udp()
		{
			UdpClient udpClient = new UdpClient(new IPEndPoint(IPAddress.Loopback, 0));
			try
			{
				udpClient.Client.IOControl(-1744830452, new byte[1], null);
			}
			catch
			{
			}
			return udpClient;
		}

		private static void HostLoop()
		{
			while (running)
			{
				bool flag = false;
				try
				{
					ulong from;
					int num;
					while ((num = SteamNet.Read(buf, 1, out from)) > 0)
					{
						flag = true;
						if (buf[0] == 1 && num >= 2)
						{
							OnHello(from, buf[1]);
						}
						else if (buf[0] == 4)
						{
							DropPeer(from);
						}
						else if (buf[0] == 6)
						{
							LobbySync.Incoming.Enqueue((from, Copy(buf, num)));
						}
					}
					ulong from2;
					while ((num = SteamNet.Read(buf, 0, out from2)) > 0)
					{
						flag = true;
						UdpClient value;
						lock (peersLock)
						{
							peers.TryGetValue(from2, out value);
						}
						value?.Send(buf, num);
					}
					lock (peersLock)
					{
						foreach (KeyValuePair<ulong, UdpClient> peer in peers)
						{
							while (peer.Value.Available > 0)
							{
								IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
								byte[] array = peer.Value.Receive(ref remoteEP);
								SteamNet.Send(peer.Key, array, array.Length, 0);
								flag = true;
							}
						}
					}
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Relay host: " + ex.Message);
				}
				if (!flag)
				{
					Thread.Sleep(1);
				}
			}
		}

		private static void OnHello(ulong from, byte theirSession)
		{
			if (theirSession != session)
			{
				SteamNet.SendReliable(from, new byte[1] { 3 }, 1);
				return;
			}
			if (!HostReady)
			{
				lock (waitingLock)
				{
					waiting[from] = Environment.TickCount64;
				}
				SteamNet.SendReliable(from, new byte[1] { 5 }, 1);
				return;
			}
			lock (waitingLock)
			{
				waiting.Remove(from);
			}
			lock (peersLock)
			{
				if (!peers.ContainsKey(from))
				{
					UdpClient udpClient = Udp();
					udpClient.Connect(IPAddress.Loopback, gamePort);
					peers[from] = udpClient;
					MelonLogger.Msg($"Relay: friend {from} connected ({peers.Count} total)");
				}
			}
			SteamNet.SendReliable(from, new byte[1] { 2 }, 1);
		}

		private static byte[] Copy(byte[] src, int n)
		{
			byte[] array = new byte[n];
			Array.Copy(src, array, n);
			return array;
		}

		public static List<ulong> FriendIds()
		{
			HashSet<ulong> hashSet = new HashSet<ulong>(WaitingFriends());
			lock (peersLock)
			{
				foreach (ulong key in peers.Keys)
				{
					hashSet.Add(key);
				}
			}
			return new List<ulong>(hashSet);
		}

		public static void SendLobby(ulong to, byte[] data)
		{
			if (running)
			{
				ulong num = ((to != 0L) ? to : hostId);
				if (num != 0L)
				{
					SteamNet.SendReliable(num, data, 1);
				}
			}
		}

		public static List<ulong> WaitingFriends()
		{
			List<ulong> list = new List<ulong>();
			long tickCount = Environment.TickCount64;
			lock (waitingLock)
			{
				foreach (KeyValuePair<ulong, long> item in waiting)
				{
					if (tickCount - item.Value < 3000)
					{
						list.Add(item.Key);
					}
				}
				return list;
			}
		}

		private static void DropPeer(ulong from)
		{
			lock (peersLock)
			{
				if (peers.TryGetValue(from, out var value))
				{
					value.Close();
					peers.Remove(from);
				}
			}
			lock (waitingLock)
			{
				waiting.Remove(from);
			}
			SteamNet.Close(from);
			LobbySync.Incoming.Enqueue((from, new byte[0]));
			MelonLogger.Msg($"Relay: friend {from} left");
		}

		private static void ClientLoop()
		{
			Stopwatch stopwatch = Stopwatch.StartNew();
			bool flag = false;
			long num = -1000L;
			while (running)
			{
				bool flag2 = false;
				try
				{
					if (!flag && stopwatch.ElapsedMilliseconds - num > 500)
					{
						num = stopwatch.ElapsedMilliseconds;
						SteamNet.SendReliable(hostId, new byte[2] { 1, session }, 1);
						if (stopwatch.ElapsedMilliseconds > 15000)
						{
							ClientStatus = "Can't reach the host. Is the code right and are they hosting?";
						}
					}
					ulong from;
					int n;
					while ((n = SteamNet.Read(buf, 1, out from)) > 0)
					{
						if (from == hostId)
						{
							if (buf[0] == 2 && !flag)
							{
								flag = true;
								Welcomed = true;
								ClientStatus = "";
								MelonLogger.Msg("Relay: host said welcome.");
							}
							else if (buf[0] == 5)
							{
								ClientStatus = "Connected! Waiting for the host to start the round";
								stopwatch.Restart();
								num = 0L;
							}
							else if (buf[0] == 3)
							{
								ClientStatus = "That code is old. Ask your friend for the new one.";
							}
							else if (buf[0] == 4)
							{
								ClientStatus = "Host closed the game.";
							}
							else if (buf[0] == 6)
							{
								LobbySync.Incoming.Enqueue((from, Copy(buf, n)));
							}
						}
					}
					ulong from2;
					while ((n = SteamNet.Read(buf, 0, out from2)) > 0)
					{
						flag2 = true;
						if (from2 == hostId && gameEndpoint != null)
						{
							local.Send(buf, n, gameEndpoint);
						}
					}
					while (local.Available > 0)
					{
						IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
						byte[] array = local.Receive(ref remoteEP);
						gameEndpoint = remoteEP;
						SteamNet.Send(hostId, array, array.Length, 0);
						flag2 = true;
					}
				}
				catch (Exception ex)
				{
					if (running)
					{
						MelonLogger.Warning("Relay client: " + ex.Message);
					}
				}
				if (!flag2)
				{
					Thread.Sleep(1);
				}
			}
		}
	}
	internal static class Session
	{
		private enum Mode
		{
			None,
			Hosting,
			Joining,
			Waiting,
			Joined
		}

		public const int Port = 5999;

		private static Mode mode;

		private static string code = "";

		private static bool sawActive;

		private static float startedAt;

		private static ulong pendingHost;

		private static byte pendingSession;

		private static NetworkManager waitingNet;

		private static bool sameMachine;

		private static float checkTimer;

		private static float inputFixTimer;

		private static readonly HashSet<IntPtr> fixedDrivers = new HashSet<IntPtr>();

		private static float loadedFor;

		private static string loadedScene = "";

		private static bool hidingOurselves;

		public static bool GameHidLoadScreen;

		private static float joinCheck;

		private static string lastBeastLog = "";

		public static bool IsHosting => mode == Mode.Hosting;

		public static void Host(Button localButton)
		{
			byte b = (byte)new Random().Next(256);
			code = JoinCode.Encode(SteamNet.MyAccountId, b);
			mode = Mode.Hosting;
			sawActive = false;
			startedAt = Time.unscaledTime;
			Clipboard.Set(code);
			Relay.StartHost(b, 5999);
			MelonLogger.Msg("Hosting with code " + code);
			((UnityEvent)localButton.onClick).Invoke();
		}

		public static void Join(string joinCode, Button localButton)
		{
			if (JoinCode.TryDecode(joinCode, out var account, out var session))
			{
				code = JoinCode.Clean(joinCode);
				mode = Mode.Joining;
				pendingHost = 76561197960265728uL + (ulong)account;
				pendingSession = session;
				sawActive = false;
				startedAt = Time.unscaledTime;
				sameMachine = pendingHost == SteamNet.MySteamId;
				if (!sameMachine)
				{
					Relay.StartClient(pendingHost, pendingSession);
				}
				MelonLogger.Msg($"Join {code} -> Steam {pendingHost}. Waiting for beast select to finish.");
				((UnityEvent)localButton.onClick).Invoke();
			}
		}

		public static bool InterceptLaunchHost(NetworkManager net)
		{
			if (mode == Mode.Waiting)
			{
				return true;
			}
			if (mode != Mode.Joining || pendingHost == 0L)
			{
				return false;
			}
			waitingNet = net;
			mode = Mode.Waiting;
			MelonLogger.Msg($"Beasts ready ({NetMemberContext.PlayerCount}). Waiting for the host's round{(sameMachine ? " (same PC)" : "")}.");
			return true;
		}

		public static void Tick()
		{
			LobbySync.Active = mode == Mode.Hosting || ((mode == Mode.Joining || mode == Mode.Waiting) && !sameMachine);
			LobbySync.Tick(mode == Mode.Hosting);
			if (mode == Mode.Hosting)
			{
				bool flag = false;
				try
				{
					flag = NetworkServer.active;
				}
				catch
				{
				}
				Relay.HostReady = flag;
				if (flag)
				{
					FixRemoteInput();
				}
			}
			if (mode == Mode.Joining)
			{
				AutoStartJoin();
			}
			else if (mode == Mode.Joined)
			{
				LoadScreenWatchdog();
			}
			else
			{
				if (mode != Mode.Waiting)
				{
					return;
				}
				checkTimer -= Time.unscaledDeltaTime;
				if (!(checkTimer > 0f))
				{
					checkTimer = 0.5f;
					if (sameMachine ? LocalServerUp() : Relay.Welcomed)
					{
						mode = Mode.Joined;
						pendingHost = 0uL;
						NetMemberContext.LocalHostedGame = false;
						int num = (sameMachine ? 5999 : Relay.LocalPort);
						MelonLogger.Msg($"Host is up. Connecting to 127.0.0.1:{num} with {NetMemberContext.PlayerCount} beast(s).");
						waitingNet.LaunchClient("127.0.0.1", num);
					}
				}
			}
		}

		private static void FixRemoteInput()
		{
			inputFixTimer -= Time.unscaledDeltaTime;
			if (inputFixTimer > 0f)
			{
				return;
			}
			inputFixTimer = 0.5f;
			foreach (Actor item in Object.FindObjectsOfType<Actor>())
			{
				if ((Object)(object)item == (Object)null || item.IsAI)
				{
					continue;
				}
				bool flag;
				try
				{
					NetworkConnection connectionToClient = ((NetworkBehaviour)item).connectionToClient;
					flag = connectionToClient != null && connectionToClient.hostId != -1;
				}
				catch
				{
					continue;
				}
				if (!flag)
				{
					continue;
				}
				foreach (InputDriver componentsInChild in ((Component)item).GetComponentsInChildren<InputDriver>(true))
				{
					if (!((Object)(object)componentsInChild == (Object)null) && fixedDrivers.Add(((Il2CppObjectBase)componentsInChild).Pointer))
					{
						try
						{
							componentsInChild.DisconnectController();
						}
						catch
						{
						}
						((Behaviour)componentsInChild).enabled = false;
						MelonLogger.Msg($"Remote beast '{((Object)item).name}' (player {item.playerID}): stopped reading this PC's controller.");
					}
				}
			}
		}

		public static void OnHideLoadingScreen()
		{
			if (!hidingOurselves && mode == Mode.Joined)
			{
				GameHidLoadScreen = true;
			}
		}

		private static void LoadScreenWatchdog()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			string name = ((Scene)(ref activeScene)).name;
			if (name == "Menu" || name == "_bootScene")
			{
				loadedFor = 0f;
				return;
			}
			if (name != loadedScene)
			{
				loadedScene = name;
				loadedFor = 0f;
				GameHidLoadScreen = false;
			}
			if (GameHidLoadScreen)
			{
				return;
			}
			NetClientCatchUp val = Object.FindObjectOfType<NetClientCatchUp>();
			if ((Object)(object)val != (Object)null && val.CatchingUp)
			{
				loadedFor = 0f;
				return;
			}
			loadedFor += Time.unscaledDeltaTime;
			if (loadedFor < 6f)
			{
				return;
			}
			LoadScreenSystem val2 = Object.FindObjectOfType<LoadScreenSystem>();
			if (!((Object)(object)val2 == (Object)null) && val2.LoadScreenActive && !val2.HideFadeActive)
			{
				MelonLogger.Msg($"Loading screen still up {loadedFor:0.0}s after '{name}' loaded - hiding it.");
				hidingOurselves = true;
				try
				{
					val2.HideLoadingScreen((Action)null, -1f, true);
				}
				finally
				{
					hidingOurselves = false;
				}
				loadedFor = -10f;
			}
		}

		private static void AutoStartJoin()
		{
			joinCheck -= Time.unscaledDeltaTime;
			if (joinCheck > 0f)
			{
				return;
			}
			joinCheck = 0.25f;
			LobbyManager instance = LobbyManager.Instance;
			LocalBeastSetupTracker val = ((instance != null) ? instance.LocalBeasts : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = val.TotalInState((PlayerState)2);
			int num2 = val.TotalInState((PlayerState)1);
			string text = $"ready={num} designing={num2}";
			if (text != lastBeastLog)
			{
				lastBeastLog = text;
				MelonLogger.Msg("Join beast select: " + text);
			}
			if (num != 0 && num2 <= 0)
			{
				try
				{
					int value = val.SetupNetMemberContext(false);
					MelonLogger.Msg($"SetupNetMemberContext -> {value}");
				}
				catch (Exception ex)
				{
					MelonLogger.Error("SetupNetMemberContext failed: " + ex.Message);
					return;
				}
				NetworkManager singleton = NetworkManager.singleton;
				NetworkManager val2 = ((singleton != null) ? ((Il2CppObjectBase)singleton).TryCast<NetworkManager>() : null);
				if ((Object)(object)val2 != (Object)null)
				{
					InterceptLaunchHost(val2);
				}
			}
		}

		private static bool LocalServerUp()
		{
			try
			{
				IPEndPoint[] activeUdpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners();
				for (int i = 0; i < activeUdpListeners.Length; i++)
				{
					if (activeUdpListeners[i].Port == 5999)
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		public static string StatusLine()
		{
			return "Friends join with your code from anywhere";
		}

		public static string BadgeLine()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (mode == Mode.None)
			{
				return null;
			}
			bool flag = false;
			try
			{
				flag = (Object)(object)NetworkManager.singleton != (Object)null && NetworkManager.singleton.isNetworkActive;
			}
			catch
			{
			}
			if (flag)
			{
				sawActive = true;
			}
			else
			{
				if (sawActive)
				{
					End();
					return null;
				}
				if (mode == Mode.Hosting || mode == Mode.Joining)
				{
					Scene activeScene = SceneManager.GetActiveScene();
					if (((Scene)(ref activeScene)).name == "Menu" && MainMenu.OnMainList && Time.unscaledTime - startedAt > 1f)
					{
						End();
						return null;
					}
				}
			}
			string clientStatus = Relay.ClientStatus;
			return mode switch
			{
				Mode.Hosting => "Hosting  <color=#ffffff>" + code + "</color>  <size=70%>(copied)</size>" + ((!Relay.HostReady) ? sub("Start a round so friends can join" + WaitingLine()) : ((Relay.PeerCount > 0) ? sub($"{Relay.PeerCount} friend(s) connected") : "")), 
				Mode.Joining => "Joining  <color=#ffffff>" + code + "</color>" + sub("Join your beast and ready up - you'll connect automatically" + ((clientStatus.Length > 0) ? ("\n" + clientStatus) : "")), 
				Mode.Waiting => "Joining  <color=#ffffff>" + code + "</color>" + sub((clientStatus.Length > 0) ? clientStatus : "Waiting for the host to start a round..."), 
				_ => "Joined  <color=#ffffff>" + code + "</color>", 
			};
			static string sub(string s)
			{
				return "\n<size=60%>" + s + "</size>";
			}
		}

		private static string WaitingLine()
		{
			List<ulong> list = Relay.WaitingFriends();
			if (list.Count == 0)
			{
				return "";
			}
			List<string> list2 = new List<string>();
			foreach (ulong item in list)
			{
				list2.Add(SteamNet.Name(item));
			}
			return "\n<color=#ffffff>Waiting to join: " + string.Join(", ", list2) + "</color>";
		}

		private static void End()
		{
			mode = Mode.None;
			pendingHost = 0uL;
			waitingNet = null;
			Relay.HostReady = false;
			LobbySync.Active = false;
			LobbySync.Clear();
			Relay.Stop();
		}
	}
	internal static class JoinCode
	{
		public const int Length = 8;

		private const string Alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";

		private const ulong Mask = 711950999163uL;

		public static string Encode(uint account, byte session)
		{
			ulong num = (((ulong)account << 8) | session) ^ 0xA5C3961E7BL ^ (ulong)((session * 4311810305L) & 0xFFFFFFFF00L);
			char[] array = new char[8];
			for (int num2 = 7; num2 >= 0; num2--)
			{
				array[num2] = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"[(int)(num % 32)];
				num /= 32;
			}
			return new string(array);
		}

		public static bool TryDecode(string code, out uint account, out byte session)
		{
			account = 0u;
			session = 0;
			code = Clean(code);
			if (code.Length != 8)
			{
				return false;
			}
			ulong num = 0uL;
			string text = code;
			foreach (char value in text)
			{
				num = num * 32 + (ulong)"ABCDEFGHJKLMNPQRSTUVWXYZ23456789".IndexOf(value);
			}
			num ^= 0xA5C3961E7BL;
			session = (byte)num;
			num ^= (ulong)((session * 4311810305L) & 0xFFFFFFFF00L);
			account = (uint)(num >> 8);
			return account != 0;
		}

		public static bool IsValid(string code)
		{
			uint account;
			byte session;
			return TryDecode(code, out account, out session);
		}

		public static string Clean(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			string text = s.ToUpperInvariant();
			foreach (char value in text)
			{
				if ("ABCDEFGHJKLMNPQRSTUVWXYZ23456789".IndexOf(value) >= 0)
				{
					stringBuilder.Append(value);
				}
			}
			return stringBuilder.ToString();
		}
	}
	internal static class SteamNet
	{
		[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
		private delegate void RunCb(IntPtr self, IntPtr param);

		[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
		private delegate void RunCr(IntPtr self, IntPtr param, [MarshalAs(UnmanagedType.I1)] bool ioFailure, ulong call);

		[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
		private delegate int SizeCb(IntPtr self);

		private const string Lib = "steam_api64";

		private const int TypeUnreliable = 0;

		private const int TypeReliable = 2;

		private const int MaxUnreliable = 1200;

		private static IntPtr net;

		public const ulong IndividualBase = 76561197960265728uL;

		private const int P2PSessionRequestId = 1202;

		private static RunCb runCb;

		private static RunCr runCr;

		private static SizeCb sizeCb;

		private static IntPtr callbackObj;

		private static IntPtr vtable;

		private static IntPtr Net
		{
			get
			{
				if (!(net != IntPtr.Zero))
				{
					return net = SteamAPI_SteamNetworking_v006();
				}
				return net;
			}
		}

		public static ulong MySteamId => SteamAPI_ISteamUser_GetSteamID(SteamAPI_SteamUser_v023());

		public static uint MyAccountId => (uint)(MySteamId - 76561197960265728L);

		public static event Action<ulong> SessionRequested;

		[DllImport("steam_api64")]
		private static extern IntPtr SteamAPI_SteamUser_v023();

		[DllImport("steam_api64")]
		private static extern IntPtr SteamAPI_SteamNetworking_v006();

		[DllImport("steam_api64")]
		private static extern ulong SteamAPI_ISteamUser_GetSteamID(IntPtr self);

		[DllImport("steam_api64")]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern bool SteamAPI_ISteamNetworking_SendP2PPacket(IntPtr self, ulong remote, byte[] data, uint size, int sendType, int channel);

		[DllImport("steam_api64")]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern bool SteamAPI_ISteamNetworking_IsP2PPacketAvailable(IntPtr self, out uint size, int channel);

		[DllImport("steam_api64")]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern bool SteamAPI_ISteamNetworking_ReadP2PPacket(IntPtr self, byte[] dest, uint destSize, out uint size, out ulong remote, int channel);

		[DllImport("steam_api64")]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern bool SteamAPI_ISteamNetworking_AcceptP2PSessionWithUser(IntPtr self, ulong remote);

		[DllImport("steam_api64")]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern bool SteamAPI_ISteamNetworking_CloseP2PSessionWithUser(IntPtr self, ulong remote);

		[DllImport("steam_api64")]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern bool SteamAPI_ISteamNetworking_AllowP2PPacketRelay(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool allow);

		[DllImport("steam_api64")]
		private static extern void SteamAPI_RegisterCallback(IntPtr callback, int id);

		[DllImport("steam_api64")]
		private static extern IntPtr SteamAPI_SteamFriends_v017();

		[DllImport("steam_api64")]
		private static extern IntPtr SteamAPI_ISteamFriends_GetFriendPersonaName(IntPtr self, ulong user);

		[DllImport("steam_api64")]
		[return: MarshalAs(UnmanagedType.I1)]
		private static extern bool SteamAPI_ISteamFriends_RequestUserInformation(IntPtr self, ulong user, [MarshalAs(UnmanagedType.I1)] bool nameOnly);

		public static string Name(ulong user)
		{
			try
			{
				IntPtr self = SteamAPI_SteamFriends_v017();
				string text = Marshal.PtrToStringUTF8(SteamAPI_ISteamFriends_GetFriendPersonaName(self, user));
				if (string.IsNullOrEmpty(text) || text == "[unknown]")
				{
					SteamAPI_ISteamFriends_RequestUserInformation(self, user, nameOnly: true);
					return "a friend";
				}
				return text;
			}
			catch
			{
				return "a friend";
			}
		}

		public static void AllowRelay()
		{
			SteamAPI_ISteamNetworking_AllowP2PPacketRelay(Net, allow: true);
		}

		public static bool Send(ulong to, byte[] data, int size, int channel)
		{
			return SteamAPI_ISteamNetworking_SendP2PPacket(Net, to, data, (uint)size, (size > 1200) ? 2 : 0, channel);
		}

		public static bool SendReliable(ulong to, byte[] data, int channel)
		{
			return SteamAPI_ISteamNetworking_SendP2PPacket(Net, to, data, (uint)data.Length, 2, channel);
		}

		public static int Read(byte[] buf, int channel, out ulong from)
		{
			from = 0uL;
			if (!SteamAPI_ISteamNetworking_IsP2PPacketAvailable(Net, out var size, channel) || size == 0)
			{
				return -1;
			}
			if (size > buf.Length)
			{
				size = (uint)buf.Length;
			}
			if (!SteamAPI_ISteamNetworking_ReadP2PPacket(Net, buf, size, out var size2, out from, channel))
			{
				return -1;
			}
			return (int)size2;
		}

		public static void Accept(ulong user)
		{
			SteamAPI_ISteamNetworking_AcceptP2PSessionWithUser(Net, user);
		}

		public static void Close(ulong user)
		{
			SteamAPI_ISteamNetworking_CloseP2PSessionWithUser(Net, user);
		}

		public static void RegisterSessionRequest()
		{
			if (callbackObj != IntPtr.Zero)
			{
				return;
			}
			runCb = delegate(IntPtr self, IntPtr param)
			{
				try
				{
					SteamNet.SessionRequested?.Invoke((ulong)Marshal.ReadInt64(param));
				}
				catch (Exception ex)
				{
					MelonLogger.Error("P2P request: " + ex);
				}
			};
			runCr = delegate(IntPtr self, IntPtr param, bool fail, ulong call)
			{
				runCb(self, param);
			};
			sizeCb = (IntPtr self) => 8;
			vtable = Marshal.AllocHGlobal(IntPtr.Size * 3);
			Marshal.WriteIntPtr(vtable, 0, Marshal.GetFunctionPointerForDelegate(runCr));
			Marshal.WriteIntPtr(vtable, IntPtr.Size, Marshal.GetFunctionPointerForDelegate(runCb));
			Marshal.WriteIntPtr(vtable, IntPtr.Size * 2, Marshal.GetFunctionPointerForDelegate(sizeCb));
			callbackObj = Marshal.AllocHGlobal(16);
			Marshal.WriteIntPtr(callbackObj, 0, vtable);
			Marshal.WriteByte(callbackObj, 8, 0);
			Marshal.WriteInt32(callbackObj, 12, 1202);
			SteamAPI_RegisterCallback(callbackObj, 1202);
		}
	}
	internal static class Util
	{
		public static GameObject FindAny(string path)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.Find(path);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			foreach (Transform item in Resources.FindObjectsOfTypeAll<Transform>())
			{
				if ((Object)(object)item != (Object)null)
				{
					Scene scene = ((Component)item).gameObject.scene;
					if (((Scene)(ref scene)).IsValid() && Path(item) == path)
					{
						return ((Component)item).gameObject;
					}
				}
			}
			return null;
		}

		public static string Path(Transform t)
		{
			string text = ((Object)t).name;
			t = t.parent;
			while ((Object)(object)t != (Object)null)
			{
				text = ((Object)t).name + "/" + text;
				t = t.parent;
			}
			return text;
		}

		public static void Debug(string s)
		{
		}
	}
}