Decompiled source of MorePlayersPlus v1.0.2

MorePlayersPlus.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using MorePlayersPlus.Patches;
using Steamworks;
using Steamworks.Data;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("GameAssembly")]
[assembly: IgnoresAccessChecksTo("SharedAssembly")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MorePlayersPlus")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2+1b90ff945a285959a9ec2b98a235974fe797ddc3")]
[assembly: AssemblyProduct("More Players Plus")]
[assembly: AssemblyTitle("MorePlayersPlus")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.2.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 MorePlayersPlus
{
	internal static class LogThrottle
	{
		private const int ALWAYS_LOG_FIRST = 3;

		private const int THEN_EVERY = 200;

		internal static bool ShouldLog(int occurrence)
		{
			if (occurrence <= 0)
			{
				return false;
			}
			if (occurrence > 3)
			{
				return (occurrence - 3) % 200 == 0;
			}
			return true;
		}
	}
	internal readonly struct PageWindow
	{
		internal int Total { get; }

		internal int Start { get; }

		internal int EndExclusive { get; }

		internal int Page { get; }

		internal int PageCount { get; }

		internal bool IsPaged => PageCount > 1;

		internal string Label
		{
			get
			{
				if (Total != 0)
				{
					return $"{Start + 1}-{EndExclusive} of {Total}";
				}
				return "0 of 0";
			}
		}

		private PageWindow(int total, int start, int endExclusive, int page, int pageCount)
		{
			Total = total;
			Start = start;
			EndExclusive = endExclusive;
			Page = page;
			PageCount = pageCount;
		}

		internal bool Contains(int index)
		{
			if (index >= Start)
			{
				return index < EndExclusive;
			}
			return false;
		}

		internal static PageWindow Compute(int total, int rowsPerPage, int requestedPage)
		{
			int num = Math.Max(0, total);
			int num2 = Math.Max(1, rowsPerPage);
			int num3 = Math.Max(1, (num + num2 - 1) / num2);
			int num4 = Math.Min(Math.Max(requestedPage, 0), num3 - 1);
			int num5 = num4 * num2;
			int endExclusive = Math.Min(num, num5 + num2);
			return new PageWindow(num, num5, endExclusive, num4, num3);
		}
	}
	internal enum SpotTier
	{
		InBetween,
		SideLine,
		StaggeredRear,
		ElevatedRear,
		Squeeze
	}
	internal readonly struct SpotOffset
	{
		internal float X { get; }

		internal float Z { get; }

		internal SpotTier Tier { get; }

		internal float MinGap { get; }

		internal SpotOffset(float x, float z, SpotTier tier, float minGap)
		{
			X = x;
			Z = z;
			Tier = tier;
			MinGap = minGap;
		}
	}
	internal static class SpotCandidates
	{
		private const int MAX_REAR_ROWS = 3;

		private const int MAX_SQUEEZE_LEVELS = 2;

		private const float STAGGER_FRACTION = 0.5f;

		private const float GAP_TOLERANCE = 0.9f;

		private const float HALF = 0.5f;

		internal static IReadOnlyList<SpotOffset> GenerateForPlatform(IReadOnlyList<float> vanillaX, float spacing, float fallbackHalfWidth, float maxDistance, bool includeElevatedRear, bool inBetween, int squeezeLevels, float minSpotGap)
		{
			List<SpotOffset> list = new List<SpotOffset>();
			if (spacing <= 0f)
			{
				return list;
			}
			List<float> list2 = vanillaX.OrderBy((float x) => x).ToList();
			float halfWidth = ((list2.Count > 0) ? Math.Max(Math.Abs(list2[0]), Math.Abs(list2[list2.Count - 1])) : fallbackHalfWidth);
			if (inBetween && list2.Count > 0)
			{
				List<float> list3 = HalfStepsWithOverhang(list2, spacing);
				AddCenterOut(list, list3, SpotTier.InBetween, spacing * 0.5f * 0.9f);
				list2.AddRange(list3);
				list2.Sort();
			}
			list.AddRange(Generate(spacing, halfWidth, maxDistance, includeElevatedRear));
			AddSqueeze(list, list2, Math.Min(squeezeLevels, 2), minSpotGap);
			return list;
		}

		internal static IReadOnlyList<SpotOffset> Generate(float spacing, float halfWidth, float maxDistance, bool includeElevatedRear)
		{
			List<SpotOffset> result = new List<SpotOffset>();
			if (spacing <= 0f || maxDistance <= 0f)
			{
				return result;
			}
			float gap = spacing * 0.9f;
			AddSideLine(result, spacing, halfWidth, maxDistance, gap);
			AddStaggeredRear(result, spacing, halfWidth, maxDistance, gap);
			if (includeElevatedRear)
			{
				AddElevatedRear(result, spacing, halfWidth, maxDistance, gap);
			}
			return result;
		}

		private static List<float> HalfStepsWithOverhang(List<float> sortedLine, float spacing)
		{
			List<float> list = new List<float>();
			for (int i = 0; i + 1 < sortedLine.Count; i++)
			{
				list.Add((sortedLine[i] + sortedLine[i + 1]) * 0.5f);
			}
			list.Add(sortedLine[0] - spacing * 0.5f);
			list.Add(sortedLine[sortedLine.Count - 1] + spacing * 0.5f);
			return list;
		}

		private static void AddSqueeze(List<SpotOffset> result, List<float> line, int levels, float minSpotGap)
		{
			for (int i = 0; i < levels; i++)
			{
				List<(float, float)> list = new List<(float, float)>();
				for (int j = 0; j + 1 < line.Count; j++)
				{
					float num = (line[j + 1] - line[j]) * 0.5f;
					float item = Math.Max(num * 0.9f, minSpotGap);
					if (!(num < minSpotGap))
					{
						list.Add(((line[j] + line[j + 1]) * 0.5f, item));
					}
				}
				foreach (var (x, minGap) in from m in list
					orderby Math.Abs(m.X), m.X descending
					select m)
				{
					result.Add(new SpotOffset(x, 0f, SpotTier.Squeeze, minGap));
				}
				line.AddRange(list.Select<(float, float), float>(((float X, float Gap) m) => m.X));
				line.Sort();
			}
		}

		private static void AddCenterOut(List<SpotOffset> result, List<float> xs, SpotTier tier, float gap)
		{
			foreach (float item in from v in xs
				orderby Math.Abs(v), v descending
				select v)
			{
				result.Add(new SpotOffset(item, 0f, tier, gap));
			}
		}

		private static void AddSideLine(List<SpotOffset> result, float spacing, float halfWidth, float maxDistance, float gap)
		{
			int num = 1;
			while (true)
			{
				float num2 = halfWidth + (float)num * spacing;
				if (num2 > maxDistance)
				{
					break;
				}
				result.Add(new SpotOffset(num2, 0f, SpotTier.SideLine, gap));
				result.Add(new SpotOffset(0f - num2, 0f, SpotTier.SideLine, gap));
				num++;
			}
		}

		private static void AddStaggeredRear(List<SpotOffset> result, float spacing, float halfWidth, float maxDistance, float gap)
		{
			for (int i = 1; i <= 3; i++)
			{
				float z = (float)(-i) * spacing;
				float num = ((i % 2 == 1) ? 0.5f : 0f);
				int num2 = 1;
				while (true)
				{
					float num3 = halfWidth + ((float)num2 + num) * spacing;
					if (Distance(num3, z) > maxDistance)
					{
						break;
					}
					result.Add(new SpotOffset(num3, z, SpotTier.StaggeredRear, gap));
					result.Add(new SpotOffset(0f - num3, z, SpotTier.StaggeredRear, gap));
					num2++;
				}
			}
		}

		private static void AddElevatedRear(List<SpotOffset> result, float spacing, float halfWidth, float maxDistance, float gap)
		{
			for (int i = 1; i <= 3; i++)
			{
				float z = (float)(-i) * spacing;
				int num = (int)Math.Ceiling(halfWidth / spacing);
				for (int j = -num; j <= num; j++)
				{
					float x = (float)j * spacing;
					if (!(Distance(x, z) > maxDistance))
					{
						result.Add(new SpotOffset(x, z, SpotTier.ElevatedRear, gap));
					}
				}
			}
		}

		private static float Distance(float x, float z)
		{
			return (float)Math.Sqrt(x * x + z * z);
		}
	}
	internal static class SpotPlatformChooser
	{
		internal static int Choose(IReadOnlyList<(bool HasNext, SpotTier NextTier, int Added)> platforms)
		{
			int num = -1;
			for (int i = 0; i < platforms.Count; i++)
			{
				if (platforms[i].HasNext && (num < 0 || IsBetter(platforms[i], platforms[num])))
				{
					num = i;
				}
			}
			return num;
		}

		private static bool IsBetter((bool HasNext, SpotTier NextTier, int Added) candidate, (bool HasNext, SpotTier NextTier, int Added) current)
		{
			if (candidate.NextTier != current.NextTier)
			{
				return candidate.NextTier < current.NextTier;
			}
			return candidate.Added < current.Added;
		}
	}
	internal static class TeamCap
	{
		internal const int VANILLA_TEAM_CAP = 15;

		internal static int Resolve(int maxPlayers)
		{
			return Math.Max(15, maxPlayers);
		}
	}
	internal static class Diag
	{
		private static readonly Dictionary<string, int> ErrorCounts = new Dictionary<string, int>();

		internal static void Info(string area, string message)
		{
			Plugin.Log.LogInfo((object)("[" + area + "] " + message));
		}

		internal static void Warn(string area, string message)
		{
			Plugin.Log.LogWarning((object)("[" + area + "] " + message));
		}

		internal static void Error(string area, string context, Exception exception)
		{
			ErrorCounts.TryGetValue(area, out var value);
			value++;
			ErrorCounts[area] = value;
			if (LogThrottle.ShouldLog(value))
			{
				Plugin.Log.LogError((object)($"[{area}] ERROR (occurrence {value}). Context: {context}. " + "Cause: " + exception.GetType().Name + ": " + exception.Message + ". Resolution: this feature was skipped for this call and the game was left unchanged; please send this log." + Environment.NewLine + exception.StackTrace));
			}
		}
	}
	internal sealed class DiagnosticsReporter : MonoBehaviour
	{
		private const float PERF_INTERVAL_SECONDS = 30f;

		private const int PERF_MIN_PLAYERS = 17;

		private const float SLOW_FRAME_SECONDS = 0.25f;

		private const int MAX_SLOW_FRAME_LINES = 25;

		private const float MS_PER_SECOND = 1000f;

		private const float BYTES_PER_MEGABYTE = 1048576f;

		private const string TEE_OFF_STATE = "TeeOff";

		private int frames;

		private float frameSecondsTotal;

		private float frameSecondsMax;

		private float nextPerfTime;

		private int slowFrameLines;

		private int lastHole = int.MinValue;

		private int lastPlayers = -1;

		private string lastState = string.Empty;

		private bool autoProbed;

		private void Start()
		{
			try
			{
				LogSession();
				nextPerfTime = Time.unscaledTime + 30f;
			}
			catch (Exception exception)
			{
				Diag.Error("Session", "writing the session header", exception);
			}
		}

		private void Update()
		{
			try
			{
				float unscaledDeltaTime = Time.unscaledDeltaTime;
				frames++;
				frameSecondsTotal += unscaledDeltaTime;
				frameSecondsMax = Mathf.Max(frameSecondsMax, unscaledDeltaTime);
				LogSlowFrame(unscaledDeltaTime);
				TrackMatch();
				LogPerformanceIfDue();
			}
			catch (Exception exception)
			{
				Diag.Error("Diagnostics", "per-frame diagnostics", exception);
			}
		}

		private static void LogSession()
		{
			Diag.Info("Session", "MorePlayersPlus 1.0.2 | game " + Application.version + " | Unity " + Application.unityVersion + " | " + SystemInfo.operatingSystem);
			Diag.Info("Session", $"CPU {SystemInfo.processorType} x{SystemInfo.processorCount} | " + $"GPU {SystemInfo.graphicsDeviceName} {SystemInfo.graphicsMemorySize} MB | RAM {SystemInfo.systemMemorySize} MB | " + $"{Screen.width}x{Screen.height} | quality {QualitySettings.names[QualitySettings.GetQualityLevel()]}");
			Diag.Info("Session", $"config: MaxPlayers={Plugin.MaxPlayersConfig.Value} " + $"ExtraSpotSpacing={Plugin.ExtraSpotSpacing.Value} MaxSideSpotDistance={Plugin.MaxExtraSpotDistance.Value} " + $"RequireClearLineOfFire={Plugin.RequireClearLineOfFire.Value} " + $"AllowElevatedRearSpots={Plugin.AllowElevatedRearSpots.Value} " + $"InBetweenSpots={Plugin.InBetweenSpots.Value} SqueezeLevels={Plugin.SqueezeLevels.Value} " + $"MinSpotGap={Plugin.MinSpotGap.Value} " + $"PaginateScoreboard={Plugin.PaginateScoreboard.Value} PaginatePauseMenu={Plugin.PaginatePauseMenu.Value} " + $"RowsPerPage={Plugin.RowsPerPage.Value} PublishPresence={Plugin.PublishPresence.Value} " + $"DebugOverflowAfterPlayers={Plugin.DebugOverflowAfterPlayers.Value} " + $"LogPerformance={Plugin.LogPerformance.Value}");
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
			{
				list.Add($"{pluginInfo.Key} {pluginInfo.Value.Metadata.Version}");
			}
			Diag.Info("Session", string.Format("BepInEx plugins ({0}): {1}", list.Count, string.Join(", ", list)));
		}

		private void LogSlowFrame(float deltaSeconds)
		{
			if (!(deltaSeconds < 0.25f) && slowFrameLines < 25)
			{
				slowFrameLines++;
				Diag.Info("Perf", $"slow frame {deltaSeconds * 1000f:0} ms ({MatchSummary()})");
			}
		}

		private void TrackMatch()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (SingletonNetworkBehaviour<CourseManager>.HasInstance)
			{
				int currentHoleCourseIndex = CourseManager.CurrentHoleCourseIndex;
				int num = CourseManager.CountTotalActivePlayers();
				string text = ((object)CourseManager.MatchState/*cast due to .constrained prefix*/).ToString();
				bool num2 = currentHoleCourseIndex != lastHole || num != lastPlayers || text != lastState;
				lastHole = currentHoleCourseIndex;
				lastPlayers = num;
				lastState = text;
				if (num2)
				{
					Diag.Info("Match", MatchSummary());
				}
				if (!autoProbed && num >= 17 && text == "TeeOff")
				{
					autoProbed = true;
					ProbeCommand.WriteAll($"automatic: first tee-off with {num} players");
				}
			}
		}

		private void LogPerformanceIfDue()
		{
			if (!(Time.unscaledTime < nextPerfTime))
			{
				nextPerfTime = Time.unscaledTime + 30f;
				int num = (SingletonNetworkBehaviour<CourseManager>.HasInstance ? CourseManager.CountTotalActivePlayers() : 0);
				if (Plugin.LogPerformance.Value && num >= 17 && frames > 0)
				{
					float num2 = frameSecondsTotal / (float)frames;
					Diag.Info("Perf", $"{MatchSummary()} | fps {1f / num2:0.0} avg frame " + $"{num2 * 1000f:0.0} ms worst {frameSecondsMax * 1000f:0} ms " + $"({frames} frames) | {NetworkSummary()} | managed {(float)GC.GetTotalMemory(forceFullCollection: false) / 1048576f:0} MB");
				}
				frames = 0;
				frameSecondsTotal = 0f;
				frameSecondsMax = 0f;
			}
		}

		private static string MatchSummary()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (!SingletonNetworkBehaviour<CourseManager>.HasInstance)
			{
				return "role " + Role() + ", no course";
			}
			return $"role {Role()}, hole {CourseManager.CurrentHoleCourseIndex}, state {CourseManager.MatchState}, " + $"players {CourseManager.CountTotalActivePlayers()}, max {BNetworkManager.MaxPlayers}";
		}

		private static string NetworkSummary()
		{
			if (NetworkServer.active)
			{
				return $"net connections {NetworkServer.connections.Count} tick {NetworkServer.actualTickRate}/" + $"{NetworkServer.tickRate} Hz";
			}
			if (!NetworkClient.active)
			{
				return "net offline";
			}
			return $"net rtt {NetworkTime.rtt * 1000.0:0} ms";
		}

		private static string Role()
		{
			if (NetworkServer.active)
			{
				if (!NetworkClient.active)
				{
					return "server";
				}
				return "host";
			}
			if (!NetworkClient.active)
			{
				return "offline";
			}
			return "client";
		}
	}
	internal sealed class ListPager : MonoBehaviour
	{
		private const float SCROLL_THRESHOLD = 0.1f;

		private const int FONT_SIZE = 15;

		private const float LABEL_WIDTH = 520f;

		private const float LABEL_HEIGHT = 28f;

		private const float LABEL_BOTTOM_MARGIN = 54f;

		private const float SHADOW_OFFSET = 1f;

		private const string HINT = "   (mouse wheel / PageUp / PageDown)";

		private GUIStyle textStyle;

		private GUIStyle shadowStyle;

		private void Update()
		{
			try
			{
				int num = ReadPageDelta();
				if (num != 0)
				{
					TurnPage(num);
				}
			}
			catch (Exception exception)
			{
				Diag.Error("Lists", "reading page-turn input", exception);
			}
		}

		private static void TurnPage(int delta)
		{
			if (Scoreboard.IsVisible && Plugin.PaginateScoreboard.Value && PagingState.ScoreboardPageCount > 1)
			{
				PagingState.ScoreboardPage = Mathf.Clamp(PagingState.ScoreboardPage + delta, 0, PagingState.ScoreboardPageCount - 1);
				SingletonBehaviour<Scoreboard>.Instance.MarkDirty();
			}
			else if (SingletonBehaviour<PauseMenu>.HasInstance && PauseMenu.IsPaused && Plugin.PaginatePauseMenu.Value && PagingState.PausePageCount > 1)
			{
				PagingState.PausePage = Mathf.Clamp(PagingState.PausePage + delta, 0, PagingState.PausePageCount - 1);
				SingletonBehaviour<PauseMenu>.Instance.UpdatePlayerEntries();
			}
		}

		private void OnGUI()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string label = GetLabel();
				if (label != null)
				{
					EnsureStyles();
					float num = ((float)Screen.width - 520f) / 2f;
					float num2 = (float)Screen.height - 54f;
					GUI.Label(new Rect(num + 1f, num2 + 1f, 520f, 28f), label, shadowStyle);
					GUI.Label(new Rect(num, num2, 520f, 28f), label, textStyle);
				}
			}
			catch (Exception exception)
			{
				Diag.Error("Lists", "drawing the page hint", exception);
			}
		}

		private static string GetLabel()
		{
			if (Scoreboard.IsVisible && Plugin.PaginateScoreboard.Value && PagingState.ScoreboardPageCount > 1)
			{
				return "Players " + PagingState.ScoreboardLabel + $"  -  page {PagingState.ScoreboardPage + 1}/{PagingState.ScoreboardPageCount}" + "   (mouse wheel / PageUp / PageDown)";
			}
			if (SingletonBehaviour<PauseMenu>.HasInstance && PauseMenu.IsPaused && Plugin.PaginatePauseMenu.Value && PagingState.PausePageCount > 1)
			{
				return "Players " + PagingState.PauseLabel + $"  -  page {PagingState.PausePage + 1}/{PagingState.PausePageCount}" + "   (mouse wheel / PageUp / PageDown)";
			}
			return null;
		}

		private static int ReadPageDelta()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			Mouse current = Mouse.current;
			if (current != null)
			{
				float y = ((InputControl<Vector2>)(object)current.scroll).ReadValue().y;
				if (y > 0.1f)
				{
					num--;
				}
				else if (y < -0.1f)
				{
					num++;
				}
			}
			Keyboard current2 = Keyboard.current;
			if (current2 != null)
			{
				if (((ButtonControl)current2.pageDownKey).wasPressedThisFrame)
				{
					num++;
				}
				if (((ButtonControl)current2.pageUpKey).wasPressedThisFrame)
				{
					num--;
				}
			}
			return Mathf.Clamp(num, -1, 1);
		}

		private void EnsureStyles()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (textStyle == null)
			{
				textStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 15,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4
				};
				textStyle.normal.textColor = Color.white;
				shadowStyle = new GUIStyle(textStyle);
				shadowStyle.normal.textColor = Color.black;
			}
		}
	}
	internal static class OverflowSpotProvider
	{
		private sealed class TierBox
		{
			internal SpotTier Tier;
		}

		private sealed class PlatformState
		{
			internal readonly List<Candidate> Candidates = new List<Candidate>();

			internal readonly List<string> Report = new List<string>();

			internal int Next;

			internal int Added;

			internal int SkippedClose;

			internal bool BuiltWhileActive;
		}

		internal readonly struct Candidate
		{
			internal Vector3 TeePosition { get; }

			internal SpotTier Tier { get; }

			internal float MinGap { get; }

			internal Candidate(Vector3 teePosition, SpotTier tier, float minGap)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				TeePosition = teePosition;
				Tier = tier;
				MinGap = minGap;
			}
		}

		private const string AREA = "Spawn";

		private const float GROUND_RAY_RISE = 2f;

		private const float GROUND_RAY_LENGTH = 5f;

		private const float GROUND_HEIGHT_TOLERANCE = 0.75f;

		private const float MIN_WALKABLE_NORMAL_Y = 0.7f;

		private const float LINE_RAY_HEIGHT = 0.5f;

		private const float CLEAR_LINE_DISTANCE = 10f;

		private const float MIN_ELEVATION = 1.5f;

		private const float MAX_ELEVATION = 12f;

		private const float ELEVATED_RAY_MARGIN = 1f;

		private const int FIRST_EXTRA_NUMBER = 1;

		private static readonly ConditionalWeakTable<GolfTeeingPlatform, PlatformState> States = new ConditionalWeakTable<GolfTeeingPlatform, PlatformState>();

		private static readonly ConditionalWeakTable<TeeingSpot, TierBox> ExtraSpotTiers = new ConditionalWeakTable<TeeingSpot, TierBox>();

		internal static bool IsExtraSpot(TeeingSpot spot)
		{
			TierBox value;
			if (spot != null)
			{
				return ExtraSpotTiers.TryGetValue(spot, out value);
			}
			return false;
		}

		internal static string DescribeSpot(TeeingSpot spot)
		{
			if (spot == null || !ExtraSpotTiers.TryGetValue(spot, out var value))
			{
				return "vanilla";
			}
			return value.Tier.ToString();
		}

		internal static bool HasFreeSpot(GolfTeeManager manager)
		{
			foreach (GolfTeeingPlatform allTeeingPlaform in manager.allTeeingPlaforms)
			{
				if (allTeeingPlaform.IsActive && allTeeingPlaform.GetAvailableTeeingSpotCount() > 0)
				{
					return true;
				}
			}
			return false;
		}

		internal static bool EnsureFreeSpot(GolfTeeManager manager)
		{
			if (HasFreeSpot(manager))
			{
				return true;
			}
			TeeingSpot added;
			return TryAddSpot(manager, out added);
		}

		internal static bool IsDebugExhausted(GolfTeeManager manager)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			int value = Plugin.DebugOverflowAfterPlayers.Value;
			if (value <= 0)
			{
				return false;
			}
			int num = 0;
			foreach (GolfTeeingPlatform allTeeingPlaform in manager.allTeeingPlaforms)
			{
				if (!allTeeingPlaform.IsActive || allTeeingPlaform.teeingSpots == null)
				{
					continue;
				}
				TeeingSpot[] teeingSpots = allTeeingPlaform.teeingSpots;
				foreach (TeeingSpot val in teeingSpots)
				{
					if (!IsExtraSpot(val) && (int)val.Status != 0)
					{
						num++;
					}
				}
			}
			return num >= value;
		}

		internal static bool TryGetDebugSpot(GolfTeeManager manager, out TeeingSpot spot)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			foreach (GolfTeeingPlatform allTeeingPlaform in manager.allTeeingPlaforms)
			{
				if (!allTeeingPlaform.IsActive || allTeeingPlaform.teeingSpots == null)
				{
					continue;
				}
				TeeingSpot[] teeingSpots = allTeeingPlaform.teeingSpots;
				foreach (TeeingSpot val in teeingSpots)
				{
					if (IsExtraSpot(val) && (int)val.Status == 0)
					{
						spot = val;
						return true;
					}
				}
			}
			return TryAddSpot(manager, out spot);
		}

		private static bool TryAddSpot(GolfTeeManager manager, out TeeingSpot added)
		{
			//IL_0060: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			added = null;
			List<GolfTeeingPlatform> list;
			int num;
			PlatformState platformState;
			Candidate candidate;
			while (true)
			{
				list = new List<GolfTeeingPlatform>();
				List<PlatformState> list2 = new List<PlatformState>();
				List<(bool, SpotTier, int)> list3 = new List<(bool, SpotTier, int)>();
				CollectPlatforms(manager, list, list2, list3);
				num = SpotPlatformChooser.Choose(list3);
				if (num < 0)
				{
					LogOutOfSpots(list, list2);
					return false;
				}
				platformState = list2[num];
				candidate = platformState.Candidates[platformState.Next++];
				if (!TooCloseToAny(candidate.TeePosition, CollectOccupiedPositions(), candidate.MinGap))
				{
					break;
				}
				platformState.SkippedClose++;
				Diag.Info("Spawn", $"skipped a {candidate.Tier} spot on {((Object)list[num]).name} at " + $"{candidate.TeePosition}: too close to another platform's spot");
			}
			platformState.Added++;
			added = Append(list[num], candidate);
			Diag.Info("Spawn", $"added extra spot #{platformState.Added} ({candidate.Tier}) on {((Object)list[num]).name} at " + $"{candidate.TeePosition}. Players: {CourseManager.CountTotalActivePlayers()}.");
			return true;
		}

		private static void CollectPlatforms(GolfTeeManager manager, List<GolfTeeingPlatform> platforms, List<PlatformState> states, List<(bool HasNext, SpotTier NextTier, int Added)> view)
		{
			foreach (GolfTeeingPlatform allTeeingPlaform in manager.allTeeingPlaforms)
			{
				if (allTeeingPlaform.IsActive && allTeeingPlaform.teeingSpots != null)
				{
					PlatformState state = GetState(allTeeingPlaform);
					bool flag = state.Next < state.Candidates.Count;
					platforms.Add(allTeeingPlaform);
					states.Add(state);
					view.Add((flag, flag ? state.Candidates[state.Next].Tier : SpotTier.Squeeze, state.Added));
				}
			}
		}

		private static void LogOutOfSpots(List<GolfTeeingPlatform> platforms, List<PlatformState> states)
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < platforms.Count; i++)
			{
				GolfTeeingPlatform val = platforms[i];
				stringBuilder.Append($" [{((Object)val).name}: spots={val.teeingSpots.Length} free={val.GetAvailableTeeingSpotCount()} " + $"extrasAdded={states[i].Added} candidatesLeft={states[i].Candidates.Count - states[i].Next} " + $"skippedClose={states[i].SkippedClose}]");
			}
			Diag.Warn("Spawn", "No free spawn spot and no valid extra position. Context: every active tee platform is full " + $"(players={CourseManager.CountTotalActivePlayers()}, platforms={platforms.Count}).{stringBuilder} " + "Cause: no in-between, side, rear or squeeze candidate passed the ground and spacing checks. Resolution: the player will spectate as in vanilla; try SqueezeLevels=2, a higher MaxExtraSpotDistance or AllowElevatedRearSpots, and send this log.");
		}

		private static TeeingSpot Append(GolfTeeingPlatform platform, Candidate candidate)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			Quaternion rotation = ((Component)platform).transform.rotation;
			GolfTee teePrefab = platform.Settings.TeePrefab;
			Matrix4x4 val = Matrix4x4.TRS(candidate.TeePosition, rotation, Vector3.one);
			TeeingSpot val2 = new TeeingSpot
			{
				teeingPlatform = platform,
				teePrefab = teePrefab,
				teeWorldPosition = candidate.TeePosition,
				teeWorldRotation = rotation,
				playerWorldPosition = teePrefab.GetPlayerWorldSpawnPosition(val),
				playerWorldRotation = rotation
			};
			int num = platform.teeingSpots.Length;
			Array.Resize(ref platform.teeingSpots, num + 1);
			platform.teeingSpots[num] = val2;
			ExtraSpotTiers.Add(val2, new TierBox
			{
				Tier = candidate.Tier
			});
			return val2;
		}

		internal static void GetPreview(GolfTeeingPlatform platform, out IReadOnlyList<Candidate> candidates, out IReadOnlyList<string> report)
		{
			PlatformState state = GetState(platform);
			candidates = state.Candidates;
			report = state.Report;
		}

		internal static bool WasMeasuredWhileActive(GolfTeeingPlatform platform)
		{
			return GetState(platform).BuiltWhileActive;
		}

		private static PlatformState GetState(GolfTeeingPlatform platform)
		{
			if (States.TryGetValue(platform, out var value) && (value.BuiltWhileActive || !platform.IsActive))
			{
				return value;
			}
			PlatformState platformState = BuildState(platform);
			States.Remove(platform);
			States.Add(platform, platformState);
			return platformState;
		}

		private static PlatformState BuildState(GolfTeeingPlatform platform)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			PlatformState platformState = new PlatformState
			{
				BuiltWhileActive = platform.IsActive
			};
			TeeingPlatformSettings settings = platform.Settings;
			Transform transform = ((Component)platform).transform;
			List<float> vanillaLocalX = GetVanillaLocalX(platform);
			float spacing = ResolveSpacing(vanillaLocalX, settings);
			float y = transform.position.y;
			IReadOnlyList<SpotOffset> readOnlyList = SpotCandidates.GenerateForPlatform(vanillaLocalX, spacing, settings.FirstTeeOffset, Plugin.MaxExtraSpotDistance.Value, Plugin.AllowElevatedRearSpots.Value, Plugin.InBetweenSpots.Value, Plugin.SqueezeLevels.Value, Plugin.MinSpotGap.Value);
			List<Vector3> list = CollectOccupiedPositions();
			int[] rejected = new int[3];
			Dictionary<SpotTier, int> dictionary = new Dictionary<SpotTier, int>();
			foreach (SpotOffset item in readOnlyList)
			{
				Candidate? candidate = Evaluate(item, transform, settings, y, list, platformState.Report, rejected);
				if (candidate.HasValue)
				{
					platformState.Candidates.Add(candidate.Value);
					list.Add(candidate.Value.TeePosition);
					dictionary.TryGetValue(item.Tier, out var value);
					dictionary[item.Tier] = value + 1;
				}
			}
			LogCapacity(platform, vanillaLocalX.Count, dictionary, rejected);
			if (!platform.IsActive)
			{
				Diag.Info("Spawn", "note: " + ((Object)platform).name + " is inactive, so its deck colliders may be switched off and 'ground' rejections above can be false; it is measured again when it becomes active.");
			}
			return platformState;
		}

		private static Candidate? Evaluate(SpotOffset offset, Transform transform, TeeingPlatformSettings settings, float baseHeight, List<Vector3> occupied, List<string> report, int[] rejected)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_0018: 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_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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_00b0: 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_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = transform.position + transform.right * offset.X + transform.forward * offset.Z;
			string text = $"{offset.Tier} x={offset.X:0.0} z={offset.Z:0.0}";
			if (!TryFindGround(val, baseHeight, offset.Tier, out var groundY))
			{
				rejected[0]++;
				report.Add(text + ": rejected (no suitable ground)");
				return null;
			}
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(val.x, groundY + settings.TeeVerticalOffset, val.z);
			if (TooCloseToAny(val2, occupied, offset.MinGap))
			{
				rejected[1]++;
				report.Add(text + ": rejected (too close to another spot)");
				return null;
			}
			if (offset.Tier != SpotTier.InBetween && offset.Tier != SpotTier.Squeeze && Plugin.RequireClearLineOfFire.Value && IsLineBlocked(val2, transform.forward))
			{
				rejected[2]++;
				report.Add(text + ": rejected (line of fire blocked)");
				return null;
			}
			report.Add($"{text}: ACCEPTED at {val2}");
			return new Candidate(val2, offset.Tier, offset.MinGap);
		}

		private static void LogCapacity(GolfTeeingPlatform platform, int vanillaCount, Dictionary<SpotTier, int> accepted, int[] rejected)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (SpotTier value2 in Enum.GetValues(typeof(SpotTier)))
			{
				accepted.TryGetValue(value2, out var value);
				stringBuilder.Append($"{value2} {value}, ");
			}
			Diag.Info("Spawn", $"platform {((Object)platform).name}: vanilla spots {vanillaCount}; extras available: " + $"{stringBuilder.ToString().TrimEnd(',', ' ')}; rejected: ground {rejected[0]}, too close {rejected[1]}, " + $"line of fire {rejected[2]}");
		}

		private static List<float> GetVanillaLocalX(GolfTeeingPlatform platform)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			List<float> list = new List<float>();
			if (platform.teeingSpots == null)
			{
				return list;
			}
			Transform transform = ((Component)platform).transform;
			TeeingSpot[] teeingSpots = platform.teeingSpots;
			foreach (TeeingSpot val in teeingSpots)
			{
				if (!IsExtraSpot(val))
				{
					list.Add(Vector3.Dot(val.teeWorldPosition - transform.position, transform.right));
				}
			}
			return list;
		}

		private static float ResolveSpacing(List<float> vanillaX, TeeingPlatformSettings settings)
		{
			if (Plugin.ExtraSpotSpacing.Value > 0f)
			{
				return Plugin.ExtraSpotSpacing.Value;
			}
			if (vanillaX.Count > 1)
			{
				float num = float.MaxValue;
				float num2 = float.MinValue;
				foreach (float item in vanillaX)
				{
					num = Mathf.Min(num, item);
					num2 = Mathf.Max(num2, item);
				}
				return (num2 - num) / (float)(vanillaX.Count - 1);
			}
			return settings.DistanceBetweenTees;
		}

		private static bool TryFindGround(Vector3 flat, float baseHeight, SpotTier tier, out float groundY)
		{
			//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_0043: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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)
			groundY = 0f;
			bool flag = tier == SpotTier.ElevatedRear;
			float num = (flag ? (baseHeight + 12f + 1f) : (baseHeight + 2f));
			float num2 = (flag ? 13.75f : 5f);
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(new Vector3(flat.x, num, flat.z), Vector3.down, ref val, num2, -5, (QueryTriggerInteraction)1))
			{
				return false;
			}
			if (((RaycastHit)(ref val)).normal.y < 0.7f)
			{
				return false;
			}
			float num3 = ((RaycastHit)(ref val)).point.y - baseHeight;
			bool num4;
			if (!flag)
			{
				num4 = Mathf.Abs(num3) <= 0.75f;
			}
			else
			{
				if (!(num3 >= 1.5f))
				{
					goto IL_00b1;
				}
				num4 = num3 <= 12f;
			}
			if (num4)
			{
				groundY = ((RaycastHit)(ref val)).point.y;
				return true;
			}
			goto IL_00b1;
			IL_00b1:
			return false;
		}

		private static bool IsLineBlocked(Vector3 tee, Vector3 forward)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			return Physics.Raycast(tee + Vector3.up * 0.5f, forward, 10f, -5, (QueryTriggerInteraction)1);
		}

		private static List<Vector3> CollectOccupiedPositions()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			if (!SingletonBehaviour<GolfTeeManager>.HasInstance)
			{
				return list;
			}
			foreach (GolfTeeingPlatform allTeeingPlaform in SingletonBehaviour<GolfTeeManager>.Instance.allTeeingPlaforms)
			{
				if (allTeeingPlaform.teeingSpots != null)
				{
					TeeingSpot[] teeingSpots = allTeeingPlaform.teeingSpots;
					foreach (TeeingSpot val in teeingSpots)
					{
						list.Add(val.teeWorldPosition);
					}
				}
			}
			return list;
		}

		private static bool TooCloseToAny(Vector3 position, List<Vector3> others, float minGap)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			float num = minGap * minGap;
			foreach (Vector3 other in others)
			{
				Vector3 val = other - position;
				if (((Vector3)(ref val)).sqrMagnitude < num)
				{
					return true;
				}
			}
			return false;
		}
	}
	[BepInPlugin("com.sbg.moreplayersplus", "More Players Plus", "1.0.2")]
	[BepInIncompatibility("com.sulayre.moregolfers")]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "com.sbg.moreplayersplus";

		public const string NAME = "More Players Plus";

		public const string VERSION = "1.0.2";

		private const string MORE_GOLFERS_GUID = "com.sulayre.moregolfers";

		private const string SECTION_GENERAL = "General";

		private const string SECTION_SPAWNS = "Spawns";

		private const string SECTION_LISTS = "Lists";

		private const string SECTION_ADVANCED = "Advanced";

		private const int DEFAULT_MAX_PLAYERS = 32;

		private const int MIN_MAX_PLAYERS = 1;

		private const int MAX_MAX_PLAYERS = 100;

		private const float AUTO_SPACING = 0f;

		private const float DEFAULT_MAX_SIDE_SPOT_DISTANCE = 14f;

		private const int DEFAULT_ROWS_PER_PAGE = 16;

		internal static ManualLogSource Log;

		internal static ConfigEntry<int> MaxPlayersConfig;

		internal static ConfigEntry<float> ExtraSpotSpacing;

		internal static ConfigEntry<float> MaxExtraSpotDistance;

		internal static ConfigEntry<bool> RequireClearLineOfFire;

		internal static ConfigEntry<bool> AllowElevatedRearSpots;

		internal static ConfigEntry<bool> InBetweenSpots;

		internal static ConfigEntry<int> SqueezeLevels;

		internal static ConfigEntry<float> MinSpotGap;

		internal static ConfigEntry<bool> PaginateScoreboard;

		internal static ConfigEntry<bool> PaginatePauseMenu;

		internal static ConfigEntry<int> RowsPerPage;

		internal static ConfigEntry<bool> PublishPresence;

		internal static ConfigEntry<int> DebugOverflowAfterPlayers;

		internal static ConfigEntry<bool> LogPerformance;

		private void Awake()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0065: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			Harmony val = new Harmony("com.sbg.moreplayersplus");
			val.PatchAll();
			PatchLoadValuesLambda.Apply(val);
			PatchReserveSpotGuard.Apply(val);
			CreatePersistentComponents();
			Log.LogInfo((object)string.Format("{0} v{1} loaded. MaxPlayers={2}", "More Players Plus", "1.0.2", MaxPlayersConfig.Value));
			LogPatchedMethods(val);
		}

		private static void LogPatchedMethods(Harmony harmony)
		{
			List<string> list = new List<string>();
			foreach (MethodBase patchedMethod in harmony.GetPatchedMethods())
			{
				list.Add(patchedMethod.DeclaringType?.Name + "." + patchedMethod.Name);
			}
			list.Sort();
			Log.LogInfo((object)string.Format("[Startup] {0} game method(s) patched: {1}", list.Count, string.Join(", ", list)));
		}

		public static int GetCustomMaxPlayers()
		{
			return MaxPlayersConfig.Value;
		}

		public static int GetMaxTeamPlayers()
		{
			return TeamCap.Resolve(MaxPlayersConfig.Value);
		}

		private void BindConfig()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Expected O, but got Unknown
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Expected O, but got Unknown
			MaxPlayersConfig = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxPlayers", 32, new ConfigDescription("Maximum number of players allowed in a lobby (1-100). The game was built for 16: above about 32 it becomes laggy and unstable.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			ExtraSpotSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("Spawns", "ExtraSpotSpacing", 0f, new ConfigDescription("Distance in metres between extra spawn spots. 0 uses the game's own tee spacing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			MaxExtraSpotDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Spawns", "MaxSideSpotDistance", 14f, new ConfigDescription("Furthest a side, rear or elevated extra spawn spot may be from the tee platform, in metres. In-between and squeeze spots are always on the platform and ignore this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 150f), Array.Empty<object>()));
			RequireClearLineOfFire = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawns", "RequireClearLineOfFire", true, "Only use extra spawn spots with an unobstructed shot straight ahead, so a ball is not blocked by the tower or a wall on tee-off.");
			AllowElevatedRearSpots = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawns", "AllowElevatedRearSpots", true, "When there is not enough room beside the tee line, also use ground behind the platform that is clearly higher than it. Turn off to use only the tee line and the rows beside it.");
			InBetweenSpots = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawns", "InBetweenSpots", true, "When the normal spawn spots are full, use spots halfway between them on the same platform first (about half the normal spacing). They always have valid ground and the same line of fire as the normal spots.");
			SqueezeLevels = ((BaseUnityPlugin)this).Config.Bind<int>("Spawns", "SqueezeLevels", 1, new ConfigDescription("Last resort so nobody is sent to spectator: after every other spot is used, add spots between the in-between ones (1 = quarter steps, about 0.8 m; 2 = eighth steps, very tight). 0 turns it off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>()));
			MinSpotGap = ((BaseUnityPlugin)this).Config.Bind<float>("Spawns", "MinSpotGap", 0.8f, new ConfigDescription("The closest two squeeze spots may be to each other, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 3f), Array.Empty<object>()));
			PaginateScoreboard = ((BaseUnityPlugin)this).Config.Bind<bool>("Lists", "PaginateScoreboard", true, "Show the Tab scoreboard in pages when there are more players than RowsPerPage. Page with the mouse wheel or PageUp / PageDown while it is open.");
			PaginatePauseMenu = ((BaseUnityPlugin)this).Config.Bind<bool>("Lists", "PaginatePauseMenu", true, "Show the Esc menu player list in pages (kick, mute and volume stay reachable on every page).");
			RowsPerPage = ((BaseUnityPlugin)this).Config.Bind<int>("Lists", "RowsPerPage", 16, new ConfigDescription("How many players fit on one page of the scoreboard and Esc menu lists.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(4, 64), Array.Empty<object>()));
			PublishPresence = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "PublishPresence", true, "Tell the lobby host that you have this mod by storing its version in your Steam lobby member data. Vanilla players never read it. Lets a host that has this mod tell who can use features that need the mod on the player's own machine.");
			DebugOverflowAfterPlayers = ((BaseUnityPlugin)this).Config.Bind<int>("Advanced", "DebugOverflowAfterPlayers", 0, new ConfigDescription("Testing aid for the host: pretend the normal spawn spots are full once this many players have one, so extra spawn spots can be tested with a few real players. 0 turns it off. Leave at 0 for normal play.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			LogPerformance = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "LogPerformance", true, "In lobbies of 17 or more players, write a frame-time and network line to the BepInEx log every 30 seconds, and a line for every frame longer than a quarter of a second. Useful when reporting lag.");
		}

		private void CreatePersistentComponents()
		{
			//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_0010: Expected O, but got Unknown
			//IL_0010: 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)
			GameObject val = new GameObject("MorePlayersPlus");
			Object.DontDestroyOnLoad((Object)val);
			val.AddComponent<ListPager>();
			val.AddComponent<PresencePublisher>();
			val.AddComponent<DiagnosticsReporter>();
		}
	}
	internal static class PresenceRegistry
	{
		internal const string MEMBER_DATA_KEY = "mpp";

		internal static bool HasMod(int connectionId)
		{
			string version;
			return TryGetVersion(connectionId, out version);
		}

		internal static bool TryGetVersion(int connectionId, out string version)
		{
			//IL_003f: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			version = null;
			Lobby val = default(Lobby);
			if ((Object)(object)BNetworkManager.singleton == (Object)null || !BNetworkManager.singleton.playerGuidPerConnectionId.TryGetValue(connectionId, out var value) || !BNetworkManager.TryGetSteamLobby(ref val))
			{
				return false;
			}
			foreach (Friend member in ((Lobby)(ref val)).Members)
			{
				if (member.Id.Value == value)
				{
					string memberData = ((Lobby)(ref val)).GetMemberData(member, "mpp");
					version = (string.IsNullOrEmpty(memberData) ? null : memberData);
					return version != null;
				}
			}
			return false;
		}
	}
	internal sealed class PresencePublisher : MonoBehaviour
	{
		private const float CHECK_INTERVAL_SECONDS = 2f;

		private ulong lastPublishedLobbyId;

		private float nextCheckTime;

		private void Update()
		{
			if (Time.unscaledTime < nextCheckTime)
			{
				return;
			}
			nextCheckTime = Time.unscaledTime + 2f;
			try
			{
				Publish();
			}
			catch (Exception exception)
			{
				Diag.Error("Presence", "publishing the mod marker to the Steam lobby", exception);
			}
		}

		private void Publish()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			Lobby val = default(Lobby);
			if (Plugin.PublishPresence.Value && BNetworkManager.TryGetSteamLobby(ref val) && ((Lobby)(ref val)).Id.Value != lastPublishedLobbyId)
			{
				((Lobby)(ref val)).SetMemberData("mpp", "1.0.2");
				lastPublishedLobbyId = ((Lobby)(ref val)).Id.Value;
				Diag.Info("Presence", string.Format("Published mod marker ({0}) to Steam lobby {1}.", "1.0.2", ((Lobby)(ref val)).Id.Value));
			}
		}
	}
	internal static class PreviewSpotsCommand
	{
		private const float MARKER_SECONDS = 45f;

		private const float MARKER_SCALE = 0.6f;

		private const int DEFAULT_MARKER_COUNT = 12;

		[CCommand("morePlayersPreviewSpots", "Show where extra spawn spots would go: morePlayersPreviewSpots [count]", true, false)]
		private static void PreviewSpots(int count = 12)
		{
			if (!NetworkServer.active || !SingletonBehaviour<GolfTeeManager>.HasInstance)
			{
				Debug.Log((object)"[MorePlayersPlus] Run this as the host, inside a hole or the driving range.");
				return;
			}
			int num = 0;
			int num2 = 0;
			foreach (GolfTeeingPlatform allTeeingPlaform in SingletonBehaviour<GolfTeeManager>.Instance.allTeeingPlaforms)
			{
				num += PreviewPlatform(allTeeingPlaform, Mathf.Max(0, count - num));
				num2++;
			}
			Debug.Log((object)($"[MorePlayersPlus] Measured {num2} platform(s), showed {num} marker(s). " + "See LogOutput.log for every candidate and reason."));
		}

		private static int PreviewPlatform(GolfTeeingPlatform platform, int remaining)
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			OverflowSpotProvider.GetPreview(platform, out var candidates, out var report);
			string text = (OverflowSpotProvider.WasMeasuredWhileActive(platform) ? string.Empty : " [inactive platform: its colliders may be off, ground results unreliable]");
			ManualLogSource log = Plugin.Log;
			string text2 = $"[Preview] {((Object)platform).name} (active={platform.IsActive}, vanilla spots=";
			TeeingSpot[] teeingSpots = platform.teeingSpots;
			log.LogInfo((object)(text2 + $"{((teeingSpots != null) ? teeingSpots.Length : 0)}): {candidates.Count} accepted of {report.Count} tried" + text));
			foreach (string item in report)
			{
				Plugin.Log.LogInfo((object)("[Preview]   " + item));
			}
			int num = 0;
			foreach (OverflowSpotProvider.Candidate item2 in candidates)
			{
				if (num >= remaining)
				{
					break;
				}
				CreateMarker(item2.TeePosition, item2.Tier);
				num++;
			}
			return num;
		}

		private static void CreateMarker(Vector3 position, SpotTier tier)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)0);
			((Object)obj).name = "MorePlayersPlus preview marker";
			obj.transform.position = position;
			obj.transform.localScale = Vector3.one * 0.6f;
			Collider component = obj.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			Renderer component2 = obj.GetComponent<Renderer>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.material.color = TierColor(tier);
			}
			Object.Destroy((Object)(object)obj, 45f);
		}

		private static Color TierColor(SpotTier tier)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			return (Color)(tier switch
			{
				SpotTier.InBetween => Color.white, 
				SpotTier.SideLine => Color.green, 
				SpotTier.StaggeredRear => Color.yellow, 
				SpotTier.Squeeze => Color.red, 
				_ => Color.cyan, 
			});
		}
	}
	internal static class ProbeCommand
	{
		private const string PREFIX = "[Probe] ";

		private const int MAX_BALLS_LISTED = 3;

		private const int MAX_PLAYERS_LISTED = 40;

		private const int VANILLA_MAX_PLAYERS = 16;

		private const string SHADER_INDEX_PROPERTY = "_CameraFadePlayerIndex";

		[CCommand("morePlayersProbe", "Write physics, network and tee facts to LogOutput.log", false, false)]
		private static void Probe()
		{
			WriteAll("console command");
			Debug.Log((object)"[MorePlayersPlus] Probe written to BepInEx/LogOutput.log (search for '[Probe]').");
		}

		internal static void WriteAll(string reason)
		{
			Write("==== MorePlayersPlus probe (" + reason + ") ====");
			Section("session", WriteSession);
			Section("layers", WriteLayers);
			Section("balls", WriteBalls);
			Section("players", WritePlayers);
			Section("network", WriteNetwork);
			Section("steam networking", WriteSteamNetworking);
			Section("swing hit box", WriteSwingHitBox);
			Section("tee platforms", WriteTeePlatforms);
			Section("presence", WritePresence);
			Write("==== end of probe ====");
		}

		private static void Section(string name, Action write)
		{
			try
			{
				write();
			}
			catch (Exception exception)
			{
				Diag.Error("Probe", "writing the '" + name + "' section", exception);
			}
		}

		private static void WriteSession()
		{
			Write(string.Format("plugin={0} server={1} client={2} ", "1.0.2", NetworkServer.active, NetworkClient.active) + "lobby=" + LobbyKind() + " matchState=" + SafeMatchState() + " players=" + SafePlayerCount() + " " + $"lobbyMax={BNetworkManager.MaxPlayers}");
			bool hasInstance = SingletonNetworkBehaviour<MatchSetupRules>.HasInstance;
			Write("HitOtherPlayersBalls rule = " + (hasInstance ? MatchSetupRules.GetValueAsBool((Rule)10).ToString() : "n/a"));
		}

		private static void WriteLayers()
		{
			LayerSettings layerSettings = GameManager.LayerSettings;
			if ((Object)(object)layerSettings == (Object)null)
			{
				Write("LayerSettings unavailable");
				return;
			}
			WritePair("StationaryBall", layerSettings.StationaryBallLayer, "StationaryBall", layerSettings.StationaryBallLayer);
			WritePair("DynamicBall", layerSettings.DynamicBallLayer, "DynamicBall", layerSettings.DynamicBallLayer);
			WritePair("StationaryBall", layerSettings.StationaryBallLayer, "DynamicBall", layerSettings.DynamicBallLayer);
			WritePair("StationaryBall", layerSettings.StationaryBallLayer, "Player", layerSettings.PlayerLayer);
			WritePair("DynamicBall", layerSettings.DynamicBallLayer, "Player", layerSettings.PlayerLayer);
			WritePair("Player", layerSettings.PlayerLayer, "Player", layerSettings.PlayerLayer);
			WritePair("StationaryBall", layerSettings.StationaryBallLayer, "Hittables", layerSettings.HittablesLayer);
			WritePair("DynamicBall", layerSettings.DynamicBallLayer, "Hittables", layerSettings.HittablesLayer);
		}

		private static void WritePair(string nameA, int layerA, string nameB, int layerB)
		{
			Write($"layers {nameA}({layerA}) x {nameB}({layerB}): ignoreCollision={Physics.GetIgnoreLayerCollision(layerA, layerB)}");
		}

		private static void WriteBalls()
		{
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			GolfBall[] array = Object.FindObjectsByType<GolfBall>((FindObjectsSortMode)0);
			Write($"golf balls in scene: {array.Length}");
			for (int i = 0; i < array.Length && i < 3; i++)
			{
				GolfBall val = array[i];
				Rigidbody rigidbody = val.Rigidbody;
				Collider componentInChildren = ((Component)val).GetComponentInChildren<Collider>();
				Hittable component = ((Component)val).GetComponent<Hittable>();
				PredictedRigidbody predictedRigidbody = val.AsEntity.PredictedRigidbody;
				Write($"ball[{i}] layer={LayerName(((Component)val).gameObject.layer)} kinematic={(Object)(object)rigidbody != (Object)null && rigidbody.isKinematic} " + "collider=" + (((Object)(object)componentInChildren != (Object)null) ? ((object)componentInChildren).GetType().Name : "none") + " canBeHitBySwingProjectiles=" + (((Object)(object)component != (Object)null) ? component.ProjectileSettings.CanBeHitBySwingProjectiles.ToString() : "n/a"));
				if ((Object)(object)predictedRigidbody != (Object)null)
				{
					Write($"ball[{i}] predicted: mode={predictedRigidbody.mode} positionCorrectionThreshold={predictedRigidbody.positionCorrectionThreshold} " + $"snapThreshold={predictedRigidbody.snapThreshold} ghostVelocityThreshold={predictedRigidbody.ghostVelocityThreshold}");
				}
			}
		}

		private static void WritePlayers()
		{
			List<PlayerInfo> list = new List<PlayerInfo>();
			if ((Object)(object)GameManager.LocalPlayerInfo != (Object)null)
			{
				list.Add(GameManager.LocalPlayerInfo);
			}
			list.AddRange(GameManager.RemotePlayers);
			Write($"players known to this machine: {list.Count}");
			for (int i = 0; i < list.Count && i < 40; i++)
			{
				WritePlayer(i, list[i]);
			}
		}

		private static void WritePlayer(int ordinal, PlayerInfo info)
		{
			PlayerMovement movement = info.Movement;
			Rigidbody component = ((Component)movement).GetComponent<Rigidbody>();
			int num = 0;
			int num2 = 0;
			float num3 = float.NaN;
			Renderer[] componentsInChildren = ((Component)info).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer obj in componentsInChildren)
			{
				num++;
				if (obj.isVisible)
				{
					num2++;
				}
				Material sharedMaterial = obj.sharedMaterial;
				if (float.IsNaN(num3) && (Object)(object)sharedMaterial != (Object)null && sharedMaterial.HasProperty("_CameraFadePlayerIndex"))
				{
					num3 = sharedMaterial.GetFloat("_CameraFadePlayerIndex");
				}
			}
			string arg = (float.IsNaN(num3) ? "n/a" : num3.ToString("0"));
			string arg2 = ((!float.IsNaN(num3) && num3 >= 16f) ? " (>=16)" : string.Empty);
			Write(string.Format("player #{0} {1} layer={2} ", ordinal, ((NetworkBehaviour)info).isLocalPlayer ? "LOCAL" : "remote", LayerName(((Component)movement).gameObject.layer)) + $"kinematic={(Object)(object)component != (Object)null && component.isKinematic} uprightEnabled={((Collider)movement.UprightCollider).enabled} " + $"hittableEnabled={((Collider)movement.HittableCollider).enabled} shaderIndex={arg}{arg2} " + $"renderers={num} visible={num2} " + $"canBeHitBySwingProjectiles={info.AsHittable.ProjectileSettings.CanBeHitBySwingProjectiles}");
		}

		private static void WriteNetwork()
		{
			NetworkManager singleton = NetworkManager.singleton;
			Write("network: sendRate=" + (((Object)(object)singleton != (Object)null) ? singleton.sendRate.ToString() : "n/a") + " " + string.Format("serverTickRate={0} maxConnections={1} ", NetworkServer.tickRate, ((Object)(object)singleton != (Object)null) ? singleton.maxConnections.ToString() : "n/a") + $"fixedDeltaTime={Time.fixedDeltaTime} solverIterations={Physics.defaultSolverIterations} " + $"targetFrameRate={Application.targetFrameRate} vSync={QualitySettings.vSyncCount}");
		}

		private static void WriteSteamNetworking()
		{
			Write($"steam sockets: SendBufferSize={SteamNetworkingUtils.SendBufferSize} " + $"SendRateMin={SteamNetworkingUtils.SendRateMin} SendRateMax={SteamNetworkingUtils.SendRateMax} " + $"NagleTime={SteamNetworkingUtils.NagleTime} Timeout={SteamNetworkingUtils.Timeout}");
		}

		private static void WriteSwingHitBox()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Write($"swing hit box size={GameManager.GolfSettings.GetLocalPlayerEffectiveSwingHitBoxSize()} " + $"center={GameManager.GolfSettings.GetLocalPlayerEffectiveSwingHitBoxLocalCenter()}");
		}

		private static void WriteTeePlatforms()
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if (!SingletonBehaviour<GolfTeeManager>.HasInstance)
			{
				Write("no GolfTeeManager in this scene");
				return;
			}
			foreach (GolfTeeingPlatform allTeeingPlaform in SingletonBehaviour<GolfTeeManager>.Instance.allTeeingPlaforms)
			{
				TeeingPlatformSettings settings = allTeeingPlaform.Settings;
				int num = ((allTeeingPlaform.teeingSpots != null) ? allTeeingPlaform.teeingSpots.Length : (-1));
				int num2 = ((allTeeingPlaform.teeingSpots != null) ? allTeeingPlaform.GetAvailableTeeingSpotCount() : (-1));
				Write($"platform {((Object)allTeeingPlaform).name}: active={allTeeingPlaform.IsActive} minPlayers={allTeeingPlaform.MinPlayerCount} " + $"pos={((Component)allTeeingPlaform).transform.position} spots={num} free={num2} (server-only) maxTee={settings.MaxTeeCount} " + $"spacing={settings.DistanceBetweenTees} firstOffset={settings.FirstTeeOffset}");
			}
		}

		private static void WritePresence()
		{
			if (!NetworkServer.active)
			{
				Write("presence: host-only information (not the server)");
				return;
			}
			foreach (KeyValuePair<int, ulong> item in BNetworkManager.singleton.playerGuidPerConnectionId)
			{
				string version;
				bool flag = PresenceRegistry.TryGetVersion(item.Key, out version);
				Write(string.Format("presence: connection {0} -> {1}", item.Key, flag ? ("mod " + version) : "no mod marker"));
			}
		}

		private static string SafeMatchState()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (!SingletonNetworkBehaviour<CourseManager>.HasInstance)
			{
				return "n/a";
			}
			return ((object)CourseManager.MatchState/*cast due to .constrained prefix*/).ToString();
		}

		private static string SafePlayerCount()
		{
			if (!SingletonNetworkBehaviour<CourseManager>.HasInstance)
			{
				return "n/a";
			}
			return CourseManager.CountTotalActivePlayers().ToString();
		}

		private static string LobbyKind()
		{
			Lobby val = default(Lobby);
			if (!BNetworkManager.TryGetSteamLobby(ref val))
			{
				return "none";
			}
			return "steam";
		}

		private static string LayerName(int layer)
		{
			return $"{LayerMask.LayerToName(layer)}({layer})";
		}

		private static void Write(string message)
		{
			Plugin.Log.LogInfo((object)("[Probe] " + message));
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MorePlayersPlus";

		public const string PLUGIN_NAME = "More Players Plus";

		public const string PLUGIN_VERSION = "1.0.2";
	}
}
namespace MorePlayersPlus.Patches
{
	[HarmonyPatch(typeof(Scoreboard), "Refresh")]
	internal static class PatchScoreboardPaging
	{
		private const string AREA = "Lists";

		[HarmonyPostfix]
		private static void Postfix(Scoreboard __instance)
		{
			try
			{
				if (Plugin.PaginateScoreboard.Value)
				{
					int num = Mathf.Max(CountActive(__instance.entries), Mathf.Max(CountActive(__instance.redTeamEntries), CountActive(__instance.blueTeamEntries)));
					PageWindow page = PageWindow.Compute(num, Plugin.RowsPerPage.Value, PagingState.ScoreboardPage);
					PagingState.ScoreboardPage = page.Page;
					PagingState.ScoreboardPageCount = page.PageCount;
					PagingState.ScoreboardLabel = page.Label;
					PagingState.LogChange("Lists", "scoreboard", num, page.PageCount, ref PagingState.LastScoreboardLogged);
					PagingState.HideOutsidePage(__instance.entries, page);
					PagingState.HideOutsidePage(__instance.redTeamEntries, page);
					PagingState.HideOutsidePage(__instance.blueTeamEntries, page);
				}
			}
			catch (Exception exception)
			{
				Diag.Error("Lists", "paging the Tab scoreboard", exception);
			}
		}

		private static int CountActive(List<ScoreboardEntry> entries)
		{
			int num = 0;
			foreach (ScoreboardEntry entry in entries)
			{
				if ((Object)(object)entry != (Object)null && ((Component)entry).gameObject.activeSelf)
				{
					num++;
				}
			}
			return num;
		}
	}
	[HarmonyPatch(typeof(PauseMenu), "UpdatePlayerEntries")]
	internal static class PatchPauseMenuPaging
	{
		private const string AREA = "Lists";

		[HarmonyPostfix]
		private static void Postfix(PauseMenu __instance)
		{
			try
			{
				if (Plugin.PaginatePauseMenu.Value && __instance.isPaused)
				{
					Apply(__instance);
				}
			}
			catch (Exception exception)
			{
				Diag.Error("Lists", "paging the Esc player list", exception);
			}
		}

		private static void Apply(PauseMenu menu)
		{
			List<PauseMenuPlayerEntry> list = new List<PauseMenuPlayerEntry>();
			foreach (PauseMenuPlayerEntry playerEntry in menu.playerEntries)
			{
				if ((Object)(object)playerEntry != (Object)null && ((Component)playerEntry).gameObject.activeSelf)
				{
					list.Add(playerEntry);
				}
			}
			PageWindow pageWindow = PageWindow.Compute(list.Count, Plugin.RowsPerPage.Value, PagingState.PausePage);
			PagingState.PausePage = pageWindow.Page;
			PagingState.PausePageCount = pageWindow.PageCount;
			PagingState.PauseLabel = pageWindow.Label;
			PagingState.LogChange("Lists", "pause menu", list.Count, pageWindow.PageCount, ref PagingState.LastPauseLogged);
			if (!pageWindow.IsPaged)
			{
				return;
			}
			List<PauseMenuPlayerEntry> list2 = new List<PauseMenuPlayerEntry>();
			for (int i = 0; i < list.Count; i++)
			{
				bool flag = pageWindow.Contains(i);
				((Component)list[i]).gameObject.SetActive(flag);
				if (flag)
				{
					list2.Add(list[i]);
				}
			}
			RelinkNavigation(menu, list2);
		}

		private static void RelinkNavigation(PauseMenu menu, List<PauseMenuPlayerEntry> visible)
		{
			//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_0037: Unknown result type (might be due to invalid IL or missing references)
			if (visible.Count != 0)
			{
				Button[] allButtons = menu.allButtons;
				foreach (Button obj in allButtons)
				{
					Navigation navigation = ((Selectable)obj).navigation;
					((Navigation)(ref navigation)).selectOnRight = (Selectable)(object)visible[0].volumeSlider.Slider;
					((Selectable)obj).navigation = navigation;
				}
				for (int j = 1; j < visible.Count; j++)
				{
					visible[j].SetVerticalNavigationTarget(visible[j - 1], true);
					visible[j - 1].SetVerticalNavigationTarget(visible[j], false);
				}
				PauseMenuPlayerEntry val = visible[0];
				PauseMenuPlayerEntry val2 = visible[visible.Count - 1];
				if (visible.Count == 1)
				{
					val.SetVerticalNavigationTarget((Selectable)(object)menu.exitGameButton, true);
					val.SetVerticalNavigationTarget((Selectable)(object)menu.resumeButton, false);
				}
				else
				{
					val2.SetVerticalNavigationTarget(val, false);
					val.SetVerticalNavigationTarget(val2, true);
				}
			}
		}
	}
	internal static class PagingState
	{
		internal static int ScoreboardPage;

		internal static int ScoreboardPageCount = 1;

		internal static string ScoreboardLabel = string.Empty;

		internal static int PausePage;

		internal static int PausePageCount = 1;

		internal static string PauseLabel = string.Empty;

		internal static long LastScoreboardLogged = -1L;

		internal static long LastPauseLogged = -1L;

		internal static void HideOutsidePage(List<ScoreboardEntry> entries, PageWindow page)
		{
			for (int i = 0; i < entries.Count; i++)
			{
				ScoreboardEntry val = entries[i];
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf && !page.Contains(i))
				{
					((Component)val).gameObject.SetActive(false);
				}
			}
		}

		internal static void LogChange(string area, string list, int players, int pages, ref long last)
		{
			long num = ((long)players << 32) | (uint)pages;
			if (num != last)
			{
				last = num;
				Diag.Info(area, $"{list}: {players} players, {pages} page(s) of {Plugin.RowsPerPage.Value}");
			}
		}
	}
	internal static class PatchLoadValuesLambda
	{
		private static bool _anyLambdaPatched;

		internal static void Apply(Harmony harmony)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			List<MethodInfo> list = (from m in typeof(MatchSetupMenu).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic)
				where m.Name.Contains("<LoadValues>b__")
				select m).ToList();
			if (list.Count == 0)
			{
				Plugin.Log.LogError((object)"[PatchLoadValuesLambda] No '<LoadValues>b__*' method found on MatchSetupMenu. The server-side maxPlayers cap of 16 will not be removed. The game may have restructured MatchSetupMenu since this mod was built.");
				return;
			}
			HarmonyMethod val = new HarmonyMethod(typeof(PatchLoadValuesLambda).GetMethod("Transpiler", BindingFlags.Static | BindingFlags.NonPublic));
			_anyLambdaPatched = false;
			foreach (MethodInfo item in list)
			{
				Plugin.Log.LogInfo((object)("[PatchLoadValuesLambda] Patching: " + item.Name));
				harmony.Patch((MethodBase)item, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (!_anyLambdaPatched)
			{
				Plugin.Log.LogError((object)"[PatchLoadValuesLambda] Ldc_I4_S 16 was not found in any LoadValues lambda. The server-side maxPlayers cap of 16 will NOT be removed.");
			}
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			List<CodeInstruction> list = instructions.ToList();
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Ldc_I4_S && CodeInstructionExtensions.OperandIs(list[i], (object)16))
				{
					list[i].opcode = OpCodes.Call;
					list[i].operand = typeof(Plugin).GetMethod("GetCustomMaxPlayers");
					num++;
					if (num >= 2)
					{
						break;
					}
				}
			}
			if (num > 0)
			{
				_anyLambdaPatched = true;
				Plugin.Log.LogInfo((object)($"[PatchLoadValuesLambda] {__originalMethod.Name}: replaced {num} literal(s) of 16 " + $"with a live read of MaxPlayers (currently {Plugin.GetCustomMaxPlayers()})."));
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(MatchSetupMenu), "OnStartClient")]
	internal static class PatchOnStartClient
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			bool found = false;
			foreach (CodeInstruction instruction in instructions)
			{
				if (!found && instruction.opcode == OpCodes.Ldc_R4 && CodeInstructionExtensions.OperandIs(instruction, (object)16f))
				{
					found = true;
					yield return new CodeInstruction(OpCodes.Call, (object)typeof(Plugin).GetMethod("GetCustomMaxPlayers"));
					yield return new CodeInstruction(OpCodes.Conv_R4, (object)null);
				}
				else
				{
					yield return instruction;
				}
			}
			if (found)
			{
				Plugin.Log.LogInfo((object)$"[PatchOnStartClient] Slider upper limit patched: 16 -> {Plugin.GetCustomMaxPlayers()}.");
			}
			else
			{
				Plugin.Log.LogError((object)"[PatchOnStartClient] Could not find Ldc_R4 16f in OnStartClient. The slider upper limit was not patched.");
			}
		}
	}
	[HarmonyPatch(typeof(GolfTeeManager), "GetAvailableTeeingSpotInternal")]
	internal static class PatchOverflowSpots
	{
		private const int VANILLA_MAX_PLAYERS = 16;

		private const string AREA = "Spawn";

		private static bool _loggedLargeLobby;

		[HarmonyPrefix]
		private static bool Prefix(GolfTeeManager __instance, ref TeeingSpot __result)
		{
			try
			{
				if (!NetworkServer.active)
				{
					return true;
				}
				LogLargeLobbyOnce();
				if (OverflowSpotProvider.IsDebugExhausted(__instance) && OverflowSpotProvider.TryGetDebugSpot(__instance, out var spot))
				{
					__result = spot;
					return false;
				}
				OverflowSpotProvider.EnsureFreeSpot(__instance);
			}
			catch (Exception exception)
			{
				Diag.Error("Spawn", "choosing an overflow spawn spot", exception);
			}
			return true;
		}

		[HarmonyPostfix]
		private static void Postfix(TeeingSpot __result)
		{
			try
			{
				if (NetworkServer.active)
				{
					LogResult(__result);
				}
			}
			catch (Exception exception)
			{
				Diag.Error("Spawn", "logging the spawn result", exception);
			}
		}

		private static void LogResult(TeeingSpot spot)
		{
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			int num = CourseManager.CountTotalActivePlayers();
			if (spot == null)
			{
				Diag.Warn("Spawn", $"NO SPOT available for a joining player (players={num}); that player will spectate.");
				return;
			}
			GolfTeeingPlatform teeingPlatform = spot.teeingPlatform;
			int num2 = (((Object)(object)teeingPlatform != (Object)null && teeingPlatform.teeingSpots != null) ? Array.IndexOf(teeingPlatform.teeingSpots, spot) : (-1));
			int num3 = (((Object)(object)teeingPlatform != (Object)null && teeingPlatform.teeingSpots != null) ? teeingPlatform.teeingSpots.Length : (-1));
			string text = OverflowSpotProvider.DescribeSpot(spot);
			Diag.Info("Spawn", string.Format("spot given: players={0} platform={1} ", num, ((Object)(object)teeingPlatform != (Object)null) ? ((Object)teeingPlatform).name : "?") + $"index={num2}/{num3} kind={text} pos={spot.teeWorldPosition}");
		}

		private static void LogLargeLobbyOnce()
		{
			if (!_loggedLargeLobby && CourseManager.CountTotalActivePlayers() > 16)
			{
				_loggedLargeLobby = true;
				Diag.Info("Diagnostics", $"{CourseManager.CountTotalActivePlayers()} players in the match (vanilla maximum " + $"{16}). If characters beyond the 16th are invisible, please report it: " + "the per-player shader arrays in the game are sized 128, but the shader's own limit is unverified. See the [Probe] lines for each player's shader index.");
			}
		}
	}
	internal static class PatchReserveSpotGuard
	{
		private const string LOCAL_FUNCTION_MARKER = "g__ReserveTeeingSpotFor|";

		internal static void Apply(Harmony harmony)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			MethodInfo methodInfo = typeof(CourseManager).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic).FirstOrDefault((MethodInfo methodInfo2) => methodInfo2.Name.Contains("g__ReserveTeeingSpotFor|"));
			if (methodInfo == null)
			{
				Diag.Warn("Spawn", "Could not find the 'ReserveTeeingSpotFor' local function on CourseManager. Running out of spawn spots will throw as it does in vanilla.");
				return;
			}
			MethodInfo method = typeof(PatchReserveSpotGuard).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic);
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Diag.Info("Spawn", "Guarding " + methodInfo.Name);
		}

		private static bool Prefix()
		{
			try
			{
				if (!SingletonBehaviour<GolfTeeManager>.HasInstance)
				{
					return true;
				}
				bool num = OverflowSpotProvider.EnsureFreeSpot(SingletonBehaviour<GolfTeeManager>.Instance);
				if (!num)
				{
					Diag.Warn("Spawn", "Skipped a spawn reservation: no free spot. That player will spectate.");
				}
				return num;
			}
			catch (Exception exception)
			{
				Diag.Error("Spawn", "guarding a spawn reservation", exception);
				return true;
			}
		}
	}
	[HarmonyPatch]
	internal static class PatchTeamCap
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(MatchSetupMenu), "CanSetTeam", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(MatchSetupMenu), "DisplayTeamChangeFailureMessage", (Type[])null, (Type[])null);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			MethodInfo replacement = AccessTools.Method(typeof(Plugin), "GetMaxTeamPlayers", (Type[])null, (Type[])null);
			bool replaced = false;
			foreach (CodeInstruction instruction in instructions)
			{
				if (!replaced && instruction.opcode == OpCodes.Ldc_I4_S && Convert.ToInt32(instruction.operand) == 15)
				{
					instruction.opcode = OpCodes.Call;
					instruction.operand = replacement;
					replaced = true;
				}
				yield return instruction;
			}
			if (replaced)
			{
				Plugin.Log.LogInfo((object)("[PatchTeamCap] MatchSetupMenu." + __originalMethod.Name + ": team limit " + $"{15} -> follows MaxPlayers."));
				yield break;
			}
			Plugin.Log.LogError((object)($"[PatchTeamCap] Could not find the literal {15} in " + "MatchSetupMenu." + __originalMethod.Name + ". Teams mode stays limited to " + $"{15} per team."));
		}
	}
	[HarmonyPatch(typeof(MatchSetupMenu), "OnMaxPlayersSliderValueChange")]
	internal static class PatchWarningUI
	{
		private const int WARNING_THRESHOLD = 32;

		private const string WARNING_TEXT = "Warning: More than 32 players may cause serious instability.";

		private static string _originalText;

		private static void Postfix(MatchSetupMenu __instance)
		{
			GameObject playerCountRecommendation = __instance.playerCountRecommendation;
			if ((Object)(object)playerCountRecommendation == (Object)null)
			{
				return;
			}
			TMP_Text componentInChildren = playerCountRecommendation.GetComponentInChildren<TMP_Text>(true);
			if (!((Object)(object)componentInChildren == (Object)null))
			{
				if (_originalText == null)
				{
					_originalText = componentInChildren.text;
				}
				if (__instance.maxPlayersSlider.value > 32f)
				{
					componentInChildren.text = "Warning: More than 32 players may cause serious instability.";
				}
				else if (_originalText != null)
				{
					componentInChildren.text = _originalText;
				}
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}