Decompiled source of MapVotePlusUiFix v1.0.1

MapVotePlusUiFix.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using MenuLib;
using MenuLib.MonoBehaviors;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MapVotePlusUiFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+3a03152ef87b5ef11afdc4cc063e1ee0f99bc164")]
[assembly: AssemblyProduct("MapVotePlusUiFix")]
[assembly: AssemblyTitle("MapVotePlusUiFix")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[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 MapVotePlusUiFix
{
	[BepInPlugin("themorningstar.mapvoteplusuifix", "MapVotePlus UI Fix", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "themorningstar.mapvoteplusuifix";

		internal static ManualLogSource Log = null;

		internal static readonly Type? MapVoteType = AccessTools.TypeByName("MapVotePlus.MapVotePlus");

		internal static readonly FieldInfo? VotePopupField = AccessTools.Field(MapVoteType, "VotePopup");

		internal static readonly FieldInfo? VoteLevelsField = AccessTools.Field(MapVoteType, "CurrentVoteLevels");

		internal static readonly FieldInfo? HideInMenuField = AccessTools.Field(MapVoteType, "HideInMenu");

		internal static readonly FieldInfo? CurrentMenuPageField = AccessTools.Field(typeof(MenuManager), "currentMenuPageIndex");

		internal static readonly FieldInfo? CurrentMenuStateField = AccessTools.Field(typeof(MenuManager), "currentMenuState");

		internal static readonly FieldInfo? CurrentMenuPageObjectField = AccessTools.Field(typeof(MenuManager), "currentMenuPage");

		internal static readonly FieldInfo? PageUnderThisPageField = AccessTools.Field(typeof(MenuPage), "pageUnderThisPage");

		internal static readonly MethodInfo? CreateVotePopupMethod = AccessTools.Method(MapVoteType, "CreateVotePopup", (Type[])null, (Type[])null);

		internal static readonly FieldInfo? DisableInputField = AccessTools.Field(MapVoteType, "DisableInput");

		internal static readonly FieldInfo? PageStateField = AccessTools.Field(typeof(MenuPage), "currentPageState");

		internal static readonly FieldInfo? CurrentVotesField = AccessTools.Field(MapVoteType, "CurrentVotes");

		internal static readonly FieldInfo? VoteOptionButtonsField = AccessTools.Field(MapVoteType, "VoteOptionButtons");

		internal static readonly FieldInfo? OwnVoteLevelField = AccessTools.Field(MapVoteType, "OwnVoteLevel");

		internal static readonly FieldInfo? WonMapField = AccessTools.Field(MapVoteType, "WonMap");

		internal static readonly FieldInfo? OnPlayerConnectedField = AccessTools.Field(MapVoteType, "OnPlayerConnected");

		internal static readonly MethodInfo? ResetMethod = AccessTools.Method(MapVoteType, "Reset", (Type[])null, (Type[])null);

		internal static readonly MethodInfo? UpdateButtonLabelsMethod = AccessTools.Method(MapVoteType, "UpdateButtonLabels", (Type[])null, (Type[])null);

		internal static readonly MethodInfo? CreateNextMapLabelMethod = AccessTools.Method(MapVoteType, "CreateNextMapLabel", (Type[])null, (Type[])null);

		internal static readonly MethodInfo? OnVotingDoneMethod = AccessTools.Method(MapVoteType, "OnVotingDone", (Type[])null, (Type[])null);

		internal static readonly MethodInfo? HandleOnSyncVotesMethod = AccessTools.Method(MapVoteType, "HandleOnSyncVotes", (Type[])null, (Type[])null);

		internal static readonly MethodInfo? GetLevelsMethod = AccessTools.Method(MapVoteType, "GetLevels", (Type[])null, (Type[])null);

		internal static readonly MethodInfo? GetEligibleOptionsMethod = AccessTools.Method(MapVoteType, "GetEligibleOptions", (Type[])null, (Type[])null);

		internal static readonly Type? VoteOptionButtonType = AccessTools.TypeByName("MapVotePlus.VoteOptionButton");

		internal static readonly PropertyInfo? OptionLevelProperty = AccessTools.Property(VoteOptionButtonType, "Level");

		internal static readonly Type? VotesDictionaryType = AccessTools.TypeByName("MapVotePlus.VotesDictionary");

		internal static readonly FieldInfo? VotesValuesField = AccessTools.Field(VotesDictionaryType, "Values");

		internal static readonly Type? NetworkingEventsType = AccessTools.TypeByName("REPOLib.Modules.NetworkingEvents");

		internal static readonly FieldInfo? RaiseMasterClientField = AccessTools.Field(NetworkingEventsType, "RaiseMasterClient");

		internal static bool ClientFixesReady
		{
			get
			{
				if (PageStateField != null && CurrentVotesField != null && VoteOptionButtonsField != null && OwnVoteLevelField != null && WonMapField != null && OnPlayerConnectedField != null && ResetMethod != null && UpdateButtonLabelsMethod != null && CreateNextMapLabelMethod != null && OnVotingDoneMethod != null && HandleOnSyncVotesMethod != null && GetLevelsMethod != null && OptionLevelProperty != null && VotesValuesField != null && RaiseMasterClientField != null && DisableInputField != null)
				{
					return GetEligibleOptionsMethod != null;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			if (MapVoteType == null || VotePopupField == null || VoteLevelsField == null || HideInMenuField == null || CurrentMenuPageField == null || CurrentMenuStateField == null || CurrentMenuPageObjectField == null || PageUnderThisPageField == null || CreateVotePopupMethod == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"MapVotePlus 1.3.1 UI members missing; patch not installed.");
				return;
			}
			if (!ClientFixesReady)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"MapVotePlus 1.3.1 vote members missing; client vote list fixes are off.");
			}
			try
			{
				new Harmony("themorningstar.mapvoteplusuifix").PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"MapVotePlus UI Fix 1.0.1 patches applied.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("MapVotePlus UI patch failed: " + ex));
			}
		}

		internal static bool IsMenuOpenOn(MenuPageIndex page)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			MenuManager instance = MenuManager.instance;
			if ((Object)(object)instance != (Object)null && (MenuPageIndex)CurrentMenuPageField.GetValue(instance) == page)
			{
				return (int)CurrentMenuStateField.GetValue(instance) == 0;
			}
			return false;
		}

		internal static bool IsAnotherMenuOpen()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			MenuManager instance = MenuManager.instance;
			if ((Object)(object)instance == (Object)null || (int)CurrentMenuStateField.GetValue(instance) != 0)
			{
				return false;
			}
			if ((int)(MenuPageIndex)CurrentMenuPageField.GetValue(instance) == 8)
			{
				return false;
			}
			object? value = CurrentMenuPageObjectField.GetValue(instance);
			MenuPage val = (MenuPage)((value is MenuPage) ? value : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			object? value2 = VotePopupField.GetValue(null);
			REPOPopupPage val2 = (REPOPopupPage)((value2 is REPOPopupPage) ? value2 : null);
			if ((Object)(object)val2 != (Object)null)
			{
				return !((Object)(object)val == (Object)(object)val2.menuPage);
			}
			return true;
		}

		internal static string CurrentMenuName()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			MenuManager instance = MenuManager.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				return ((object)(MenuPageIndex)CurrentMenuPageField.GetValue(instance)/*cast due to .constrained prefix*/).ToString();
			}
			return "none";
		}

		internal static bool HideVoteInLobby()
		{
			return ((ConfigEntry<bool>)HideInMenuField.GetValue(null)).Value;
		}

		internal static void OpenVote(bool inMenu, bool inLobby)
		{
			CreateVotePopupMethod.Invoke(null, new object[2] { inMenu, inLobby });
		}
	}
	internal static class VoteState
	{
		internal static REPOPopupPage? BuiltPopup;

		internal static List<string> BuiltFrom = new List<string>();

		internal static float BuiltAt;

		internal static bool Pending;

		internal static object? PendingScene;

		internal static string PendingReason = "";

		internal static object? VoteEndedScene;

		internal static object? RequestedScene;

		internal static object? Scene => GameDirector.instance;

		internal static bool InThisScene(object? token)
		{
			if (token != null)
			{
				return token == Scene;
			}
			return false;
		}

		internal static List<string> Levels()
		{
			return (Plugin.VoteLevelsField.GetValue(null) as List<string>) ?? new List<string>();
		}

		internal static List<string> OptionLevels()
		{
			List<string> list = new List<string>();
			if (!(Plugin.VoteOptionButtonsField.GetValue(null) is IList list2))
			{
				return list;
			}
			foreach (object item2 in list2)
			{
				if (item2 != null && Plugin.OptionLevelProperty.GetValue(item2) is string item)
				{
					list.Add(item);
				}
			}
			return list;
		}

		internal static Dictionary<int, string>? Votes()
		{
			object value = Plugin.CurrentVotesField.GetValue(null);
			if (value != null)
			{
				return Plugin.VotesValuesField.GetValue(value) as Dictionary<int, string>;
			}
			return null;
		}

		internal static bool IsOpen(REPOPopupPage? popup)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Invalid comparison between Unknown and I4
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Invalid comparison between Unknown and I4
			if ((Object)(object)popup == (Object)null || (Object)(object)popup.menuPage == (Object)null || !((Behaviour)popup.menuPage).enabled)
			{
				return false;
			}
			if (Plugin.PageStateField == null)
			{
				return true;
			}
			PageState val = (PageState)Plugin.PageStateField.GetValue(popup.menuPage);
			if ((int)val != 2)
			{
				return (int)val != 5;
			}
			return false;
		}

		internal static bool BuiltFromSame(List<string> list)
		{
			object? value = Plugin.VotePopupField.GetValue(null);
			REPOPopupPage val = (REPOPopupPage)((value is REPOPopupPage) ? value : null);
			if ((Object)(object)val != (Object)null && (Object)(object)BuiltPopup != (Object)null && (Object)(object)val == (Object)(object)BuiltPopup)
			{
				return BuiltFrom.SequenceEqual(list);
			}
			return false;
		}

		internal static void Defer(string reason)
		{
			Pending = true;
			PendingScene = Scene;
			PendingReason = reason;
		}

		internal static void RequestMapList()
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			object value = Plugin.OnPlayerConnectedField.GetValue(null);
			object value2 = Plugin.RaiseMasterClientField.GetValue(null);
			if (value != null && value2 != null)
			{
				AccessTools.Method(value.GetType(), "RaiseEvent", new Type[3]
				{
					typeof(object),
					value2.GetType(),
					typeof(SendOptions)
				}, (Type[])null)?.Invoke(value, new object[3]
				{
					"",
					value2,
					SendOptions.SendReliable
				});
			}
		}
	}
	[HarmonyPatch]
	internal static class ClientMapSyncPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(Plugin.MapVoteType, "HandleOnMapsRandomized", (Type[])null, (Type[])null);
		}

		private static bool Prefix(EventData data)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				return false;
			}
			if (!(data.CustomData is string[] array))
			{
				return true;
			}
			try
			{
				if (array.Length == 0)
				{
					int count = VoteState.Levels().Count;
					if (count > 0)
					{
						Plugin.Log.LogInfo((object)("Ignored an empty map list from the host; kept this client's " + count + " map(s)."));
					}
					return false;
				}
				if (VoteState.InThisScene(VoteState.VoteEndedScene))
				{
					return false;
				}
				List<string> list = new List<string>(array);
				Plugin.VoteLevelsField.SetValue(null, list);
				if (SemiFunc.RunIsLobbyMenu())
				{
					if (Plugin.HideVoteInLobby())
					{
						return false;
					}
					object? value = Plugin.VotePopupField.GetValue(null);
					REPOPopupPage popup = (REPOPopupPage)((value is REPOPopupPage) ? value : null);
					if (VoteState.BuiltFromSame(list) && VoteState.IsOpen(popup))
					{
						return false;
					}
					if (Plugin.IsMenuOpenOn((MenuPageIndex)8))
					{
						Plugin.OpenVote(inMenu: true, inLobby: true);
					}
					else
					{
						VoteState.Defer(Plugin.CurrentMenuName() + " menu");
						Plugin.Log.LogInfo((object)("Host map list arrived while this player was not on the lobby page (menu: " + Plugin.CurrentMenuName() + "); the vote opens when the lobby page is showing."));
					}
				}
				else if (SemiFunc.RunIsLobby())
				{
					if (VoteState.BuiltFromSame(list))
					{
						return false;
					}
					if (!Plugin.IsAnotherMenuOpen())
					{
						object? value2 = Plugin.VotePopupField.GetValue(null);
						REPOPopupPage val = (REPOPopupPage)((value2 is REPOPopupPage) ? value2 : null);
						int num = (((Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)VoteState.BuiltPopup) ? VoteState.BuiltFrom.Count : (-1));
						Plugin.OpenVote(inMenu: false, inLobby: false);
						if (num >= 0)
						{
							Plugin.Log.LogInfo((object)("Host map list arrived after this client built its truck vote from " + num + " map(s); rebuilt it with the host's " + list.Count + " maps."));
						}
					}
					else
					{
						VoteState.Defer(Plugin.CurrentMenuName() + " menu");
						Plugin.Log.LogInfo((object)("Host map list arrived while the " + Plugin.CurrentMenuName() + " menu was open; the vote opens when that menu closes."));
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Client map sync UI failed: " + ex));
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(MenuManager), "PageOpen")]
	internal static class MenuPageOpenPatch
	{
		[HarmonyPriority(800)]
		private unsafe static void Prefix(MenuPageIndex menuPageIndex)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			if ((int)menuPageIndex == 8 || (int)menuPageIndex == 9 || (int)menuPageIndex == 7)
			{
				return;
			}
			try
			{
				object? value = Plugin.VotePopupField.GetValue(null);
				REPOPopupPage val = (REPOPopupPage)((value is REPOPopupPage) ? value : null);
				if (!((Object)(object)val == (Object)null))
				{
					bool num = Plugin.ClientFixesReady && VoteState.IsOpen(val);
					val.ClosePage(false);
					if (num && SemiFunc.RunIsLobbyMenu() && !Plugin.HideVoteInLobby() && !VoteState.InThisScene(VoteState.VoteEndedScene))
					{
						VoteState.Defer(((object)(*(MenuPageIndex*)(&menuPageIndex))/*cast due to .constrained prefix*/).ToString() + " menu");
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not close vote popup on menu change: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(MenuManager), "Update")]
	internal static class PendingVotePatch
	{
		private static bool Prepare()
		{
			return Plugin.ClientFixesReady;
		}

		private static void Postfix()
		{
			if (!VoteState.Pending)
			{
				return;
			}
			try
			{
				if (!VoteState.InThisScene(VoteState.PendingScene) || VoteState.InThisScene(VoteState.VoteEndedScene))
				{
					VoteState.Pending = false;
					return;
				}
				MenuManager instance = MenuManager.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				object? value = Plugin.CurrentMenuPageObjectField.GetValue(instance);
				MenuPage val = (MenuPage)((value is MenuPage) ? value : null);
				if (SemiFunc.RunIsLobbyMenu())
				{
					if (Plugin.HideVoteInLobby())
					{
						VoteState.Pending = false;
					}
					else if (!((Object)(object)val == (Object)null) && Plugin.IsMenuOpenOn((MenuPageIndex)8))
					{
						VoteState.Pending = false;
						object? value2 = Plugin.VotePopupField.GetValue(null);
						if (!VoteState.IsOpen((REPOPopupPage?)((value2 is REPOPopupPage) ? value2 : null)))
						{
							Plugin.Log.LogInfo((object)("Reopened the lobby vote after the " + VoteState.PendingReason + " closed."));
							Plugin.OpenVote(inMenu: true, inLobby: true);
						}
					}
				}
				else if (SemiFunc.RunIsLobby())
				{
					if (!((Object)(object)val != (Object)null))
					{
						VoteState.Pending = false;
						if (!VoteState.BuiltFromSame(VoteState.Levels()))
						{
							Plugin.Log.LogInfo((object)("Opened the truck vote after the " + VoteState.PendingReason + " closed."));
							Plugin.OpenVote(inMenu: false, inLobby: false);
						}
					}
				}
				else
				{
					VoteState.Pending = false;
				}
			}
			catch (Exception ex)
			{
				VoteState.Pending = false;
				Plugin.Log.LogWarning((object)("Pending vote check failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch]
	internal static class CreateVotePopupPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(Plugin.MapVoteType, "CreateVotePopup", (Type[])null, (Type[])null);
		}

		private static void Prefix(bool isInMenu, bool isInLobby)
		{
			ClearStuckVoteInput(isInLobby);
			ClearDestroyedPopup(isInMenu, isInLobby);
		}

		private static void ClearStuckVoteInput(bool isInLobby)
		{
			if (Plugin.DisableInputField == null || (!Plugin.ClientFixesReady && (!isInLobby || !SemiFunc.IsMasterClientOrSingleplayer())) || VoteState.InThisScene(VoteState.VoteEndedScene))
			{
				return;
			}
			try
			{
				object value = Plugin.DisableInputField.GetValue(null);
				if (value is bool && (bool)value)
				{
					Plugin.DisableInputField.SetValue(null, false);
					Plugin.Log.LogWarning((object)("MapVotePlus DisableInput was still true when " + (isInLobby ? "the lobby opened" : "a new vote opened") + "; a vote animation from an earlier scene never finished. Cleared it so Start and the vote buttons respond."));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Vote input check failed: " + ex.Message));
			}
		}

		private unsafe static void ClearDestroyedPopup(bool isInMenu, bool isInLobby)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				object? value = Plugin.VotePopupField.GetValue(null);
				REPOPopupPage val = (REPOPopupPage)((value is REPOPopupPage) ? value : null);
				if (val == null)
				{
					return;
				}
				MenuPage menuPage = val.menuPage;
				if ((Object)(object)val != (Object)null)
				{
					return;
				}
				string text = "it had no page under it, so nothing would have changed";
				if (menuPage != null)
				{
					object? value2 = Plugin.PageUnderThisPageField.GetValue(menuPage);
					MenuPage val2 = (MenuPage)((value2 is MenuPage) ? value2 : null);
					if (val2 != null)
					{
						MenuPageIndex menuPageIndex = val2.menuPageIndex;
						string text2 = (((Object)(object)val2 == (Object)null) ? "destroyed" : "live");
						text = "MenuLib would have made a " + text2 + " " + ((object)(*(MenuPageIndex*)(&menuPageIndex))/*cast due to .constrained prefix*/).ToString() + " page current and locked the lobby buttons";
					}
				}
				Plugin.VotePopupField.SetValue(null, null);
				Plugin.Log.LogInfo((object)("Cleared vote popup destroyed by a level load (CreateVotePopup isInMenu=" + isInMenu + ", isInLobby=" + isInLobby + "); " + text + "."));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Stale vote popup check failed: " + ex.Message));
			}
		}

		private static void Postfix(bool isInLobby)
		{
			RecordBuiltPopup();
			GuardLobbyFocus(isInLobby);
		}

		private static void RecordBuiltPopup()
		{
			if (!Plugin.ClientFixesReady)
			{
				return;
			}
			try
			{
				List<string> list = VoteState.Levels();
				object? value = Plugin.VotePopupField.GetValue(null);
				VoteState.BuiltPopup = (REPOPopupPage?)((value is REPOPopupPage) ? value : null);
				VoteState.BuiltFrom = new List<string>(list);
				VoteState.BuiltAt = Time.realtimeSinceStartup;
				if (list.Count == 0 && !SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.RunIsLobby() && !VoteState.InThisScene(VoteState.RequestedScene) && !VoteState.InThisScene(VoteState.VoteEndedScene))
				{
					VoteState.RequestedScene = VoteState.Scene;
					VoteState.RequestMapList();
					Plugin.Log.LogInfo((object)"Built the truck vote before the host's map list arrived; asked the host to send it again.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Vote popup bookkeeping failed: " + ex.Message));
			}
		}

		private static void GuardLobbyFocus(bool isInLobby)
		{
			//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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Invalid comparison between Unknown and I4
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected I4, but got Unknown
			if (!isInLobby || !SemiFunc.RunIsLobbyMenu())
			{
				return;
			}
			try
			{
				MenuManager instance = MenuManager.instance;
				MenuPageLobby instance2 = MenuPageLobby.instance;
				object? value = Plugin.VotePopupField.GetValue(null);
				REPOPopupPage val = (REPOPopupPage)((value is REPOPopupPage) ? value : null);
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance2 == (Object)null) && !((Object)(object)val == (Object)null))
				{
					MenuPage component = ((Component)instance2).GetComponent<MenuPage>();
					MenuPageIndex val2 = (MenuPageIndex)Plugin.CurrentMenuPageField.GetValue(instance);
					object? value2 = Plugin.CurrentMenuPageObjectField.GetValue(instance);
					MenuPage val3 = (MenuPage)((value2 is MenuPage) ? value2 : null);
					if (!((Object)(object)component == (Object)null) && (int)val2 != 8 && !((Object)(object)val3 != (Object)(object)val.menuPage))
					{
						Plugin.Log.LogWarning((object)("Lobby vote popup opened as the current page (index " + (int)val2 + "), which locks the lobby buttons. Giving focus back to the lobby page."));
						instance.PageSetCurrent((MenuPageIndex)8, component);
						Plugin.PageUnderThisPageField.SetValue(val.menuPage, null);
						val.OpenPage(true);
						val.menuPage.PageStateSet((PageState)1);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Lobby focus check failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch]
	internal static class VoteEndPatch
	{
		private static bool Prepare()
		{
			return Plugin.ClientFixesReady;
		}

		private static MethodBase TargetMethod()
		{
			return Plugin.OnVotingDoneMethod;
		}

		private static bool Prefix(string winningMap, ref IEnumerator __result)
		{
			VoteState.VoteEndedScene = VoteState.Scene;
			VoteState.Pending = false;
			try
			{
				bool flag = SemiFunc.IsMasterClientOrSingleplayer();
				List<string> list = VoteState.OptionLevels();
				int num = list.Count((string level) => level != "Random");
				int num2 = VoteState.Votes()?.Count ?? 0;
				int num3 = (((Object)(object)GameDirector.instance != (Object)null) ? GameDirector.instance.PlayerList.Count : 0);
				bool flag2 = Plugin.OwnVoteLevelField.GetValue(null) != null;
				object? value = Plugin.VotePopupField.GetValue(null);
				REPOPopupPage val = (REPOPopupPage)((value is REPOPopupPage) ? value : null);
				string text = (((Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)VoteState.BuiltPopup) ? ((Time.realtimeSinceStartup - VoteState.BuiltAt).ToString("0.0") + " s") : "an unknown time");
				Plugin.Log.LogInfo((object)("Vote ended on " + winningMap + " (" + (flag ? "host" : "client") + "): " + num2 + " of " + num3 + " players voted; this machine's list had " + num + " map(s); it voted: " + (flag2 ? "yes" : "no") + "; its vote popup was built " + text + " earlier."));
				if (!flag && winningMap != null && !SpinCanFinish(winningMap, out string why))
				{
					Plugin.Log.LogWarning((object)("Vote winner " + winningMap + ": " + why + " (this client's list: " + string.Join(", ", list) + "); MapVotePlus's end-of-vote animation would never finish. Ended the vote without it."));
					__result = EndVoteWithoutSpin(winningMap);
					return false;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Vote end check failed: " + ex.Message));
			}
			return true;
		}

		private static bool SpinCanFinish(string winningMap, out string why)
		{
			why = "";
			Plugin.WonMapField.SetValue(null, winningMap);
			IList list = Plugin.VoteOptionButtonsField.GetValue(null) as IList;
			List<object> list2 = ((list == null) ? null : new List<object>(list.Cast<object>()));
			IList list3;
			try
			{
				list3 = Plugin.GetEligibleOptionsMethod.Invoke(null, null) as IList;
				if (list3 != null)
				{
					list3 = new List<object>(list3.Cast<object>());
				}
			}
			catch (TargetInvocationException ex)
			{
				why = "MapVotePlus's GetEligibleOptions threw " + (ex.InnerException ?? ex).GetType().Name;
				return false;
			}
			finally
			{
				if (list != null && list2 != null && list.Count != list2.Count)
				{
					list.Clear();
					foreach (object item in list2)
					{
						list.Add(item);
					}
				}
			}
			if (list3 == null || list3.Count <= 1)
			{
				return true;
			}
			foreach (object item2 in list3)
			{
				if (item2 != null && Plugin.OptionLevelProperty.GetValue(item2) as string == winningMap)
				{
					return true;
				}
			}
			why = "not among this client's " + list3.Count + " spin options";
			return false;
		}

		private static IEnumerator EndVoteWithoutSpin(string winningMap)
		{
			Plugin.WonMapField.SetValue(null, winningMap);
			Plugin.DisableInputField.SetValue(null, false);
			if (SemiFunc.RunIsLobby())
			{
				try
				{
					Plugin.CreateNextMapLabelMethod.Invoke(null, new object[1] { winningMap });
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Next map label failed: " + ex.Message));
				}
				object? value = Plugin.VotePopupField.GetValue(null);
				REPOPopupPage val = (REPOPopupPage)((value is REPOPopupPage) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					val.ClosePage(true);
				}
				MenuAPI.CloseAllPagesAddedOnTop();
			}
			Plugin.ResetMethod.Invoke(null, null);
			yield break;
		}
	}
	[HarmonyPatch]
	internal static class ClientVoteSyncPatch
	{
		private static bool Prepare()
		{
			return Plugin.ClientFixesReady;
		}

		private static MethodBase TargetMethod()
		{
			return Plugin.HandleOnSyncVotesMethod;
		}

		private static bool Prefix(EventData data)
		{
			if (SemiFunc.IsMasterClient())
			{
				return true;
			}
			if (!(data.CustomData is Dictionary<int, string> dictionary))
			{
				return true;
			}
			try
			{
				Dictionary<int, string> dictionary2 = VoteState.Votes();
				if (dictionary2 == null)
				{
					return true;
				}
				int count = VoteState.Levels().Count;
				int count2 = VoteState.OptionLevels().Count;
				dictionary2.Clear();
				foreach (KeyValuePair<int, string> item in dictionary)
				{
					dictionary2[item.Key] = item.Value;
				}
				Plugin.WonMapField.SetValue(null, null);
				Plugin.UpdateButtonLabelsMethod.Invoke(null, null);
				if (count > 0 || count2 > 0)
				{
					Plugin.Log.LogInfo((object)("Host vote sync (" + dictionary.Count + " votes): kept this client's map list (" + count + ") and vote buttons (" + count2 + ") that MapVotePlus would have cleared."));
				}
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Vote sync handling failed, using MapVotePlus's: " + ex.Message));
				return true;
			}
		}
	}
	[HarmonyPatch]
	internal static class LevelMatchPatch
	{
		private static bool Prepare()
		{
			return Plugin.ClientFixesReady;
		}

		private static MethodBase TargetMethod()
		{
			return Plugin.GetLevelsMethod;
		}

		private static void Postfix(bool reRandomize, List<Level> __result)
		{
			if (reRandomize || __result == null)
			{
				return;
			}
			try
			{
				List<string> list = VoteState.Levels();
				if (list.Count != 0)
				{
					HashSet<string> have = new HashSet<string>(from level in __result
						where (Object)(object)level != (Object)null
						select ((Object)level).name);
					List<string> list2 = list.Where((string name) => !have.Contains(name)).ToList();
					if (list2.Count > 0)
					{
						Plugin.Log.LogWarning((object)("The host's map list names " + list2.Count + " map(s) this game does not have: " + string.Join(", ", list2) + ". They are left out of this vote list."));
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Map list check failed: " + ex.Message));
			}
		}
	}
}