Decompiled source of Gamemode Lib v0.2.1

com.github.glarmer.Gamemode_Lib.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.CompilerServices;
using Gamemode_Lib.ConfigSync;
using Gamemode_Lib.Events;
using Gamemode_Lib.Network.Messages;
using Gamemode_Lib.Patches;
using Gamemode_Lib.Patches.Features;
using Gamemode_Lib.Teams;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Pool;
using UnityEngine.SceneManagement;
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("com.github.glarmer.Gamemode_Lib")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.1.0")]
[assembly: AssemblyInformationalVersion("0.5.1+9d9bdcb58e0a52d8479020bb6fdc773de9ffa3c1")]
[assembly: AssemblyProduct("com.github.glarmer.Gamemode_Lib")]
[assembly: AssemblyTitle("Gamemode_Lib")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace Microsoft.CodeAnalysis
{
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace Gamemode_Lib
{
	public static class GameModeUtilities
	{
		public static Dictionary<string, IGamemode> Modes { get; } = new Dictionary<string, IGamemode>();

		public static string? CurrentGamemodeId { get; set; }

		public static bool GameEnded { get; set; } = true;

		public static void ReinitializeGameState()
		{
			string currentGamemodeId = CurrentGamemodeId;
			Plugin.Log.LogInfo((object)($"[GamemodeLib] ReinitializeGameState: serverActive={NetworkServer.active} clientActive={NetworkClient.active} " + string.Format("gameEnded={0} currentGamemodeId='{1}'", GameEnded, currentGamemodeId ?? "<null>")));
			if (currentGamemodeId != null && Modes.TryGetValue(currentGamemodeId, out IGamemode value) && value != null)
			{
				try
				{
					Harmony gamemodeHarmony = value.GamemodeHarmony;
					if (gamemodeHarmony != null)
					{
						gamemodeHarmony.UnpatchSelf();
					}
				}
				catch (Exception arg)
				{
					Plugin.Log.LogWarning((object)$"[GamemodeLib] ReinitializeGameState: failed to unpatch gamemode harmony for '{currentGamemodeId}': {arg}");
				}
			}
			ConfigSyncManager.Instance?.ClearAllScopes();
			bool active = NetworkServer.active;
			TeamManager.Instance?.ResetToDefaults(active);
			CurrentGamemodeId = null;
			GameEnded = true;
		}

		public static void RegisterGameMode(IGamemode gamemode)
		{
			if (Modes != null)
			{
				if (Modes.ContainsKey(gamemode.GameModeId))
				{
					Plugin.Log.LogError((object)("Gamemode with same ID: " + gamemode.GameModeId + " has already been registered! We will not re-register..."));
					return;
				}
				Modes.Add(gamemode.GameModeId, gamemode);
				_ = gamemode.IsTeamBased;
				Plugin.Log.LogInfo((object)("Gamemode ID: " + gamemode.GameModeId + " has been registered!"));
			}
			else
			{
				Plugin.Log.LogError((object)("Gamemode dictionary was null! " + gamemode.Name + " is not loaded..."));
			}
		}

		public static void ApplyGamemodeStartMessage(GamemodeStartMessage message)
		{
			Plugin.Log.LogInfo((object)($"[GamemodeLib] ApplyGamemodeStartMessage: serverActive={NetworkServer.active} clientActive={NetworkClient.active} " + string.Format("gameEnded={0} currentGamemodeId='{1}' msgGamemodeId='{2}'", GameEnded, CurrentGamemodeId ?? "<null>", message.GamemodeId ?? "<null>")));
			if (NetworkServer.active)
			{
				Plugin.Log.LogInfo((object)"[GamemodeLib] ApplyGamemodeStartMessage: ignoring because server is active");
				return;
			}
			if (message.GamemodeId == null)
			{
				Plugin.Log.LogError((object)"[GamemodeLib] Received GamemodeStartMessage with null GamemodeId");
				return;
			}
			if (!Modes.TryGetValue(message.GamemodeId, out IGamemode value) || value == null)
			{
				Plugin.Log.LogError((object)$"[GamemodeLib] Received GamemodeStartMessage for unknown mode id '{message.GamemodeId}'. modesCount={Modes.Count}");
				return;
			}
			Plugin.Log.LogInfo((object)("[GamemodeLib] ApplyGamemodeStartMessage: starting mode '" + message.GamemodeId + "'. " + string.Format("previousState: gameEnded={0} currentGamemodeId='{1}'", GameEnded, CurrentGamemodeId ?? "<null>")));
			CurrentGamemodeId = message.GamemodeId;
			GameEnded = false;
			if ((Object)(object)ConfigSyncManager.Instance != (Object)null && !ConfigSyncManager.Instance.HasReceivedFull(message.GamemodeId))
			{
				ConfigSyncManager.Instance.RequestScopeFromHost(message.GamemodeId);
			}
			try
			{
				value.OnGameStart();
				Plugin.Log.LogInfo((object)("[GamemodeLib] ApplyGamemodeStartMessage: mode.OnGameStart() finished for '" + message.GamemodeId + "'"));
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[GamemodeLib] ApplyGamemodeStartMessage: mode.OnGameStart() threw for '{message.GamemodeId}': {arg}");
				throw;
			}
		}

		public static void ApplyGamemodeEndMessage(GamemodeEndMessage message)
		{
			Plugin.Log.LogInfo((object)($"[GamemodeLib] ApplyGamemodeEndMessage: serverActive={NetworkServer.active} clientActive={NetworkClient.active} " + string.Format("gameEnded={0} currentGamemodeId='{1}' msgGamemodeId='{2}'", GameEnded, CurrentGamemodeId ?? "<null>", message.GamemodeId ?? "<null>")));
			if (NetworkServer.active)
			{
				Plugin.Log.LogInfo((object)"[GamemodeLib] ApplyGamemodeEndMessage: ignoring because server is active");
				return;
			}
			if (GameEnded)
			{
				Plugin.Log.LogInfo((object)"[GamemodeLib] ApplyGamemodeEndMessage: ignoring because GameEnded is already true");
				return;
			}
			if (message.GamemodeId == null)
			{
				Plugin.Log.LogError((object)"[GamemodeLib] Received GamemodeEndMessage with null GamemodeId");
				return;
			}
			if (!Modes.TryGetValue(message.GamemodeId, out IGamemode value) || value == null)
			{
				Plugin.Log.LogError((object)("[GamemodeLib] Received GamemodeEndMessage for unknown mode id '" + message.GamemodeId + "'. " + $"modesCount={Modes.Count}. Forcing local end state."));
				GameEnded = true;
				CurrentGamemodeId = null;
				return;
			}
			Plugin.Log.LogInfo((object)("[GamemodeLib] ApplyGamemodeEndMessage: ending mode '" + message.GamemodeId + "'. " + string.Format("previousState: gameEnded={0} currentGamemodeId='{1}'", GameEnded, CurrentGamemodeId ?? "<null>")));
			CurrentGamemodeId = message.GamemodeId;
			GameEnded = true;
			try
			{
				value.OnGameEnd();
				Plugin.Log.LogInfo((object)("[GamemodeLib] ApplyGamemodeEndMessage: mode.OnGameEnd() finished for '" + message.GamemodeId + "'"));
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[GamemodeLib] ApplyGamemodeEndMessage: mode.OnGameEnd() threw for '{message.GamemodeId}': {arg}");
				throw;
			}
			CurrentGamemodeId = null;
			ConfigSyncManager.Instance?.ClearScope(message.GamemodeId);
			Plugin.Log.LogInfo((object)"[GamemodeLib] ApplyGamemodeEndMessage: cleared CurrentGamemodeId and set GameEnded=true");
		}

		internal static void TryEndCurrentGame(bool broadcastToClients)
		{
			Plugin.Log.LogInfo((object)($"[GamemodeLib] TryEndCurrentGame: broadcastToClients={broadcastToClients} serverActive={NetworkServer.active} clientActive={NetworkClient.active} " + string.Format("gameEnded={0} currentGamemodeId='{1}'", GameEnded, CurrentGamemodeId ?? "<null>")));
			if (GameEnded)
			{
				Plugin.Log.LogInfo((object)"[GamemodeLib] TryEndCurrentGame: no-op because GameEnded is already true");
				return;
			}
			string currentGamemodeId = CurrentGamemodeId;
			if (currentGamemodeId == null)
			{
				Plugin.Log.LogWarning((object)"[GamemodeLib] TryEndCurrentGame: CurrentGamemodeId was null while GameEnded=false; forcing GameEnded=true");
				GameEnded = true;
				return;
			}
			if (!Modes.TryGetValue(currentGamemodeId, out IGamemode value) || value == null)
			{
				Plugin.Log.LogWarning((object)($"[GamemodeLib] TryEndCurrentGame: mode lookup failed for id '{currentGamemodeId}'. modesCount={Modes.Count}. " + "Forcing GameEnded=true and clearing CurrentGamemodeId."));
				GameEnded = true;
				CurrentGamemodeId = null;
				return;
			}
			Plugin.Log.LogInfo((object)("[GamemodeLib] TryEndCurrentGame: ending current mode '" + currentGamemodeId + "' (modeType=" + value.GetType().FullName + ")"));
			GameEnded = true;
			try
			{
				value.OnGameEnd();
				Plugin.Log.LogInfo((object)("[GamemodeLib] TryEndCurrentGame: mode.OnGameEnd() finished for '" + currentGamemodeId + "'"));
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[GamemodeLib] TryEndCurrentGame: mode.OnGameEnd() threw for '{currentGamemodeId}': {arg}");
				throw;
			}
			finally
			{
				if (broadcastToClients && NetworkServer.active)
				{
					Plugin.Log.LogInfo((object)("[GamemodeLib] TryEndCurrentGame: broadcasting GamemodeEndMessage to all clients for '" + currentGamemodeId + "'"));
					NetworkMessageBootstrap.Register();
					NetworkServer.SendToAll<GamemodeEndMessage>(new GamemodeEndMessage
					{
						GamemodeId = currentGamemodeId
					}, 0, false);
					Plugin.Log.LogInfo((object)("[GamemodeLib] TryEndCurrentGame: broadcast sent for '" + currentGamemodeId + "'"));
				}
				else
				{
					Plugin.Log.LogInfo((object)$"[GamemodeLib] TryEndCurrentGame: not broadcasting (broadcastToClients={broadcastToClients}, serverActive={NetworkServer.active})");
				}
				ConfigSyncManager.Instance?.ClearScope(currentGamemodeId);
				CurrentGamemodeId = null;
				Plugin.Log.LogInfo((object)"[GamemodeLib] TryEndCurrentGame: cleared CurrentGamemodeId");
			}
		}
	}
	public interface IGamemode
	{
		Harmony GamemodeHarmony { get; init; }

		string Name { get; }

		string ModId { get; }

		string GameModeId => ModId + ":" + Name;

		int MinPlayers { get; }

		int MaxPlayers { get; }

		bool IsTeamBased { get; }

		bool IsNormalStartProcedure { get; }

		bool IsTaggingEnabled { get; }

		int TeamCount { get; }

		string Description { get; }

		void OnGameStart();

		void OnGameEnd();

		bool CanStart(int playerCount);
	}
	public static class NetworkMessageBootstrap
	{
		private static bool _commonRegistered;

		private static bool _clientRegistered;

		private static bool _serverRegistered;

		public static void Register()
		{
			Plugin.Log.LogInfo((object)($"[GamemodeLib] NetworkMessageBootstrap.Register: clientActive={NetworkClient.active} serverActive={NetworkServer.active} " + $"commonRegistered={_commonRegistered} clientRegistered={_clientRegistered} serverRegistered={_serverRegistered}"));
			RegisterCommon();
			if (NetworkClient.active)
			{
				RegisterClient();
			}
			if (NetworkServer.active)
			{
				RegisterServer();
			}
		}

		private static void RegisterCommon()
		{
			if (_commonRegistered)
			{
				Plugin.Log.LogInfo((object)"[GamemodeLib] NetworkMessageBootstrap.RegisterCommon: already registered");
				return;
			}
			_commonRegistered = true;
			Plugin.Log.LogInfo((object)"[GamemodeLib] NetworkMessageBootstrap.RegisterCommon: registering readers/writers");
			Writer<TeamAssignMessage>.write = delegate(NetworkWriter writer, TeamAssignMessage msg)
			{
				NetworkWriterExtensions.WriteULong(writer, msg.PlayerGuid);
				NetworkWriterExtensions.WriteInt(writer, msg.TeamId);
			};
			Reader<TeamAssignMessage>.read = (NetworkReader reader) => new TeamAssignMessage
			{
				PlayerGuid = NetworkReaderExtensions.ReadULong(reader),
				TeamId = NetworkReaderExtensions.ReadInt(reader)
			};
			Writer<TeamRequestMessage>.write = delegate(NetworkWriter writer, TeamRequestMessage msg)
			{
				NetworkWriterExtensions.WriteULong(writer, msg.PlayerGuid);
			};
			Reader<TeamRequestMessage>.read = (NetworkReader reader) => new TeamRequestMessage
			{
				PlayerGuid = NetworkReaderExtensions.ReadULong(reader)
			};
			Writer<TeamDefinitionMessage>.write = delegate(NetworkWriter writer, TeamDefinitionMessage msg)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				NetworkWriterExtensions.WriteInt(writer, msg.ID);
				NetworkWriterExtensions.WriteColor(writer, msg.Color);
				NetworkWriterExtensions.WriteString(writer, msg.Name);
			};
			Reader<TeamDefinitionMessage>.read = (NetworkReader reader) => new TeamDefinitionMessage
			{
				ID = NetworkReaderExtensions.ReadInt(reader),
				Color = NetworkReaderExtensions.ReadColor(reader),
				Name = NetworkReaderExtensions.ReadString(reader)
			};
			Writer<GamemodeStartMessage>.write = delegate(NetworkWriter writer, GamemodeStartMessage msg)
			{
				NetworkWriterExtensions.WriteString(writer, msg.GamemodeId);
			};
			Reader<GamemodeStartMessage>.read = (NetworkReader reader) => new GamemodeStartMessage
			{
				GamemodeId = NetworkReaderExtensions.ReadString(reader)
			};
			Writer<GamemodeEndMessage>.write = delegate(NetworkWriter writer, GamemodeEndMessage msg)
			{
				NetworkWriterExtensions.WriteString(writer, msg.GamemodeId);
			};
			Reader<GamemodeEndMessage>.read = (NetworkReader reader) => new GamemodeEndMessage
			{
				GamemodeId = NetworkReaderExtensions.ReadString(reader)
			};
			Writer<RaycastRequestMessage>.write = delegate(NetworkWriter writer, RaycastRequestMessage msg)
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				NetworkWriterExtensions.WriteString(writer, msg.Purpose);
				NetworkWriterExtensions.WriteULong(writer, msg.Guid);
				NetworkWriterExtensions.WriteVector3(writer, msg.Origin);
				NetworkWriterExtensions.WriteVector3(writer, msg.Direction);
				NetworkWriterExtensions.WriteFloat(writer, msg.MaxDistance);
				NetworkWriterExtensions.WriteInt(writer, msg.RaycastMask);
			};
			Reader<RaycastRequestMessage>.read = (NetworkReader reader) => new RaycastRequestMessage
			{
				Purpose = NetworkReaderExtensions.ReadString(reader),
				Guid = NetworkReaderExtensions.ReadULong(reader),
				Origin = NetworkReaderExtensions.ReadVector3(reader),
				Direction = NetworkReaderExtensions.ReadVector3(reader),
				MaxDistance = NetworkReaderExtensions.ReadFloat(reader),
				RaycastMask = NetworkReaderExtensions.ReadInt(reader)
			};
			Writer<RaycastResultMessage>.write = delegate(NetworkWriter writer, RaycastResultMessage msg)
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				NetworkWriterExtensions.WriteString(writer, msg.Purpose);
				NetworkWriterExtensions.WriteULong(writer, msg.Guid);
				NetworkWriterExtensions.WriteVector3(writer, msg.Origin);
				NetworkWriterExtensions.WriteVector3(writer, msg.Direction);
				NetworkWriterExtensions.WriteFloat(writer, msg.MaxDistance);
				NetworkWriterExtensions.WriteInt(writer, msg.RaycastMask);
				NetworkWriterExtensions.WriteBool(writer, msg.HasHit);
				NetworkWriterExtensions.WriteVector3(writer, msg.HitPoint);
				NetworkWriterExtensions.WriteVector3(writer, msg.HitNormal);
				NetworkWriterExtensions.WriteFloat(writer, msg.HitDistance);
				NetworkWriterExtensions.WriteString(writer, msg.HitObjectName);
				NetworkWriterExtensions.WriteString(writer, msg.ClosestValidRootObjectName);
			};
			Reader<RaycastResultMessage>.read = (NetworkReader reader) => new RaycastResultMessage
			{
				Purpose = NetworkReaderExtensions.ReadString(reader),
				Guid = NetworkReaderExtensions.ReadULong(reader),
				Origin = NetworkReaderExtensions.ReadVector3(reader),
				Direction = NetworkReaderExtensions.ReadVector3(reader),
				MaxDistance = NetworkReaderExtensions.ReadFloat(reader),
				RaycastMask = NetworkReaderExtensions.ReadInt(reader),
				HasHit = NetworkReaderExtensions.ReadBool(reader),
				HitPoint = NetworkReaderExtensions.ReadVector3(reader),
				HitNormal = NetworkReaderExtensions.ReadVector3(reader),
				HitDistance = NetworkReaderExtensions.ReadFloat(reader),
				HitObjectName = NetworkReaderExtensions.ReadString(reader),
				ClosestValidRootObjectName = NetworkReaderExtensions.ReadString(reader)
			};
			Writer<ConfigScopeRequestMessage>.write = delegate(NetworkWriter writer, ConfigScopeRequestMessage msg)
			{
				NetworkWriterExtensions.WriteString(writer, msg.ScopeId);
			};
			Reader<ConfigScopeRequestMessage>.read = (NetworkReader reader) => new ConfigScopeRequestMessage
			{
				ScopeId = NetworkReaderExtensions.ReadString(reader)
			};
			Writer<ConfigEntry>.write = delegate(NetworkWriter writer, ConfigEntry entry)
			{
				NetworkWriterExtensions.WriteString(writer, entry.Key);
				writer.WriteByte((byte)entry.Type);
				switch (entry.Type)
				{
				case ConfigValueType.String:
					NetworkWriterExtensions.WriteString(writer, entry.StringValue);
					break;
				case ConfigValueType.Int:
					NetworkWriterExtensions.WriteInt(writer, entry.IntValue);
					break;
				case ConfigValueType.Float:
					NetworkWriterExtensions.WriteFloat(writer, entry.FloatValue);
					break;
				case ConfigValueType.Bool:
					NetworkWriterExtensions.WriteBool(writer, entry.BoolValue);
					break;
				default:
					NetworkWriterExtensions.WriteString(writer, entry.StringValue);
					break;
				}
			};
			Reader<ConfigEntry>.read = delegate(NetworkReader reader)
			{
				ConfigEntry result = new ConfigEntry
				{
					Key = NetworkReaderExtensions.ReadString(reader),
					Type = (ConfigValueType)reader.ReadByte()
				};
				switch (result.Type)
				{
				case ConfigValueType.String:
					result.StringValue = NetworkReaderExtensions.ReadString(reader);
					break;
				case ConfigValueType.Int:
					result.IntValue = NetworkReaderExtensions.ReadInt(reader);
					break;
				case ConfigValueType.Float:
					result.FloatValue = NetworkReaderExtensions.ReadFloat(reader);
					break;
				case ConfigValueType.Bool:
					result.BoolValue = NetworkReaderExtensions.ReadBool(reader);
					break;
				default:
					result.StringValue = NetworkReaderExtensions.ReadString(reader);
					break;
				}
				return result;
			};
			Writer<ConfigScopeUpdateMessage>.write = delegate(NetworkWriter writer, ConfigScopeUpdateMessage msg)
			{
				NetworkWriterExtensions.WriteString(writer, msg.ScopeId);
				writer.Write<ConfigEntry>(msg.Entry);
			};
			Reader<ConfigScopeUpdateMessage>.read = (NetworkReader reader) => new ConfigScopeUpdateMessage
			{
				ScopeId = NetworkReaderExtensions.ReadString(reader),
				Entry = reader.Read<ConfigEntry>()
			};
			Writer<ConfigScopeFullMessage>.write = delegate(NetworkWriter writer, ConfigScopeFullMessage msg)
			{
				NetworkWriterExtensions.WriteString(writer, msg.ScopeId);
				int num = msg.Entries?.Count ?? 0;
				NetworkWriterExtensions.WriteInt(writer, num);
				for (int i = 0; i < num; i++)
				{
					writer.Write<ConfigEntry>(msg.Entries[i]);
				}
			};
			Reader<ConfigScopeFullMessage>.read = delegate(NetworkReader reader)
			{
				ConfigScopeFullMessage result = new ConfigScopeFullMessage
				{
					ScopeId = NetworkReaderExtensions.ReadString(reader),
					Entries = new List<ConfigEntry>()
				};
				int num = NetworkReaderExtensions.ReadInt(reader);
				for (int i = 0; i < num; i++)
				{
					result.Entries.Add(reader.Read<ConfigEntry>());
				}
				return result;
			};
			Plugin.Log.LogInfo((object)"[GamemodeLib] NetworkMessageBootstrap.RegisterCommon: registered network message readers/writers (teams + gamemodes + config + raycasts)");
		}

		private static void RegisterClient()
		{
			if (_clientRegistered)
			{
				Plugin.Log.LogInfo((object)"[GamemodeLib] NetworkMessageBootstrap.RegisterClient: already registered");
				return;
			}
			_clientRegistered = true;
			Plugin.Log.LogInfo((object)"[GamemodeLib] NetworkMessageBootstrap.RegisterClient: registering client handlers");
			NetworkClient.RegisterHandler<TeamAssignMessage>((Action<TeamAssignMessage>)delegate(TeamAssignMessage msg)
			{
				Plugin.Log.LogInfo((object)$"[GamemodeLib] Client received TeamAssignMessage: playerGuid={msg.PlayerGuid} teamId={msg.TeamId}");
				TeamManager.Instance?.ApplyTeamMessage(msg);
			}, true);
			NetworkClient.RegisterHandler<TeamDefinitionMessage>((Action<TeamDefinitionMessage>)delegate(TeamDefinitionMessage msg)
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				Plugin.Log.LogInfo((object)string.Format("[GamemodeLib] Client received TeamDefinitionMessage: id={0} name='{1}' color={2}", msg.ID, msg.Name ?? "<null>", msg.Color));
				TeamManager.Instance?.ApplyTeamDefinitionMessage(msg);
			}, true);
			NetworkClient.RegisterHandler<GamemodeStartMessage>((Action<GamemodeStartMessage>)delegate(GamemodeStartMessage msg)
			{
				Plugin.Log.LogInfo((object)("[GamemodeLib] Client received GamemodeStartMessage: gamemodeId='" + (msg.GamemodeId ?? "<null>") + "'"));
				GameModeUtilities.ApplyGamemodeStartMessage(msg);
			}, true);
			NetworkClient.RegisterHandler<GamemodeEndMessage>((Action<GamemodeEndMessage>)delegate(GamemodeEndMessage msg)
			{
				Plugin.Log.LogInfo((object)("[GamemodeLib] Client received GamemodeEndMessage: gamemodeId='" + (msg.GamemodeId ?? "<null>") + "'"));
				GameModeUtilities.ApplyGamemodeEndMessage(msg);
			}, true);
			NetworkClient.RegisterHandler<ConfigScopeFullMessage>((Action<ConfigScopeFullMessage>)delegate(ConfigScopeFullMessage msg)
			{
				Plugin.Log.LogInfo((object)string.Format("[GamemodeLib] Client received ConfigScopeFullMessage: scopeId='{0}' entries={1}", msg.ScopeId ?? "<null>", msg.Entries?.Count ?? 0));
				ConfigSyncManager.Instance?.ApplyFull(msg);
			}, true);
			NetworkClient.RegisterHandler<ConfigScopeUpdateMessage>((Action<ConfigScopeUpdateMessage>)delegate(ConfigScopeUpdateMessage msg)
			{
				Plugin.Log.LogInfo((object)string.Format("[GamemodeLib] Client received ConfigScopeUpdateMessage: scopeId='{0}' key='{1}' type={2}", msg.ScopeId ?? "<null>", msg.Entry.Key ?? "<null>", (byte)msg.Entry.Type));
				ConfigSyncManager.Instance?.ApplyUpdate(msg);
			}, true);
			NetworkClient.RegisterHandler<RaycastResultMessage>((Action<RaycastResultMessage>)delegate(RaycastResultMessage msg)
			{
				Plugin.Log.LogInfo((object)string.Format("[GamemodeLib] Client received RaycastResultMessage: purpose='{0}' hasHit={1} root='{2}'", msg.Purpose ?? "<null>", msg.HasHit, msg.ClosestValidRootObjectName ?? "<null>"));
				RaycastUtility.HandleRaycastResult(msg);
			}, true);
			Plugin.Log.LogInfo((object)"[GamemodeLib] NetworkMessageBootstrap.RegisterClient: client handlers registered");
		}

		private static void RegisterServer()
		{
			if (_serverRegistered)
			{
				Plugin.Log.LogInfo((object)"[GamemodeLib] NetworkMessageBootstrap.RegisterServer: already registered");
				return;
			}
			_serverRegistered = true;
			Plugin.Log.LogInfo((object)"[GamemodeLib] NetworkMessageBootstrap.RegisterServer: registering server handlers");
			NetworkServer.RegisterHandler<TeamRequestMessage>((Action<NetworkConnectionToClient, TeamRequestMessage>)delegate(NetworkConnectionToClient conn, TeamRequestMessage msg)
			{
				Plugin.Log.LogInfo((object)$"[GamemodeLib] Server received TeamRequestMessage: connId={conn.connectionId} playerGuid={msg.PlayerGuid}");
				TeamManager.Instance?.HandleTeamRequest(conn, msg);
			}, true);
			NetworkServer.RegisterHandler<ConfigScopeRequestMessage>((Action<NetworkConnectionToClient, ConfigScopeRequestMessage>)delegate(NetworkConnectionToClient conn, ConfigScopeRequestMessage msg)
			{
				Plugin.Log.LogInfo((object)string.Format("[GamemodeLib] Server received ConfigScopeRequestMessage: connId={0} scopeId='{1}'", conn.connectionId, msg.ScopeId ?? "<null>"));
				ConfigSyncManager.Instance?.HandleScopeRequest(conn, msg);
			}, true);
			NetworkServer.RegisterHandler<RaycastRequestMessage>((Action<NetworkConnectionToClient, RaycastRequestMessage>)delegate(NetworkConnectionToClient conn, RaycastRequestMessage msg)
			{
				Plugin.Log.LogInfo((object)string.Format("[GamemodeLib] Server received RaycastRequestMessage: connId={0} purpose='{1}' maxDistance={2} mask={3}", conn.connectionId, msg.Purpose ?? "<null>", msg.MaxDistance, msg.RaycastMask));
				RaycastUtility.HandleRaycastRequest(conn.connectionId, msg);
			}, true);
			Plugin.Log.LogInfo((object)"[GamemodeLib] NetworkMessageBootstrap.RegisterServer: server handlers registered");
		}

		[HarmonyPatch(typeof(BNetworkManager), "OnStartClient")]
		[HarmonyPostfix]
		public static void OnStartClient_Postfix()
		{
			Plugin.Log.LogInfo((object)"[GamemodeLib] BNetworkManager.OnStartClient postfix: registering network messages");
			Register();
		}

		[HarmonyPatch(typeof(BNetworkManager), "OnStartServer")]
		[HarmonyPostfix]
		public static void OnStartServer_Postfix()
		{
			Plugin.Log.LogInfo((object)"[GamemodeLib] BNetworkManager.OnStartServer postfix: registering network messages");
			Register();
		}

		[HarmonyPatch(typeof(BNetworkManager), "OnDestroy")]
		[HarmonyPrefix]
		public static void OnDestroy_Prefix()
		{
			Plugin.Log.LogInfo((object)$"[GamemodeLib] BNetworkManager.OnDestroy prefix: attempting to end current game (serverActive={NetworkServer.active})");
			GameModeUtilities.TryEndCurrentGame(NetworkServer.active);
		}
	}
	[BepInPlugin("com.github.glarmer.Gamemode_Lib", "Gamemode_Lib", "0.5.1")]
	public class Plugin : BaseUnityPlugin
	{
		internal readonly Harmony _harmony = new Harmony("com.github.glarmer.Gamemode_Lib");

		internal static Plugin Instance;

		public const string Id = "com.github.glarmer.Gamemode_Lib";

		internal static ManualLogSource Log { get; private set; }

		public static string Name => "Gamemode_Lib";

		public static string Version => "0.5.1";

		private void Awake()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			GameObject val = new GameObject("GamemodeLib");
			val.AddComponent<ConfigSyncManager>();
			Log.LogInfo((object)("Plugin " + Name + " (Version " + Version + ") is patching!"));
			Log.LogInfo((object)"[GamemodeLib] is running Network patches");
			_harmony.PatchAll(typeof(NetworkMessageBootstrap));
			Log.LogInfo((object)"[GamemodeLib] is patching Scoreboard");
			_harmony.PatchAll(typeof(ScoreboardPatches));
			Log.LogInfo((object)"[GamemodeLib] is patching MatchSetupPlayer");
			_harmony.PatchAll(typeof(MatchSetupPlayerPatches));
			Log.LogInfo((object)"[GamemodeLib] is patching CourseManager");
			_harmony.PatchAll(typeof(CourseManagerPatches));
			Log.LogInfo((object)"[GamemodeLib] is patching NameTagUi");
			_harmony.PatchAll(typeof(NameTagUiPatches));
			Log.LogInfo((object)"[GamemodeLib] is patching GameManager");
			_harmony.PatchAll(typeof(GameManagerPatches));
			Log.LogInfo((object)"[GamemodeLib] is patching MatchSetup");
			_harmony.PatchAll(typeof(MatchSetupMenuPatches));
			Log.LogInfo((object)"[GamemodeLib] is patching PlayerInfo");
			_harmony.PatchAll(typeof(PlayerInfoPatches));
			_harmony.PatchAll(typeof(TeeOffCountdownPatches));
			Log.LogInfo((object)"[GamemodeLib] is finished patching.");
			SceneEvents.Init();
			SceneEvents.OnReturnToLobby += OnReturnToLobby;
			PlayerEvents.Init();
			Log.LogInfo((object)"[GamemodeLib] Initialized.");
		}

		private void OnReturnToLobby(Scene hole, Scene lobby)
		{
			if (GameModeUtilities.CurrentGamemodeId != null)
			{
				GameModeUtilities.TryEndCurrentGame(broadcastToClients: true);
			}
			GameModeUtilities.ReinitializeGameState();
		}

		private void OnDestroy()
		{
			SceneEvents.OnReturnToLobby -= OnReturnToLobby;
			SceneEvents.Shutdown();
			_harmony.UnpatchSelf();
		}
	}
	public static class RaycastUtility
	{
		public readonly struct RaycastCompletedEventArgs
		{
			public readonly string Purpose;

			public readonly int RequestingConnectionId;

			public readonly ulong RequestingClientGuid;

			public readonly Vector3 Origin;

			public readonly Vector3 Direction;

			public readonly float MaxDistance;

			public readonly int RaycastMask;

			public readonly bool HasHit;

			public readonly RaycastHit Hit;

			public readonly GameObject ClosestValidRootObject;

			public RaycastCompletedEventArgs(string purpose, int requestingConnectionId, ulong requestingClientGuid, Vector3 origin, Vector3 direction, float maxDistance, int raycastMask, bool hasHit, RaycastHit hit, GameObject closestValidRootObject)
			{
				//IL_0016: 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_0020: 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_0040: Unknown result type (might be due to invalid IL or missing references)
				Purpose = purpose;
				RequestingConnectionId = requestingConnectionId;
				RequestingClientGuid = requestingClientGuid;
				Origin = origin;
				Direction = direction;
				MaxDistance = maxDistance;
				RaycastMask = raycastMask;
				HasHit = hasHit;
				Hit = hit;
				ClosestValidRootObject = closestValidRootObject;
			}
		}

		public readonly struct RaycastResultReceivedEventArgs
		{
			public readonly string Purpose;

			public readonly ulong RequestingClientGuid;

			public readonly Vector3 Origin;

			public readonly Vector3 Direction;

			public readonly float MaxDistance;

			public readonly int RaycastMask;

			public readonly bool HasHit;

			public readonly Vector3 HitPoint;

			public readonly Vector3 HitNormal;

			public readonly float HitDistance;

			public readonly string HitObjectName;

			public readonly string ClosestValidRootObjectName;

			public RaycastResultReceivedEventArgs(string purpose, ulong requestingClientGuid, Vector3 origin, Vector3 direction, float maxDistance, int raycastMask, bool hasHit, Vector3 hitPoint, Vector3 hitNormal, float hitDistance, string hitObjectName, string closestValidRootObjectName)
			{
				//IL_000f: 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_0016: 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_0036: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0040: Unknown result type (might be due to invalid IL or missing references)
				Purpose = purpose;
				RequestingClientGuid = requestingClientGuid;
				Origin = origin;
				Direction = direction;
				MaxDistance = maxDistance;
				RaycastMask = raycastMask;
				HasHit = hasHit;
				HitPoint = hitPoint;
				HitNormal = hitNormal;
				HitDistance = hitDistance;
				HitObjectName = hitObjectName;
				ClosestValidRootObjectName = closestValidRootObjectName;
			}
		}

		public static event Action<RaycastCompletedEventArgs> RaycastCompleted;

		public static event Action<RaycastResultReceivedEventArgs> RaycastResultReceived;

		public static GameObject GetClosestValidObjectFromMainCameraCenter(float maxDistance = 100f, LayerMask raycastMask = default(LayerMask))
		{
			//IL_0017: 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)
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return null;
			}
			return GetClosestValidObject(((Component)main).transform.position, ((Component)main).transform.forward, maxDistance, raycastMask);
		}

		public static GameObject GetClosestValidObject(Vector3 origin, Vector3 direction, float maxDistance = 100f, LayerMask raycastMask = default(LayerMask))
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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_0025: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			if (LayerMask.op_Implicit(raycastMask) == 0)
			{
				raycastMask = LayerMask.op_Implicit(-1);
			}
			if (((Vector3)(ref direction)).sqrMagnitude <= 1E-06f)
			{
				return null;
			}
			Ray val = default(Ray);
			((Ray)(ref val))..ctor(origin, ((Vector3)(ref direction)).normalized);
			RaycastHit[] array = Physics.RaycastAll(val, maxDistance, LayerMask.op_Implicit(raycastMask), (QueryTriggerInteraction)1);
			Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
			RaycastHit[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				RaycastHit val2 = array2[num];
				GameObject gameObject = ((Component)((RaycastHit)(ref val2)).collider).gameObject;
				if (!ShouldIgnore(gameObject))
				{
					return GetHighestParent(gameObject);
				}
			}
			return null;
		}

		public static void RequestRaycastOnHost(string purpose, ulong guid, Vector3 origin, Vector3 direction, float maxDistance = 100f, LayerMask raycastMask = default(LayerMask))
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (NetworkClient.active)
			{
				if (NetworkServer.active)
				{
					HandleRaycastRequest(-1, new RaycastRequestMessage
					{
						Purpose = purpose,
						Guid = guid,
						Origin = origin,
						Direction = direction,
						MaxDistance = maxDistance,
						RaycastMask = ((LayerMask.op_Implicit(raycastMask) == 0) ? (-1) : ((LayerMask)(ref raycastMask)).value)
					});
				}
				else
				{
					NetworkMessageBootstrap.Register();
					NetworkClient.Send<RaycastRequestMessage>(new RaycastRequestMessage
					{
						Purpose = purpose,
						Guid = guid,
						Origin = origin,
						Direction = direction,
						MaxDistance = maxDistance,
						RaycastMask = ((LayerMask.op_Implicit(raycastMask) == 0) ? (-1) : ((LayerMask)(ref raycastMask)).value)
					}, 0);
				}
			}
		}

		internal static void HandleRaycastRequest(int requestingConnectionId, RaycastRequestMessage msg)
		{
			//IL_001b: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active)
			{
				return;
			}
			int num = ((msg.RaycastMask == 0) ? (-1) : msg.RaycastMask);
			LayerMask val = LayerMask.op_Implicit(num);
			bool flag = false;
			RaycastHit hit = default(RaycastHit);
			GameObject val2 = null;
			if (((Vector3)(ref msg.Direction)).sqrMagnitude > 1E-06f)
			{
				Ray val3 = default(Ray);
				((Ray)(ref val3))..ctor(msg.Origin, ((Vector3)(ref msg.Direction)).normalized);
				RaycastHit[] array = Physics.RaycastAll(val3, msg.MaxDistance, LayerMask.op_Implicit(val), (QueryTriggerInteraction)1);
				Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
				RaycastHit[] array2 = array;
				for (int num2 = 0; num2 < array2.Length; num2++)
				{
					RaycastHit val4 = array2[num2];
					GameObject gameObject = ((Component)((RaycastHit)(ref val4)).collider).gameObject;
					if (!ShouldIgnore(gameObject))
					{
						flag = true;
						hit = val4;
						val2 = GetHighestParent(gameObject);
						break;
					}
				}
			}
			if (!((Object)(object)val2 == (Object)null))
			{
				RaycastUtility.RaycastCompleted?.Invoke(new RaycastCompletedEventArgs(msg.Purpose, requestingConnectionId, msg.Guid, msg.Origin, msg.Direction, msg.MaxDistance, num, flag, hit, val2));
				NetworkMessageBootstrap.Register();
				NetworkServer.SendToAll<RaycastResultMessage>(new RaycastResultMessage
				{
					Purpose = (msg.Purpose ?? string.Empty),
					Guid = msg.Guid,
					Origin = msg.Origin,
					Direction = msg.Direction,
					MaxDistance = msg.MaxDistance,
					RaycastMask = num,
					HasHit = flag,
					HitPoint = (Vector3)(flag ? ((RaycastHit)(ref hit)).point : default(Vector3)),
					HitNormal = (Vector3)(flag ? ((RaycastHit)(ref hit)).normal : default(Vector3)),
					HitDistance = (flag ? ((RaycastHit)(ref hit)).distance : 0f),
					HitObjectName = (flag ? ((Object)((Component)((RaycastHit)(ref hit)).collider).gameObject).name : string.Empty),
					ClosestValidRootObjectName = (((Object)val2).name ?? string.Empty)
				}, 0, false);
			}
		}

		internal static void HandleRaycastResult(RaycastResultMessage msg)
		{
			//IL_0017: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			RaycastUtility.RaycastResultReceived?.Invoke(new RaycastResultReceivedEventArgs(msg.Purpose, msg.Guid, msg.Origin, msg.Direction, msg.MaxDistance, msg.RaycastMask, msg.HasHit, msg.HitPoint, msg.HitNormal, msg.HitDistance, msg.HitObjectName, msg.ClosestValidRootObjectName));
		}

		private static bool ShouldIgnore(GameObject obj)
		{
			Transform val = obj.transform;
			while ((Object)(object)val != (Object)null)
			{
				if (((Object)val).name == "Terrain")
				{
					return true;
				}
				if ((Object)(object)((Component)val).GetComponent<PlayerInfo>() != (Object)null)
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static GameObject GetHighestParent(GameObject obj)
		{
			Transform val = obj.transform;
			while ((Object)(object)val.parent != (Object)null)
			{
				val = val.parent;
			}
			return ((Component)val).gameObject;
		}
	}
}
namespace Gamemode_Lib.Patches
{
	public class CourseManagerPatches
	{
		[HarmonyPatch(typeof(CourseManager), "EndCourse")]
		[HarmonyPrefix]
		public static void EndCourse_Prefix()
		{
			Plugin.Log.LogInfo((object)$"[GamemodeLib] CourseManager.EndCourse prefix: TryEndCurrentGame(broadcastToClients=true) serverActive={NetworkServer.active}");
			GameModeUtilities.TryEndCurrentGame(broadcastToClients: true);
		}
	}
	public class GameManagerPatches
	{
		[HarmonyPatch(typeof(GameManager), "Awake")]
		[HarmonyPostfix]
		public static void Awake_Postfix(GameManager __instance)
		{
			((Component)__instance).gameObject.AddComponent<TeamManager>();
		}
	}
	public class MatchSetupMenuPatches
	{
		private static readonly Dictionary<TMP_Dropdown, Dictionary<int, string>> DropdownMappings = new Dictionary<TMP_Dropdown, Dictionary<int, string>>();

		private static TMP_Dropdown? _tmpDropdown;

		private static IGamemode? _gameMode;

		[HarmonyPatch(typeof(MatchSetupMenu), "SetEnabled")]
		[HarmonyPostfix]
		public static void SetEnabled_Postfix(MatchSetupMenu __instance)
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			if ((Object)(object)_tmpDropdown == (Object)null)
			{
				_tmpDropdown = ((IEnumerable<TMP_Dropdown>)Object.FindObjectsOfType<TMP_Dropdown>(true)).FirstOrDefault((Func<TMP_Dropdown, bool>)((TMP_Dropdown dropdown) => dropdown.options != null && dropdown.options.Any((OptionData o) => o.text == "Free-for-all")));
				if ((Object)(object)_tmpDropdown == (Object)null)
				{
					Plugin.Log.LogError((object)"[GamemodeLib] Could not find TMP_Dropdown containing 'Free-for-all'");
					return;
				}
			}
			if (DropdownMappings.ContainsKey(_tmpDropdown))
			{
				return;
			}
			Dictionary<int, string> dictionary = new Dictionary<int, string>();
			int num = _tmpDropdown.options.Count;
			foreach (KeyValuePair<string, IGamemode> mode in GameModeUtilities.Modes)
			{
				string key = mode.Key;
				IGamemode value = mode.Value;
				_tmpDropdown.options.Add(new OptionData(value.Name));
				dictionary[num] = key;
				num++;
			}
			_tmpDropdown.RefreshShownValue();
			DropdownMappings[_tmpDropdown] = dictionary;
			((UnityEvent<int>)(object)_tmpDropdown.onValueChanged).AddListener((UnityAction<int>)delegate(int index)
			{
				//IL_00e3: 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)
				Plugin.Log.LogInfo((object)("[GamemodeLib] Selected gamemode: " + _tmpDropdown.options[index].text + ")"));
				_gameMode = null;
				if (DropdownMappings[_tmpDropdown].TryGetValue(index, out string value2))
				{
					IGamemode gamemode = (_gameMode = GameModeUtilities.Modes[value2]);
					Plugin.Log.LogInfo((object)("[GamemodeLib] " + gamemode.Name + " is using GamemodeLib. It's ID is... " + gamemode.GameModeId));
					if (NetworkServer.active)
					{
						TeamManager.Instance.ResetTeams();
						TeamManager.Instance.ClearTeamDefinitions();
						if (_gameMode.IsTeamBased)
						{
							__instance.gameMode = (GameMode)0;
							__instance.OnGameModeChanged((GameMode)1, (GameMode)0);
							TeamManager.Instance.CreateAndAssignTeams(_gameMode.TeamCount);
						}
						else
						{
							__instance.gameMode = (GameMode)0;
							__instance.OnGameModeChanged((GameMode)1, (GameMode)0);
						}
					}
				}
			});
			Plugin.Log.LogInfo((object)"[GamemodeLib] Gamemodes injected into dropdown");
		}

		[HarmonyPatch(typeof(MatchSetupMenu), "StartOrCancelMatch")]
		[HarmonyPrefix]
		public static bool StartOrCancelMatch_Prefix(MatchSetupMenu __instance)
		{
			if ((Object)(object)_tmpDropdown == (Object)null)
			{
				return true;
			}
			if (_gameMode == null)
			{
				return true;
			}
			if (_gameMode.CanStart(__instance.maxPlayers))
			{
				if (StartGame())
				{
					StopAutoNextHole.HideCursor();
					return true;
				}
			}
			else
			{
				Plugin.Log.LogError((object)"[GamemodeLib] Invalid choices, not starting gamemode.");
			}
			return false;
		}

		private static bool StartGame()
		{
			Plugin.Log.LogInfo((object)"[GamemodeLib] Starting gamemode.");
			if ((Object)(object)TeamManager.Instance != (Object)null)
			{
				TeamManager.Instance.AssignUnAssignedPlayersToTeams();
				TeamManager.Instance.SaveCurrentTeams();
				TeamManager.Instance.TryRefreshLocalPlayerTeam();
				if ((Object)(object)TeamManager.Instance.LocalPlayerTeam == (Object)null)
				{
					Plugin.Log.LogWarning((object)"[GamemodeLib] LocalPlayerTeam is null immediately before OnGameStart()");
				}
			}
			GameModeUtilities.CurrentGamemodeId = _gameMode.GameModeId;
			GameModeUtilities.GameEnded = false;
			_gameMode.OnGameStart();
			if (NetworkServer.active)
			{
				NetworkMessageBootstrap.Register();
				ConfigSyncManager.Instance?.BroadcastScopeToClients(_gameMode.GameModeId);
				NetworkServer.SendToAll<GamemodeStartMessage>(new GamemodeStartMessage
				{
					GamemodeId = _gameMode.GameModeId
				}, 0, false);
			}
			if (_gameMode.IsNormalStartProcedure)
			{
				return true;
			}
			Plugin.Log.LogInfo((object)"[GamemodeLib] Custom gamemode is chosen, cancelling default start procedure.");
			return false;
		}

		[HarmonyPatch(typeof(MatchSetupMenu), "StartOrCancelMatch")]
		[HarmonyPostfix]
		public static void StartOrCancelMatch_Postfix(MatchSetupMenu __instance)
		{
			TeamManager.Instance.ReloadSavedTeams();
		}
	}
	public class MatchSetupPlayerPatches
	{
		private const string SwapButtonName = "SwapTeamButton";

		[HarmonyPatch(typeof(MatchSetupPlayerEntry), "Update")]
		[HarmonyPostfix]
		public static void Update_Postfix(MatchSetupPlayerEntry __instance)
		{
			UpdateBackground(__instance);
		}

		[HarmonyPatch(typeof(MatchSetupPlayerEntry), "Awake")]
		[HarmonyPostfix]
		public static void Awake_Postfix(MatchSetupPlayerEntry __instance)
		{
			AddSwapButton(__instance);
		}

		private static void UpdateBackground(MatchSetupPlayerEntry playerUI)
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)TeamManager.Instance == (Object)null)
			{
				return;
			}
			ulong guid = playerUI.Guid;
			PlayerTeam playerTeam = null;
			foreach (PlayerTeam player in TeamManager.Instance.Players)
			{
				if (!((Object)(object)player == (Object)null) && !((Object)(object)player.playerInfo == (Object)null) && player.playerInfo.PlayerId.guid == guid)
				{
					playerTeam = player;
					break;
				}
			}
			if ((Object)(object)playerTeam == (Object)null || !TeamManager.Instance.TryGetTeam(playerTeam.teamId, out TeamData team))
			{
				return;
			}
			Transform val = ((Component)playerUI).transform.Find("Background");
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Image component = ((Component)val).GetComponent<Image>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			((Graphic)component).color = team.Color;
			Transform val2 = ((Component)playerUI).transform.Find("Portrait");
			if (!((Object)(object)val2 == (Object)null))
			{
				Image component2 = ((Component)val2).GetComponent<Image>();
				if (!((Object)(object)component2 == (Object)null))
				{
					((Graphic)component2).color = team.Color * 0.7f;
				}
			}
		}

		private static void AddSwapButton(MatchSetupPlayerEntry ui)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Expected O, but got Unknown
			Transform val = ((Component)ui).transform.Find("Info").Find("Buttons");
			if ((Object)(object)val == (Object)null || (Object)(object)val.Find("SwapTeamButton") != (Object)null)
			{
				return;
			}
			Button kickButton = ui.kickButton;
			if ((Object)(object)kickButton == (Object)null)
			{
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)kickButton).gameObject, val);
			((Object)val2).name = "SwapTeamButton";
			Button component = val2.GetComponent<Button>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			foreach (Transform item in val2.transform)
			{
				Transform val3 = item;
				Object.Destroy((Object)(object)((Component)val3).gameObject);
			}
			GameObject val4 = new GameObject("Label", new Type[1] { typeof(RectTransform) });
			val4.transform.SetParent(val2.transform, false);
			RectTransform component2 = val4.GetComponent<RectTransform>();
			component2.anchorMin = Vector2.zero;
			component2.anchorMax = Vector2.one;
			component2.offsetMin = Vector2.zero;
			component2.offsetMax = Vector2.zero;
			TextMeshProUGUI val5 = val4.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val5).text = "<->";
			((TMP_Text)val5).alignment = (TextAlignmentOptions)514;
			((TMP_Text)val5).fontSize = 24f;
			((TMP_Text)val5).enableAutoSizing = true;
			((TMP_Text)val5).fontSizeMin = 12f;
			((TMP_Text)val5).fontSizeMax = 28f;
			((Graphic)val5).raycastTarget = false;
			((TMP_Text)val5).font = TMP_Settings.defaultFontAsset;
			Image component3 = val2.GetComponent<Image>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.sprite = null;
				((Graphic)component3).color = new Color(0.25f, 0.25f, 0.25f, 0.9f);
			}
			LayoutElement val6 = val2.GetComponent<LayoutElement>();
			if ((Object)(object)val6 == (Object)null)
			{
				val6 = val2.AddComponent<LayoutElement>();
			}
			val6.preferredWidth = 60f;
			val6.preferredHeight = 40f;
			((UnityEventBase)component.onClick).RemoveAllListeners();
			((UnityEvent)component.onClick).AddListener((UnityAction)delegate
			{
				OnSwapClicked(ui);
			});
			val2.SetActive(NetworkServer.active && NetworkClient.active);
		}

		private static void OnSwapClicked(MatchSetupPlayerEntry ui)
		{
			if (!NetworkServer.active)
			{
				return;
			}
			TeamManager instance = TeamManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			ulong guid = ui.Guid;
			foreach (PlayerTeam player in instance.Players)
			{
				if (!((Object)(object)player == (Object)null) && !((Object)(object)player.playerInfo == (Object)null) && player.playerInfo.PlayerId.guid == guid)
				{
					int nextTeam = GetNextTeam(player.teamId, instance);
					instance.SetTeam(player, nextTeam);
					break;
				}
			}
		}

		private static int GetNextTeam(int current, TeamManager manager)
		{
			if (manager.Teams.Count == 0)
			{
				return -1;
			}
			List<int> list = manager.Teams.Keys.OrderBy((int x) => x).ToList();
			int num = list.IndexOf(current);
			if (num == -1 || num + 1 >= list.Count)
			{
				return list[0];
			}
			return list[num + 1];
		}
	}
	public class NameTagUiPatches
	{
		[HarmonyPatch(typeof(NameTagUi), "LateUpdate")]
		[HarmonyPostfix]
		public static void LateUpdate_Postfix(NameTagUi __instance)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)SingletonNetworkBehaviour<CourseManager>.Instance != (Object)null && SingletonNetworkBehaviour<CourseManager>.Instance.currentHoleCourseIndex == -1)
			{
				((Graphic)__instance.tag).color = Color.white;
			}
			if (!((Object)(object)TeamManager.Instance == (Object)null) && (GameModeUtilities.CurrentGamemodeId == null || GameModeUtilities.Modes[GameModeUtilities.CurrentGamemodeId].IsTeamBased) && TeamManager.Instance.SavedTeamIdByGuid != null && TeamManager.Instance.SavedTeamIdByGuid.Count != 0 && TeamManager.Instance.Teams != null && TeamManager.Instance.Teams.Count != 0)
			{
				PlayerInfo playerInfo = __instance.playerInfo;
				if (!((Object)(object)playerInfo == (Object)null))
				{
					int num = TeamManager.Instance.SavedTeamIdByGuid[playerInfo.PlayerId.guid];
					Color color = ((num == -1) ? Color.white : TeamManager.Instance.Teams[num].Color);
					((Graphic)__instance.tag).color = color;
				}
			}
		}
	}
	public class PlayerInfoPatches
	{
		[HarmonyPatch(typeof(PlayerInfo), "Start")]
		[HarmonyPostfix]
		public static void Start_Postfix(PlayerInfo __instance)
		{
			TeamManager instance = TeamManager.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				instance.EnsurePlayerTeam(__instance);
				instance.RequestTeamFromHost(__instance);
			}
		}

		[HarmonyPatch(typeof(PlayerInfo), "ServerInitializeAsParticipant")]
		[HarmonyPostfix]
		public static void ServerInitializeAsParticipant_Postfix(PlayerInfo __instance)
		{
			Plugin.Log.LogInfo((object)"ServerInitializeAsParticipant_Postfix");
			TeamManager instance = TeamManager.Instance;
			Plugin.Log.LogInfo((object)"ServerInitializeAsParticipant_Postfix2");
			if (!((Object)(object)instance == (Object)null))
			{
				Plugin.Log.LogInfo((object)"ServerInitializeAsParticipant_Postfix3");
				instance.EnsurePlayerTeam(__instance);
				Plugin.Log.LogInfo((object)"ServerInitializeAsParticipant_Postfix4");
				instance.TryApplySavedTeam(__instance);
				Plugin.Log.LogInfo((object)"ServerInitializeAsParticipant_Postfix5");
			}
		}
	}
	public class ScoreboardPatches
	{
		private class ColorCache
		{
			public Color background;

			public Color statusBackground;

			public Color infoBackground;

			public Color statsBackground;

			public Color stripes;
		}

		private static readonly Dictionary<ScoreboardEntry, ColorCache> OriginalColors = new Dictionary<ScoreboardEntry, ColorCache>();

		[HarmonyPatch(typeof(ScoreboardEntry), "PopulateWith")]
		[HarmonyPostfix]
		public static void PopulateWith_Postfix(ScoreboardEntry __instance, PlayerState playerState)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			CacheOriginalColors(__instance);
			ApplyTeamColors(__instance, playerState);
		}

		private static void CacheOriginalColors(ScoreboardEntry entry)
		{
			//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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			if (!OriginalColors.ContainsKey(entry))
			{
				OriginalColors[entry] = new ColorCache
				{
					background = ((Graphic)entry.background).color,
					statusBackground = ((Graphic)entry.statusBackground).color,
					infoBackground = ((Graphic)entry.infoBackground).color,
					statsBackground = ((Graphic)entry.statsBackground).color,
					stripes = ((Graphic)entry.stripes).color
				};
			}
		}

		private static void ApplyTeamColors(ScoreboardEntry entry, PlayerState state)
		{
			//IL_0002: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			PlayerInfo val = null;
			if (!GameManager.TryFindPlayerByGuid(state.playerGuid, ref val))
			{
				Plugin.Log.LogWarning((object)$"[Teams] Could not find player with guid={state.playerGuid}");
				RestoreOriginalColors(entry);
				return;
			}
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)$"[Teams] Could not find player with guid={state.playerGuid}");
				RestoreOriginalColors(entry);
				return;
			}
			PlayerTeam component = ((Component)val).GetComponent<PlayerTeam>();
			if ((Object)(object)component == (Object)null || component.teamId < 0)
			{
				Plugin.Log.LogWarning((object)$"[Teams] Could not find player team for player with guid={state.playerGuid}");
				RestoreOriginalColors(entry);
				return;
			}
			if ((Object)(object)TeamManager.Instance == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[Teams] TeamManager not initialised yet; cannot apply team colors.");
				RestoreOriginalColors(entry);
				return;
			}
			if (!TeamManager.Instance.TryGetTeam(component.teamId, out TeamData team))
			{
				Plugin.Log.LogWarning((object)$"[Teams] Could not find team with id={component.teamId}");
				RestoreOriginalColors(entry);
				return;
			}
			Color color = team.Color;
			Color background = OriginalColors[entry].background;
			((Graphic)entry.background).color = Color.Lerp(background, color, 0.5f);
			((Graphic)entry.statusBackground).color = color;
			((Graphic)entry.infoBackground).color = color;
			Color color2 = color;
			color2.a *= 0.5f;
			((Graphic)entry.statsBackground).color = color2;
			Color stripes = OriginalColors[entry].stripes;
			Color color3 = Color.Lerp(stripes, color, 0.5f);
			color3.a = stripes.a;
			((Graphic)entry.stripes).color = color3;
			Plugin.Log.LogInfo((object)("[Teams] Applied team color to " + ((Object)val).name));
		}

		private static void RestoreOriginalColors(ScoreboardEntry entry)
		{
			//IL_0017: 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_0039: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			if (OriginalColors.TryGetValue(entry, out ColorCache value))
			{
				((Graphic)entry.background).color = value.background;
				((Graphic)entry.statusBackground).color = value.statusBackground;
				((Graphic)entry.infoBackground).color = value.infoBackground;
				((Graphic)entry.statsBackground).color = value.statsBackground;
				((Graphic)entry.stripes).color = value.stripes;
			}
		}
	}
	public class TeeOffCountdownPatches
	{
		[HarmonyPatch(typeof(TeeOffCountdown), "Hide")]
		[HarmonyPostfix]
		public static void Hide_Postfix(TeeOffCountdown __instance)
		{
			MatchEvents.InvokeOnTeeOffFinished();
		}
	}
}
namespace Gamemode_Lib.Patches.Features
{
	public class DisableLevelBounds
	{
		[HarmonyPatch(typeof(LevelBoundsTracker), "Awake")]
		[HarmonyPostfix]
		public static void Awake_Prefix(LevelBoundsTracker __instance)
		{
			HideLevelBoundsObject();
		}

		[HarmonyPatch(typeof(LevelBoundsTracker), "InformLevelBoundsStateChanged")]
		[HarmonyPrefix]
		public static void InformLevelBoundsStateChanged_Prefix(ref BoundsState boundsState)
		{
			bool flag = BoundsStateExtensions.HasState(boundsState, (BoundsState)1) || BoundsStateExtensions.HasState(boundsState, (BoundsState)2) || BoundsStateExtensions.HasState(boundsState, (BoundsState)4);
			if (BoundsStateExtensions.HasState(boundsState, (BoundsState)8) && !flag)
			{
				boundsState = (BoundsState)0;
			}
		}

		private static void HideLevelBoundsObject()
		{
			GameObject val = GameObject.Find("Level bounds");
			if (!((Object)(object)val == (Object)null))
			{
				MeshRenderer component = val.GetComponent<MeshRenderer>();
				if (!((Object)(object)component == (Object)null))
				{
					((Renderer)component).forceRenderingOff = true;
				}
			}
		}
	}
	public class HideAheadOfBallMessage
	{
		[HarmonyPatch(typeof(AheadOfBallMessage), "Update")]
		[HarmonyPrefix]
		public static void Update_Prefix(AheadOfBallMessage __instance)
		{
			if (SingletonBehaviour<RestartPrompt>.HasInstance)
			{
				RestartPrompt instance = SingletonBehaviour<RestartPrompt>.Instance;
				Object.Destroy((Object)(object)instance);
			}
			__instance.HideInternal();
		}
	}
	public class StopAutoNextHole
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__OnNextClicked;
		}

		[StructLayout(LayoutKind.Auto)]
		[CompilerGenerated]
		private struct <FixLayoutNextFrame>d__4 : IAsyncStateMachine
		{
			public int <>1__state;

			public AsyncUniTaskMethodBuilder <>t__builder;

			public RectTransform rect;

			private Awaiter <>u__1;

			private void MoveNext()
			{
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Unknown result type (might be due to invalid IL or missing references)
				//IL_004e: 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_000f: 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)
				//IL_0017: 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_002c: Unknown result type (might be due to invalid IL or missing references)
				int num = <>1__state;
				try
				{
					Awaiter val2;
					if (num != 0)
					{
						YieldAwaitable val = UniTask.Yield();
						val2 = ((YieldAwaitable)(ref val)).GetAwaiter();
						if (!((Awaiter)(ref val2)).IsCompleted)
						{
							num = (<>1__state = 0);
							<>u__1 = val2;
							((AsyncUniTaskMethodBuilder)(ref <>t__builder)).AwaitUnsafeOnCompleted<Awaiter, <FixLayoutNextFrame>d__4>(ref val2, ref this);
							return;
						}
					}
					else
					{
						val2 = <>u__1;
						<>u__1 = default(Awaiter);
						num = (<>1__state = -1);
					}
					((Awaiter)(ref val2)).GetResult();
					if (!((Object)(object)rect == (Object)null))
					{
						LayoutRebuilder.ForceRebuildLayoutImmediate(rect);
						Canvas.ForceUpdateCanvases();
					}
				}
				catch (Exception exception)
				{
					<>1__state = -2;
					((AsyncUniTaskMethodBuilder)(ref <>t__builder)).SetException(exception);
					return;
				}
				<>1__state = -2;
				((AsyncUniTaskMethodBuilder)(ref <>t__builder)).SetResult();
			}

			void IAsyncStateMachine.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				this.MoveNext();
			}

			[DebuggerHidden]
			private void SetStateMachine(IAsyncStateMachine stateMachine)
			{
				((AsyncUniTaskMethodBuilder)(ref <>t__builder)).SetStateMachine(stateMachine);
			}

			void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
			{
				//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
				this.SetStateMachine(stateMachine);
			}
		}

		private static GameObject nextButtonInstance;

		public static bool END_GAME;

		[HarmonyPatch(typeof(CourseManager), "OnMatchStateChanged")]
		[HarmonyPrefix]
		public static bool OnMatchStateChanged_Prefix(CourseManager __instance, ref MatchState currentState)
		{
			if ((int)currentState == 6)
			{
				currentState = (MatchState)4;
				ServerInitiateMatchFinish(__instance);
				CreateNextButton();
				return false;
			}
			return true;
		}

		public static void CreateNextButton()
		{
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Expected O, but got Unknown
			if (!NetworkServer.active || !NetworkClient.active || (Object)(object)nextButtonInstance != (Object)null)
			{
				return;
			}
			GameObject val = ((IEnumerable<GameObject>)Resources.FindObjectsOfTypeAll<GameObject>()).FirstOrDefault((Func<GameObject, bool>)((GameObject o) => ((Object)o).name == "Message Button"));
			if ((Object)(object)val == (Object)null)
			{
				Debug.LogError((object)"Message Button not found!");
				return;
			}
			Canvas val2 = ((IEnumerable<Canvas>)Object.FindObjectsOfType<Canvas>()).FirstOrDefault((Func<Canvas, bool>)((Canvas c) => ((Behaviour)c).isActiveAndEnabled && (int)c.renderMode != 2));
			if ((Object)(object)val2 == (Object)null)
			{
				Debug.LogError((object)"Canvas not found!");
				return;
			}
			nextButtonInstance = Object.Instantiate<GameObject>(val);
			((Object)nextButtonInstance).name = "Next Button";
			nextButtonInstance.transform.SetParent(((Component)val2).transform, false);
			RectTransform component = nextButtonInstance.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 0f);
			component.anchorMax = new Vector2(0.5f, 0f);
			component.pivot = new Vector2(0.5f, 0.5f);
			component.anchoredPosition = new Vector2(0f, 120f);
			((Transform)component).localScale = Vector3.one;
			component.sizeDelta = val.GetComponent<RectTransform>().sizeDelta;
			nextButtonInstance.SetActive(true);
			CanvasGroup[] componentsInChildren = nextButtonInstance.GetComponentsInChildren<CanvasGroup>(true);
			foreach (CanvasGroup val3 in componentsInChildren)
			{
				val3.alpha = 1f;
				val3.interactable = true;
				val3.blocksRaycasts = true;
			}
			Graphic[] componentsInChildren2 = nextButtonInstance.GetComponentsInChildren<Graphic>(true);
			foreach (Graphic val4 in componentsInChildren2)
			{
				((Behaviour)val4).enabled = true;
			}
			TextMeshProUGUI componentInChildren = nextButtonInstance.GetComponentInChildren<TextMeshProUGUI>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				((TMP_Text)componentInChildren).text = "Next";
				((TMP_Text)componentInChildren).enableWordWrapping = false;
				((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514;
			}
			Button component2 = nextButtonInstance.GetComponent<Button>();
			if ((Object)(object)component2 != (Object)null)
			{
				((UnityEventBase)component2.onClick).RemoveAllListeners();
				ButtonClickedEvent onClick = component2.onClick;
				object obj = <>O.<0>__OnNextClicked;
				if (obj == null)
				{
					UnityAction val5 = OnNextClicked;
					<>O.<0>__OnNextClicked = val5;
					obj = (object)val5;
				}
				((UnityEvent)onClick).AddListener((UnityAction)obj);
			}
			UniTaskExtensions.Forget(FixLayoutNextFrame(component));
			ShowCursor();
		}

		[AsyncStateMachine(typeof(<FixLayoutNextFrame>d__4))]
		private static UniTask FixLayoutNextFrame(RectTransform rect)
		{
			//IL_0002: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			<FixLayoutNextFrame>d__4 <FixLayoutNextFrame>d__ = default(<FixLayoutNextFrame>d__4);
			<FixLayoutNextFrame>d__.<>t__builder = AsyncUniTaskMethodBuilder.Create();
			<FixLayoutNextFrame>d__.rect = rect;
			<FixLayoutNextFrame>d__.<>1__state = -1;
			((AsyncUniTaskMethodBuilder)(ref <FixLayoutNextFrame>d__.<>t__builder)).Start<<FixLayoutNextFrame>d__4>(ref <FixLayoutNextFrame>d__);
			return ((AsyncUniTaskMethodBuilder)(ref <FixLayoutNextFrame>d__.<>t__builder)).Task;
		}

		private static void ShowCursor()
		{
			CursorManager.SetCursorForceUnlocked(true);
		}

		public static void HideCursor()
		{
			CursorManager.SetCursorForceUnlocked(false);
			CursorManager.ApplyCursorLock();
		}

		private static void OnNextClicked()
		{
			HideCursor();
			CourseManager instance = SingletonNetworkBehaviour<CourseManager>.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				if ((Object)(object)nextButtonInstance != (Object)null)
				{
					Object.Destroy((Object)(object)nextButtonInstance);
					nextButtonInstance = null;
				}
				if (!END_GAME)
				{
					SingletonNetworkBehaviour<CourseManager>.Instance.ServerStartNextMatch(false);
				}
				else
				{
					CourseManager.EndCourse();
				}
			}
		}

		private static void ServerInitiateMatchFinish(CourseManager courseManager)
		{
			ServerFinishMatchDelayed(courseManager, isCourseFinished: false);
		}

		private static void AwardCourseBonus(CourseManager courseManager)
		{
			AwardCourseBonusAsync(courseManager);
		}

		private static async void ServerFinishMatchDelayed(CourseManager courseManager, bool isCourseFinished)
		{
			if (!MatchSetupRules.IsCheatsEnabled() && isCourseFinished)
			{
				AwardCourseBonus(courseManager);
			}
			NextMatchCountdown.Show();
			((TMP_Text)SingletonNetworkBehaviour<NextMatchCountdown>.Instance.message).SetText("Waiting for host!");
			float delayDuration = (isCourseFinished ? GameManager.MatchSettings.FinishCourseDelay : GameManager.MatchSettings.StartNextMatchDelay);
			for (float time = 0f; time < delayDuration; time += Time.deltaTime)
			{
				if (!courseManager.forceDisplayScoreboard && time >= GameManager.MatchSettings.MatchEndScoreboardDisplayDelay)
				{
					courseManager.NetworkforceDisplayScoreboard = true;
				}
				await UniTask.Yield();
				if ((Object)(object)courseManager == (Object)null)
				{
					return;
				}
			}
			if (isCourseFinished)
			{
				courseManager.EndCourseInternal();
			}
		}

		private static async void AwardCourseBonusAsync(CourseManager courseManager)
		{
			await UniTask.WaitForSeconds(1f, false, (PlayerLoopTiming)8, default(CancellationToken), false);
			if ((Object)(object)courseManager == (Object)null)
			{
				return;
			}
			List<PlayerState> sortedPlayerStatesInternal = courseManager.GetSortedPlayerStatesInternal(false);
			if (sortedPlayerStatesInternal.Count <= 1)
			{
				return;
			}
			List<PlayerState> list = default(List<PlayerState>);
			PooledObject<List<PlayerState>> val = CollectionPool<List<PlayerState>, PlayerState>.Get(ref list);
			try
			{
				NetworkConnectionToClient val3 = default(NetworkConnectionToClient);
				for (int i = 0; i < sortedPlayerStatesInternal.Count; i++)
				{
					PlayerState val2 = sortedPlayerStatesInternal[i];
					if (val2.isConnected && !val2.isInSpectatorMode && BNetworkManager.singleton.ServerTryGetConnectionFromPlayerGuid(val2.playerGuid, ref val3))
					{
						float num = ((i == 0) ? 1f : ((i >= sortedPlayerStatesInternal.Count / 2) ? 0.5f : 0.75f));
						courseManager.RpcAwardCourseBonus(val3, num);
					}
				}
			}
			finally
			{
				((IDisposable)val/*cast due to .constrained prefix*/).Dispose();
			}
		}
	}
	public class StopCountdownToMatchEnd
	{
		[HarmonyPatch(typeof(CourseManager), "BeginCountdownToMatchEnd")]
		[HarmonyPrefix]
		public static bool BeginCountdownToMatchEnd_Postfix(CourseManager __instance)
		{
			return false;
		}
	}
}
namespace Gamemode_Lib.Teams
{
	public struct GamemodeEndMessage : NetworkMessage
	{
		public string GamemodeId;
	}
	public struct GamemodeStartMessage : NetworkMessage
	{
		public string GamemodeId;
	}
	public struct TeamAssignMessage : NetworkMessage
	{
		public ulong PlayerGuid;

		public int TeamId;
	}
	public struct TeamDefinitionMessage : NetworkMessage
	{
		public int ID;

		public Color Color;

		public string Name;
	}
	public class PlayerTag : MonoBehaviour
	{
		public enum TagHitType
		{
			Dive,
			GolfSwing
		}

		private Hittable hittable;

		private PlayerTeam playerTeam;

		public static event Action<PlayerInfo, PlayerInfo, TagHitType> PlayerTagged;

		private void Awake()
		{
			hittable = ((Component)this).GetComponent<Hittable>();
			playerTeam = ((Component)this).GetComponent<PlayerTeam>();
			if ((Object)(object)hittable != (Object)null)
			{
				hittable.WasHitByDive += OnWasHitByDive;
				hittable.WasHitByGolfSwing += OnWasHitByGolfSwing;
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)hittable != (Object)null)
			{
				hittable.WasHitByDive -= OnWasHitByDive;
				hittable.WasHitByGolfSwing -= OnWasHitByGolfSwing;
			}
		}

		public void OnWasHitByDive(PlayerMovement hitter)
		{
			if ((GameModeUtilities.CurrentGamemodeId == null || GameModeUtilities.Modes[GameModeUtilities.CurrentGamemodeId].IsTaggingEnabled) && !((Object)(object)hittable == (Object)null))
			{
				PlayerInfo val = (PlayerInfo)(((Object)(object)playerTeam != (Object)null) ? ((object)playerTeam.playerInfo) : ((object)((Component)this).GetComponent<PlayerInfo>()));
				PlayerInfo val2 = (((Object)(object)hitter != (Object)null) ? ((Component)hitter).GetComponent<PlayerInfo>() : null);
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val == (Object)null))
				{
					Plugin.Log.LogInfo((object)("Player " + (((val != null) ? ((Object)((Component)val).gameObject).name : null) ?? ((Object)((Component)this).gameObject).name) + " was hit by dive from " + (((val2 != null) ? ((Object)((Component)val2).gameObject).name : null) ?? ((hitter != null) ? ((Object)((Component)hitter).gameObject).name : null) ?? "unknown")));
					PlayerTag.PlayerTagged?.Invoke(val, val2, TagHitType.Dive);
				}
			}
		}

		public void OnWasHitByGolfSwing(PlayerGolfer hitter, Vector3 vector3, float arg3, SwingType swingType)
		{
			if ((GameModeUtilities.CurrentGamemodeId == null || GameModeUtilities.Modes[GameModeUtilities.CurrentGamemodeId].IsTaggingEnabled) && !((Object)(object)hittable == (Object)null))
			{
				PlayerInfo val = (PlayerInfo)(((Object)(object)playerTeam != (Object)null) ? ((object)playerTeam.playerInfo) : ((object)((Component)this).GetComponent<PlayerInfo>()));
				PlayerInfo val2 = (((Object)(object)hitter != (Object)null) ? ((Component)hitter).GetComponent<PlayerInfo>() : null);
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val == (Object)null))
				{
					Plugin.Log.LogInfo((object)("Player " + (((val != null) ? ((Object)((Component)val).gameObject).name : null) ?? ((Object)((Component)this).gameObject).name) + " was hit by swing from " + (((val2 != null) ? ((Object)((Component)val2).gameObject).name : null) ?? ((hitter != null) ? ((Object)((Component)hitter).gameObject).name : null) ?? "unknown")));
					PlayerTag.PlayerTagged?.Invoke(val, val2, TagHitType.GolfSwing);
				}
			}
		}
	}
	public class PlayerTeam : MonoBehaviour
	{
		public int teamId = -1;

		public PlayerInfo playerInfo;

		private void Awake()
		{
			((Component)this).gameObject.AddComponent<PlayerTag>();
			playerInfo = ((Component)this).GetComponent<PlayerInfo>();
			if ((Object)(object)TeamManager.Instance != (Object)null)
			{
				TeamManager.Instance.RegisterPlayer(this);
			}
		}

		public void SetLocalTeam(int newTeam)
		{
			int num = teamId;
			teamId = newTeam;
			Plugin.Log.LogInfo((object)$"[LOCAL] Team changed {num} -> {newTeam}");
			TeamManager.Instance?.NotifyTeamChanged(this, num, newTeam);
		}

		private void OnDestroy()
		{
			TeamManager.Instance?.UnregisterPlayer(this);
		}
	}
	public class TeamData
	{
		public int ID;

		public Color Color;

		public string Name;

		public int Score;

		public readonly HashSet<PlayerTeam> Members = new HashSet<PlayerTeam>();

		public int MemberCount => Members.Count;

		internal void AddMember(PlayerTeam player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				Members.Add(player);
			}
		}

		internal void RemoveMember(PlayerTeam player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				Members.Remove(player);
			}
		}

		internal void ClearMembers()
		{
			Members.Clear();
		}
	}
	public class TeamManager : MonoBehaviour
	{
		private readonly struct BroadcastSuppressScope : IDisposable
		{
			private readonly TeamManager _manager;

			public BroadcastSuppressScope(TeamManager manager)
			{
				_manager = manager;
				_manager._broadcastSuppressDepth++;
			}

			public void Dispose()
			{
				_manager._broadcastSuppressDepth = Mathf.Max(0, _manager._broadcastSuppressDepth - 1);
			}
		}

		public static TeamManager Instance;

		public readonly Dictionary<int, TeamData> Teams = new Dictionary<int, TeamData>();

		public readonly HashSet<PlayerTeam> Players = new HashSet<PlayerTeam>();

		public readonly Dictionary<ulong, int> SavedTeamIdByGuid = new Dictionary<ulong, int>();

		public PlayerTeam LocalPlayerTeam;

		private readonly Dictionary<ulong, int> _pendingTeamIdByGuid = new Dictionary<ulong, int>();

		private readonly HashSet<int> _requestedTeamInstanceIds = new HashSet<int>();

		private int _broadcastSuppressDepth;

		private int? _lastAllPlayersOnOneTeamId;

		private bool IsBroadcastSuppressed => _broadcastSuppressDepth > 0;

		public event Action<TeamData> AllPlayersOnOneTeam;

		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			Instance = this;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			foreach (PlayerTeam item in PlayerTeamsInScene())
			{
			}
		}

		public void RegisterPlayer(PlayerTeam player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				Players.Add(player);
				if (player.teamId >= 0)
				{
					EnsureTeamData(player.teamId)?.AddMember(player);
				}
				EvaluateAllPlayersOnOneTeam();
			}
		}

		public void UnregisterPlayer(PlayerTeam player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			Players.Remove(player);
			foreach (TeamData value in Teams.Values)
			{
				value?.RemoveMember(player);
			}
			EvaluateAllPlayersOnOneTeam();
		}

		public PlayerTeam EnsurePlayerTeam(PlayerInfo info)
		{
			if ((Object)(object)info == (Object)null)
			{
				return null;
			}
			PlayerTeam playerTeam = default(PlayerTeam);
			if (!((Component)info).TryGetComponent<PlayerTeam>(ref playerTeam) || (Object)(object)playerTeam == (Object)null)
			{
				playerTeam = ((Component)info).gameObject.AddComponent<PlayerTeam>();
			}
			playerTeam.playerInfo = info;
			RegisterPlayer(playerTeam);
			if ((Object)(object)SingletonBehaviour<GameManager>.Instance?.localPlayerInfo == (Object)(object)info)
			{
				LocalPlayerTeam = playerTeam;
			}
			ApplyPendingTeamIfAny(info, playerTeam);
			return playerTeam;
		}

		public bool TryRefreshLocalPlayerTeam()
		{
			LocalPlayerTeam = EnsurePlayerTeam(SingletonBehaviour<GameManager>.Instance?.localPlayerInfo);
			return (Object)(object)LocalPlayerTeam != (Object)null;
		}

		public void SetTeam(PlayerTeam player, int teamId, bool broadcastToClients = true)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			if (broadcastToClients)
			{
				player.SetLocalTeam(teamId);
				return;
			}
			using (SuppressBroadcast())
			{
				player.SetLocalTeam(teamId);
			}
		}

		internal void NotifyTeamChanged(PlayerTeam player, int oldTeamId, int newTeamId)
		{
			MoveMember(player, oldTeamId, newTeamId);
			if (NetworkServer.active && TryGetGuid(player, out var guid))
			{
				SaveTeamId(guid, newTeamId);
				Plugin.Log.LogInfo((object)$"[Teams] Persisted team change guid={guid}: {oldTeamId} -> {newTeamId}");
				if (!IsBroadcastSuppressed)
				{
					BroadcastTeam(guid, newTeamId);
				}
			}
			EvaluateAllPlayersOnOneTeam();
		}

		public void AssignUnAssignedPlayersToTeams(bool broadcastToClients = true)
		{
			if (!NetworkServer.active || Teams.Count <= 0)
			{
				return;
			}
			int num = 0;
			foreach (PlayerTeam item in ValidPlayers())
			{
				if (item.teamId == -1)
				{
					SetTeam(item, num % Teams.Count, broadcastToClients);
					num++;
				}
			}
		}

		public void CreateAndAssignTeams(int teamCount)
		{
			if (!NetworkServer.active || teamCount <= 0)
			{
				return;
			}
			Teams.Clear();
			for (int i = 0; i < teamCount; i++)
			{
				SendTeamDefinitionToClients(EnsureTeamData(i));
			}
			int num = 0;
			foreach (PlayerTeam item in PlayerTeamsInScene())
			{
				SetTeam(item, num % teamCount);
				num++;
			}
		}

		public void ResetTeams()
		{
			if (!NetworkServer.active)
			{
				return;
			}
			foreach (PlayerTeam item in PlayerTeamsInScene())
			{
				SetTeam(item, -1);
			}
		}

		public void ResetToDefaults(bool broadcastToClients)
		{
			SavedTeamIdByGuid.Clear();
			_pendingTeamIdByGuid.Clear();
			_requestedTeamInstanceIds.Clear();
			_lastAllPlayersOnOneTeamId = null;
			Teams.Clear();
			foreach (PlayerTeam item in ValidPlayers())
			{
				SetTeam(item, -1, broadcastToClients);
			}
			LocalPlayerTeam = null;
			TryRefreshLocalPlayerTeam();
			EvaluateAllPlayersOnOneTeam();
		}

		public void SaveCurrentTeams()
		{
			SavedTeamIdByGuid.Clear();
			foreach (var (playerTeam, val) in ValidPlayersWithInfo())
			{
				SaveTeamId(val.PlayerId.guid, playerTeam.teamId);
			}
		}

		public int ReloadSavedTeams(bool broadcastToClients = true)
		{
			int num = 0;
			foreach (var (player, val) in ValidPlayersWithInfo())
			{
				if (SavedTeamIdByGuid.TryGetValue(val.PlayerId.guid, out var value))
				{
					SetTeam(player, value, broadcastToClients);
					num++;
				}
			}
			return num;
		}

		public bool TryApplySavedTeam(PlayerInfo info, bool broadcastToClients = true)
		{
			if ((Object)(object)info == (Object)null)
			{
				return false;
			}
			if (!SavedTeamIdByGuid.TryGetValue(info.PlayerId.guid, out var value))
			{
				return false;
			}
			SetTeam(EnsurePlayerTeam(info), value, broadcastToClients);
			return true;
		}

		public void RequestTeamFromHost(PlayerInfo info)
		{
			if (!((Object)(object)info == (Object)null) && NetworkClient.active && !NetworkServer.active && !((Object)(object)SingletonBehaviour<GameManager>.Instance?.localPlayerInfo != (Object)(object)info))
			{
				int instanceID = ((Object)info).GetInstanceID();
				if (_requestedTeamInstanceIds.Add(instanceID))
				{
					((MonoBehaviour)this).StartCoroutine(RequestTeamFromHostCoroutine(info, instanceID));
				}
			}
		}

		private IEnumerator RequestTeamFromHostCoroutine(PlayerInfo info, int instanceId)
		{
			try
			{
				for (int i = 0; i < 300; i++)
				{
					if ((Object)(object)info == (Object)null)
					{
						yield break;
					}
					ulong guid = info.PlayerId.guid;
					if (guid != 0L)
					{
						NetworkMessageBootstrap.Register();
						NetworkClient.Send<TeamRequestMessage>(new TeamRequestMessage
						{
							PlayerGuid = guid
						}, 0);
						Plugin.Log.LogInfo((object)$"[Teams] Requested team for local player guid={guid}");
						yield break;
					}
					yield return null;
				}
				Plugin.Log.LogWarning((object)"[Teams] Timed out waiting for local player guid; team request not sent.");
			}
			finally
			{
				_requestedTeamInstanceIds.Remove(instanceId);
			}
		}

		public void HandleTeamRequest(NetworkConnectionToClient conn, TeamRequestMessage msg)
		{
			if (NetworkServer.active && conn != null && msg.PlayerGuid != 0L)
			{
				PlayerTeam playerTeam = FindPlayerTeamByGuid(msg.PlayerGuid);
				if ((Object)(object)playerTeam == (Object)null)
				{
					Plugin.Log.LogInfo((object)$"[Teams] Team request for guid={msg.PlayerGuid} but no PlayerInfo/PlayerTeam found yet.");
					return;
				}
				((NetworkConnection)conn).Send<TeamAssignMessage>(new TeamAssignMessage
				{
					PlayerGuid = msg.PlayerGuid,
					TeamId = playerTeam.teamId
				}, 0);
			}
		}

		public void ApplyTeamMessage(TeamAssignMessage msg)
		{
			if (NetworkClient.active && msg.PlayerGuid != 0L)
			{
				SaveTeamId(msg.PlayerGuid, msg.TeamId);
				PlayerTeam playerTeam = FindPlayerTeamByGuid(msg.PlayerGuid);
				if ((Object)(object)playerTeam == (Object)null)
				{
					_pendingTeamIdByGuid[msg.PlayerGuid] = msg.TeamId;
				}
				else
				{
					SetTeam(playerTeam, msg.TeamId, broadcastToClients: false);
				}
			}
		}

		public void ApplyTeamDefinitionMessage(TeamDefinitionMessage msg)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			SetTeamDefinition(msg.ID, msg.Name, msg.Color);
		}

		public void SendTeamDefinitionToClients(TeamData team)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (NetworkServer.active && team != null)
			{
				NetworkServer.SendToAll<TeamDefinitionMessage>(new TeamDefinitionMessage
				{
					ID = team.ID,
					Name = team.Name,
					Color = team.Color
				}, 0, false);
			}
		}

		public void ClearTeamDefinitions()
		{
			Teams.Clear();
		}

		public bool TryGetTeam(int teamId, out TeamData team)
		{
			return Teams.TryGetValue(teamId, out team);
		}

		private void ApplyPendingTeamIfAny(PlayerInfo info, PlayerTeam team)
		{
			ulong guid = info.PlayerId.guid;
			if (_pendingTeamIdByGuid.TryGetValue(guid, out var value))
			{
				SetTeam(team, value, broadcastToClients: false);
				_pendingTeamIdByGuid.Remove(guid);
			}
		}

		private void BroadcastTeam(ulong guid, int teamId)
		{
			if (NetworkServer.active && guid != 0L)
			{
				NetworkServer.SendToAll<TeamAssignMessage>(new TeamAssignMessage
				{
					PlayerGuid = guid,
					TeamId = teamId
				}, 0, false);
			}
		}

		private void SaveTeamId(ulong guid, int teamId)
		{
			if (guid != 0L)
			{
				if (teamId < 0)
				{
					SavedTeamIdByGuid.Remove(guid);
				}
				else
				{
					SavedTeamIdByGuid[guid] = teamId;
				}
			}
		}

		private bool TryGetGuid(PlayerTeam player, out ulong guid)
		{
			ulong? obj;
			if (player == null)
			{
				obj = null;
			}
			else
			{
				PlayerInfo playerInfo = player.playerInfo;
				obj = ((playerInfo != null) ? new ulong?(playerInfo.PlayerId.guid) : ((ulong?)null));
			}
			ulong? num = obj;
			guid = num.GetValueOrDefault();
			return guid != 0;
		}

		private PlayerTeam FindPlayerTeamByGuid(ulong guid)
		{
			if (guid == 0L)
			{
				return null;
			}
			foreach (var (result, val) in ValidPlayersWithInfo())
			{
				if (val.PlayerId.guid == guid)
				{
					return result;
				}
			}
			foreach (PlayerTeam item in PlayerTeamsInScene())
			{
				if ((Object)(object)item.playerInfo != (Object)null && item.playerInfo.PlayerId.guid == guid)
				{
					return item;
				}
			}
			return null;
		}

		private IEnumerable<PlayerTeam> ValidPlayers()
		{
			Players.RemoveWhere((PlayerTeam player) => (Object)(object)player == (Object)null);
			foreach (PlayerTeam player in Players)
			{
				yield return player;
			}
		}

		private IEnumerable<(PlayerTeam Player, PlayerInfo Info)> ValidPlayersWithInfo()
		{
			foreach (PlayerTeam item in ValidPlayers())
			{
				if ((Object)(object)item.playerInfo != (Object)null)
				{
					yield return (Player: item, Info: item.playerInfo);
				}
			}
		}

		private IEnumerable<PlayerTeam> PlayerTeamsInScene()
		{
			PlayerInfo[] array = Object.FindObjectsByType<PlayerInfo>((FindObjectsSortMode)0);
			foreach (PlayerInfo info in array)
			{
				PlayerTeam playerTeam = EnsurePlayerTeam(info);
				if ((Object)(object)playerTeam != (Object)null)
				{
					yield return playerTeam;
				}
			}
		}

		private void MoveMember(PlayerTeam player, int oldTeamId, int newTeamId)
		{
			if (!((Object)(object)player == (Object)null))
			{
				if (oldTeamId >= 0 && Teams.TryGetValue(oldTeamId, out TeamData value))
				{
					value?.RemoveMember(player);
				}
				if (newTeamId >= 0)
				{
					EnsureTeamData(newTeamId)?.AddMember(player);
				}
			}
		}

		private TeamData EnsureTeamData(int teamId)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (teamId < 0)
			{
				return null;
			}
			if (!Teams.TryGetValue(teamId, out TeamData value) || value == null)
			{
				return SetTeamDefinition(teamId, $"Team {teamId + 1}", GetDefaultColor(teamId));
			}
			return value;
		}

		private TeamData SetTeamDefinition(int id, string name, Color color)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (!Teams.TryGetValue(id, out TeamData value) || value == null)
			{
				value = (Teams[id] = new TeamData());
			}
			value.ID = id;
			value.Name = name;
			value.Color = color;
			return value;
		}

		private void EvaluateAllPlayersOnOneTeam()
		{
			int? allPlayersSingleTeamId = GetAllPlayersSingleTeamId();
			if (!allPlayersSingleTeamId.HasValue)
			{
				_lastAllPlayersOnOneTeamId = null;
			}
			else if (_lastAllPlayersOnOneTeamId != allPlayersSingleTeamId)
			{
				_lastAllPlayersOnOneTeamId = allPlayersSingleTeamId;
				this.AllPlayersOnOneTeam?.Invoke(EnsureTeamData(allPlayersSingleTeamId.Value));
			}
		}

		private int? GetAllPlayersSingleTeamId()
		{
			int? result = null;
			int num = 0;
			foreach (PlayerTeam item in ValidPlayers())
			{
				if (item.teamId < 0)
				{
					return null;
				}
				int valueOrDefault = result.GetValueOrDefault();
				if (!result.HasValue)
				{
					valueOrDefault = item.teamId;
					result = valueOrDefault;
				}
				if (item.teamId != result.Value)
				{
					return null;
				}
				num++;
			}
			if (num != 0)
			{
				return result;
			}
			return null;
		}

		private IDisposable SuppressBroadcast()
		{
			return new BroadcastSuppressScope(this);
		}

		private static Color GetDefaultColor(int teamId)
		{
			//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_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_0028: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			return (Color)(teamId switch
			{
				0 => Color.red, 
				1 => Color.blue, 
				2 => Color.green, 
				3 => Color.yellow, 
				_ => Color.white, 
			});
		}
	}
}
namespace Gamemode_Lib.Network.Messages
{
	public struct ConfigEntry
	{
		public string Key;

		public ConfigValueType Type;

		public string StringValue;

		public int IntValue;

		public float FloatValue;

		public bool BoolValue;

		public static ConfigEntry From(string key, ConfigValue value)
		{
			ConfigEntry result = new ConfigEntry
			{
				Key = key,
				Type = value.Type
			};
			switch (value.Type)
			{
			case ConfigValueType.String:
				result.StringValue = value.StringValue;
				break;
			case ConfigValueType.Int:
				result.IntValue = value.IntValue;
				break;
			case ConfigValueType.Float:
				result.FloatValue = value.FloatValue;
				break;
			case ConfigValueType.Bool:
				result.BoolValue = value.BoolValue;
				break;
			}
			return result;
		}

		public ConfigValue ToValue()
		{
			return Type switch
			{
				ConfigValueType.String => ConfigValue.FromString(StringValue), 
				ConfigValueType.Int => ConfigValue.FromInt(IntValue), 
				ConfigValueType.Float => ConfigValue.FromFloat(FloatValue), 
				ConfigValueType.Bool => ConfigValue.FromBool(BoolValue), 
				_ => ConfigValue.FromString(StringValue), 
			};
		}
	}
	public struct ConfigScopeFullMessage : NetworkMessage
	{
		public string ScopeId;

		public List<ConfigEntry> Entries;
	}
	public struct ConfigScopeRequestMessage : NetworkMessage
	{
		public string ScopeId;
	}
	public struct ConfigScopeUpdateMessage : NetworkMessage
	{
		public string ScopeId;

		public ConfigEntry Entry;
	}
	public struct RaycastRequestMessage : NetworkMessage
	{
		public string Purpose;

		public ulong Guid;

		public Vector3 Origin;

		public Vector3 Direction;

		public float MaxDistance;

		public int RaycastMask;
	}
	public struct RaycastResultMessage : NetworkMessage
	{
		public string Purpose;

		public ulong Guid;

		public Vector3 Origin;

		public Vector3 Direction;

		public float MaxDistance;

		public int RaycastMask;

		public bool HasHit;

		public Vector3 HitPoint;

		public Vector3 HitNormal;

		public float HitDistance;

		public string HitObjectName;

		public string ClosestValidRootObjectName;
	}
	public struct TeamRequestMessage : NetworkMessage
	{
		public ulong PlayerGuid;
	}
}
namespace Gamemode_Lib.Events
{
	public static class MatchEvents
	{
		public static event Action OnTeeOffFinished;

		public static void InvokeOnTeeOffFinished()
		{
			MatchEvents.OnTeeOffFinished?.Invoke();
		}
	}
	public static class PlayerEvents
	{
		private static bool _initialized;

		public static event Action OnLocalPlayerLoaded;

		public static event Action<PlayerInfo> OnRemotePlayerLoaded;

		public static void Init()
		{
			if (_initialized)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[PlayerEvents] Init skipped; already initialized.");
				}
				return;
			}
			GameManager.LocalPlayerRegistered += OnLocalPlayerRegistered;
			GameManager.RemotePlayerRegistered += OnRemotePlayerRegistered;
			_initialized = true;
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)"[PlayerEvents] Initialized and subscribed to GameManager.LocalPlayerRegistered & GameManager.RemotePlayerRegistered.");
			}
		}

		public static void Shutdown(bool clearStaticEvents = true)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"[PlayerEvents] Shutdown called. initialized={_initialized} clearStaticEvents={clearStaticEvents}");
			}
			if (_initialized)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"[PlayerEvents] Unsubscribing from ameManager.LocalPlayerRegistered & GameManager.RemotePlayerRegistered.");
				}
				GameManager.LocalPlayerRegistered -= OnLocalPlayerRegistered;
				GameManager.RemotePlayerRegistered -= OnRemotePlayerRegistered;
				_initialized = false;
			}
			if (clearStaticEvents)
			{
				PlayerEvents.OnLocalPlayerLoaded = null;
				PlayerEvents.OnRemotePlayerLoaded = null;
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)"[PlayerEvents] Static events cleared.");
				}
			}
		}

		private static void OnLocalPlayerRegistered()
		{
			PlayerInfo localPlayerInfo = GameManager.LocalPlayerInfo;
			Plugin.Log.LogInfo((object)("[GamemodeLib] OnLocalPlayerRegistered for player " + ((Object)localPlayerInfo).name));
			PlayerEvents.OnLocalPlayerLoaded?.Invoke();
		}

		private static void OnRemotePlayerRegistered(PlayerInfo playerInfo)
		{
			Plugin.Log.LogInfo((object)("[GamemodeLib] OnRemotePlayerRegistered for player " + ((Object)playerInfo).name));
			PlayerEvents.OnRemotePlayerLoaded?.Invoke(playerInfo);
		}
	}
	public static class SceneEvents
	{
		private const string LobbySceneName = "Driving range";

		private static bool _initialized;

		public static event Action<Scene, Scene> OnNextHole;

		public static event Action<Scene, Scene> OnReturnToLobby;

		public static void Init()
		{
			if (_initialized)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[Sce