Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MapVotePlusUiFix v1.0.1
MapVotePlusUiFix.dll
Decompiled a month agousing 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)); } } } }