Decompiled source of Gamemode Lib v0.2.1
com.github.glarmer.Gamemode_Lib.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
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