using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using GTFO.API;
using GTFOPause.Net;
using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using SNetwork;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("GTFOPause")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+8108b790a1c68400d1ad77fe8493f97988df8ec0")]
[assembly: AssemblyProduct("GTFOPause")]
[assembly: AssemblyTitle("GTFOPause")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace GTFOPause
{
internal static class Logger
{
private static ManualLogSource s_log;
public static void Setup(ManualLogSource log)
{
s_log = log;
}
public static void Info(object data)
{
s_log.LogInfo(data);
}
public static void Warn(object data)
{
s_log.LogWarning(data);
}
public static void Error(object data)
{
s_log.LogError(data);
}
public static void Debug(object data)
{
s_log.LogDebug(data);
}
}
internal static class Notify
{
private const int BannerPriority = 100;
private static bool s_bannerShown;
public static string PausedBy { get; set; }
public static void Event(string message)
{
Logger.Info(message);
if (PauseConfig.ShowNotifications.Value)
{
PlayerGuiLayer playerLayer = GuiManager.PlayerLayer;
if (playerLayer != null && !((Object)(object)playerLayer.m_gameEventLog == (Object)null))
{
playerLayer.m_gameEventLog.AddLogItem(message, (eGameEventChatLogType)2);
}
}
}
public static void UpdateBanner()
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
bool flag = PauseState.IsPaused && PauseConfig.ShowNotifications.Value && !PauseRules.IsMenu(FocusStateManager.CurrentState);
if (!flag && !s_bannerShown)
{
return;
}
InteractionGuiLayer interactionLayer = GuiManager.InteractionLayer;
if (interactionLayer != null)
{
if (flag)
{
interactionLayer.SetMessage(BannerText(), (ePUIMessageStyle)1, 100);
interactionLayer.MessageVisible = true;
HideIntroBoxes(interactionLayer.m_message);
}
else
{
interactionLayer.MessageVisible = false;
}
s_bannerShown = flag;
}
}
private static void HideIntroBoxes(PUI_InteractionPrompt prompt)
{
if (!((Object)(object)prompt == (Object)null))
{
if ((Object)(object)prompt.m_whiteBox != (Object)null && prompt.m_whiteBox.activeSelf)
{
prompt.m_whiteBox.SetActive(false);
}
if ((Object)(object)prompt.m_whiteBoxWide != (Object)null && prompt.m_whiteBoxWide.activeSelf)
{
prompt.m_whiteBoxWide.SetActive(false);
}
}
}
private static string BannerText()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
string text = ((object)PauseConfig.PauseHotkey.Value/*cast due to .constrained prefix*/).ToString();
if (PausedBy != null)
{
return $"PAUSED by {PausedBy} - press {text} to resume";
}
return "PAUSED - press " + text + " to resume";
}
}
internal static class PauseConfig
{
public static ConfigEntry<bool> PauseOnMenu { get; private set; }
public static ConfigEntry<KeyCode> PauseHotkey { get; private set; }
public static ConfigEntry<bool> ShowNotifications { get; private set; }
public static ConfigEntry<bool> AllowMultiplayerPause { get; private set; }
public static void Bind(ConfigFile config)
{
PauseOnMenu = config.Bind<bool>("General", "PauseOnMenu", false, "Solo only: also pause while the Esc menu is open, like Risk of Rain 2 singleplayer. Off by default: the hotkey is the pause and Esc is just the menu.");
PauseHotkey = config.Bind<KeyCode>("General", "PauseHotkey", (KeyCode)289, "Toggles the pause. Solo: pauses without opening the menu. Multiplayer: asks the host to pause everyone.");
ShowNotifications = config.Bind<bool>("General", "ShowNotifications", true, "Show the PAUSED banner and the paused/resumed lines in the game event log.");
AllowMultiplayerPause = config.Bind<bool>("Multiplayer", "AllowMultiplayerPause", true, "Host only: let any player with GTFOPause pause the whole squad. Every human player needs the mod. Turn off to keep pausing solo-only.");
}
}
internal class PauseController : MonoBehaviour
{
public PauseController(IntPtr ptr)
: base(ptr)
{
}
private void Update()
{
try
{
PauseLogic.Tick();
}
catch (Exception data)
{
Logger.Error(data);
}
}
private void LateUpdate()
{
try
{
Notify.UpdateBanner();
}
catch (Exception data)
{
Logger.Error(data);
}
}
}
internal static class PauseLogic
{
private static int s_lastHumanCount = -1;
private static ulong s_lastMaster;
public static void OnFocusStateChanged(eFocusState oldState, eFocusState newState)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Invalid comparison between Unknown and I4
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
if (!PauseRules.InLevel)
{
return;
}
if ((int)newState == 17)
{
ForceResume("popup opened");
}
else if (PauseRules.IsMenu(newState))
{
if (PauseConfig.PauseOnMenu.Value && PauseRules.IsSolo)
{
PauseState.Add(PauseReason.Menu);
}
}
else if (PauseRules.IsMenu(oldState))
{
PauseState.Remove(PauseReason.Menu);
}
}
public static void OnGameStateChanged(eGameStateName oldState, eGameStateName newState)
{
//IL_0018: 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)
ForceResume($"game state {oldState} -> {newState}");
}
public static void OnCheckpointReloaded()
{
ForceResume("checkpoint reloaded");
}
public static void Tick()
{
//IL_000f: 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)
WatchSession();
PauseNet.Tick();
if (Input.GetKeyDown(PauseConfig.PauseHotkey.Value) && PauseRules.HotkeyAllowedIn(FocusStateManager.CurrentState))
{
OnHotkey();
}
}
public static void ForceResume(string why)
{
if (PauseState.Reasons != PauseReason.None)
{
Logger.Info("Force resume: " + why);
PauseNet.OnLocalForceResume();
PauseState.Clear();
}
}
private static void OnHotkey()
{
if (!PauseRules.InLevel)
{
return;
}
if (PauseRules.IsSolo)
{
if (PauseState.IsPaused)
{
PauseState.Clear();
}
else
{
PauseState.Add(PauseReason.Hotkey);
}
}
else
{
PauseNet.OnHotkey();
}
}
private static void WatchSession()
{
int num = PauseRules.HumanCount();
ulong num2 = ((SNet.HasMaster && (Object)(object)SNet.Master != (Object)null) ? SNet.Master.Lookup : 0);
bool flag = num != s_lastHumanCount || num2 != s_lastMaster;
s_lastHumanCount = num;
s_lastMaster = num2;
if (PauseState.IsPaused)
{
if (!PauseRules.InLevel)
{
ForceResume("left the level");
}
else if (flag)
{
ForceResume("players or host changed");
}
}
}
}
internal static class PauseRules
{
public static bool InLevel => (int)GameStateManager.CurrentStateName == 10;
public static bool IsSolo
{
get
{
if (SNet.IsMaster)
{
return HumanCount() <= 1;
}
return false;
}
}
public static int HumanCount()
{
List<SNet_Player> lobbyPlayers = SNet.LobbyPlayers;
if (lobbyPlayers == null)
{
return 0;
}
int num = 0;
for (int i = 0; i < lobbyPlayers.Count; i++)
{
SNet_Player val = lobbyPlayers[i];
if ((Object)(object)val != (Object)null && !val.IsBot)
{
num++;
}
}
return num;
}
public static bool IsMenu(eFocusState state)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
return (int)state == 2;
}
public static bool HotkeyAllowedIn(eFocusState state)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Invalid comparison between Unknown and I4
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Invalid comparison between Unknown and I4
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Invalid comparison between Unknown and I4
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Invalid comparison between Unknown and I4
if ((int)state <= 5)
{
if ((int)state == 3 || (int)state == 5)
{
goto IL_0017;
}
}
else if ((int)state == 8 || (int)state == 14)
{
goto IL_0017;
}
bool flag = false;
goto IL_001d;
IL_0017:
flag = true;
goto IL_001d;
IL_001d:
return !flag;
}
public static bool BlocksInputIn(eFocusState state)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Invalid comparison between Unknown and I4
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Invalid comparison between Unknown and I4
if ((int)state == 4 || (int)state == 6 || (int)state == 15)
{
return true;
}
return false;
}
public static bool AllowedWhilePaused(InputAction action)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0003: Invalid comparison between Unknown and I4
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Invalid comparison between Unknown and I4
if ((int)action == 36 || action - 55 <= 2)
{
return true;
}
return false;
}
}
[Flags]
internal enum PauseReason
{
None = 0,
Menu = 1,
Hotkey = 2,
Remote = 4
}
internal static class PauseState
{
private static PauseReason s_reasons;
private static float s_savedTimeScale = 1f;
public static bool IsPaused { get; private set; }
public static PauseReason Reasons => s_reasons;
public static float FrozenExpeditionTime { get; private set; }
public static void Add(PauseReason reason)
{
Set(s_reasons | reason);
}
public static void Remove(PauseReason reason)
{
Set(s_reasons & ~reason);
}
public static void Clear()
{
Set(PauseReason.None);
}
private static void Set(PauseReason reasons)
{
s_reasons = reasons;
bool flag = reasons != PauseReason.None;
if (flag != IsPaused)
{
IsPaused = flag;
if (flag)
{
s_savedTimeScale = ((Time.timeScale > 0f) ? Time.timeScale : 1f);
Time.timeScale = 0f;
FrozenExpeditionTime = Clock.ExpeditionProgressionTime;
SetAudioSuspended(suspend: true);
Logger.Info($"Paused ({reasons})");
}
else
{
Time.timeScale = s_savedTimeScale;
SetAudioSuspended(suspend: false);
Logger.Info("Resumed");
}
}
}
private static void SetAudioSuspended(bool suspend)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0003: 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_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Invalid comparison between Unknown and I4
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
try
{
AKRESULT val = (suspend ? AkSoundEngine.Suspend(false) : AkSoundEngine.WakeupFromSuspend());
if ((int)val != 1)
{
Logger.Warn($"Wwise {(suspend ? "Suspend" : "WakeupFromSuspend")} returned {val}");
}
}
catch (Exception value)
{
Logger.Error($"Wwise {(suspend ? "Suspend" : "WakeupFromSuspend")} failed: {value}");
}
}
}
[BepInPlugin("mrwave.GTFOPause", "GTFOPause", "0.1.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BasePlugin
{
[CompilerGenerated]
private static class <>O
{
public static FocusStateChangedDelegate <0>__OnFocusStateChanged;
public static GameStateChangedDelegate <1>__OnGameStateChanged;
public static Action <2>__OnCheckpointReloaded;
}
public const string Guid = "mrwave.GTFOPause";
public const string Name = "GTFOPause";
public const string Version = "0.1.1";
public override void Load()
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Expected O, but got Unknown
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Expected O, but got Unknown
Logger.Setup(((BasePlugin)this).Log);
PauseConfig.Bind(((BasePlugin)this).Config);
new Harmony("mrwave.GTFOPause").PatchAll();
((BasePlugin)this).AddComponent<PauseController>();
PauseNet.Register();
object obj = <>O.<0>__OnFocusStateChanged;
if (obj == null)
{
FocusStateChangedDelegate val = PauseLogic.OnFocusStateChanged;
<>O.<0>__OnFocusStateChanged = val;
obj = (object)val;
}
EventAPI.OnFocusStateChanged += (FocusStateChangedDelegate)obj;
object obj2 = <>O.<1>__OnGameStateChanged;
if (obj2 == null)
{
GameStateChangedDelegate val2 = PauseLogic.OnGameStateChanged;
<>O.<1>__OnGameStateChanged = val2;
obj2 = (object)val2;
}
EventAPI.OnGameStateChanged += (GameStateChangedDelegate)obj2;
EventAPI.OnCheckpointReloaded += PauseLogic.OnCheckpointReloaded;
Logger.Info("GTFOPause 0.1.1 loaded");
}
}
}
namespace GTFOPause.Patches
{
[HarmonyPatch(typeof(InputMapper))]
internal static class InputPatches
{
private static bool ShouldBlock(InputAction action)
{
//IL_0007: 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)
if (PauseState.IsPaused && !PauseRules.AllowedWhilePaused(action))
{
return PauseRules.BlocksInputIn(FocusStateManager.CurrentState);
}
return false;
}
[HarmonyPatch("DoGetButton")]
[HarmonyPrefix]
private static bool DoGetButton(InputAction action, ref bool __result)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
return Pass(action, ref __result);
}
[HarmonyPatch("DoGetButtonDown")]
[HarmonyPrefix]
private static bool DoGetButtonDown(InputAction action, ref bool __result)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
return Pass(action, ref __result);
}
[HarmonyPatch("DoGetButtonUp")]
[HarmonyPrefix]
private static bool DoGetButtonUp(InputAction action, ref bool __result)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
return Pass(action, ref __result);
}
[HarmonyPatch("DoGetAxis")]
[HarmonyPrefix]
private static bool DoGetAxis(InputAction action, ref float __result)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
if (!ShouldBlock(action))
{
return true;
}
__result = 0f;
return false;
}
private static bool Pass(InputAction action, ref bool result)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
if (!ShouldBlock(action))
{
return true;
}
result = false;
return false;
}
}
[HarmonyPatch(typeof(CM_TimedButton))]
internal static class MenuPatches
{
[HarmonyPatch("OnHoverUpdate")]
[HarmonyPrefix]
private static void OnHoverUpdatePrefix(out float __state)
{
__state = UseRealDelta();
}
[HarmonyPatch("OnHoverUpdate")]
[HarmonyPostfix]
private static void OnHoverUpdatePostfix(float __state)
{
RestoreDelta(__state);
}
[HarmonyPatch("Update")]
[HarmonyPrefix]
private static void UpdatePrefix(out float __state)
{
__state = UseRealDelta();
}
[HarmonyPatch("Update")]
[HarmonyPostfix]
private static void UpdatePostfix(float __state)
{
RestoreDelta(__state);
}
private static float UseRealDelta()
{
if (!PauseState.IsPaused)
{
return -1f;
}
float delta = Clock.Delta;
Clock.Delta = Mathf.Min(Time.unscaledDeltaTime, Time.maximumDeltaTime);
return delta;
}
private static void RestoreDelta(float saved)
{
if (saved >= 0f)
{
Clock.Delta = saved;
}
}
}
[HarmonyPatch]
internal static class SessionPatches
{
[HarmonyPatch(typeof(SNet_SyncManager), "SessionCommand")]
[HarmonyPrefix]
private static void SessionCommand(eSessionCommandType type)
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
PauseLogic.ForceResume($"session command {type}");
}
[HarmonyPatch(typeof(SNet_SessionHub), "LeaveHub")]
[HarmonyPrefix]
private static void LeaveHub()
{
PauseLogic.ForceResume("leaving the session");
}
}
[HarmonyPatch(typeof(GS_InLevel), "Update")]
internal static class TimerPatches
{
private static void Postfix()
{
if (PauseState.IsPaused)
{
Clock.ExpeditionProgressionTime = PauseState.FrozenExpeditionTime;
}
}
}
}
namespace GTFOPause.Net
{
internal static class PauseNet
{
private struct pHello
{
public uint Protocol;
}
private struct pRequest
{
public byte WantPaused;
}
private struct pState
{
public byte Paused;
public ulong By;
public uint Seq;
}
private struct pDeny
{
public byte Reason;
public ulong Player;
}
private enum DenyReason : byte
{
Disabled = 1,
MissingMod,
NotInLevel,
Cooldown,
ProtocolMismatch
}
private const string HelloEvent = "GTFOPause.Hello";
private const string RequestEvent = "GTFOPause.Request";
private const string StateEvent = "GTFOPause.State";
private const string DenyEvent = "GTFOPause.Deny";
private const uint Protocol = 1u;
private const float ResendInterval = 2f;
private const float WatchdogTimeout = 6f;
private const float ToggleCooldown = 1f;
private static readonly HashSet<ulong> s_moddedPlayers = new HashSet<ulong>();
private static readonly HashSet<ulong> s_lobbyScratch = new HashSet<ulong>();
private static bool s_squadPaused;
private static ulong s_squadPausedBy;
private static uint s_seq;
private static float s_nextResend;
private static float s_lastToggle = float.NegativeInfinity;
private static ulong s_helloMaster;
private static bool s_hostHasMod;
private static ulong s_lastSender;
private static uint s_lastSeq;
private static uint? s_dismissedSeq;
private static float s_lastStateTime;
private static float Now => Time.realtimeSinceStartup;
private static ulong LocalLookup
{
get
{
if (!SNet.HasLocalPlayer)
{
return 0uL;
}
return SNet.LocalPlayer.Lookup;
}
}
public static void Register()
{
NetworkAPI.RegisterEvent<pHello>("GTFOPause.Hello", (Action<ulong, pHello>)OnHello);
NetworkAPI.RegisterEvent<pRequest>("GTFOPause.Request", (Action<ulong, pRequest>)OnRequest);
NetworkAPI.RegisterEvent<pState>("GTFOPause.State", (Action<ulong, pState>)OnState);
NetworkAPI.RegisterEvent<pDeny>("GTFOPause.Deny", (Action<ulong, pDeny>)OnDeny);
}
public static void Tick()
{
if (SNet.IsMaster)
{
HostTick();
}
else
{
ClientTick();
}
}
public static void OnHotkey()
{
if (SNet.IsMaster)
{
HostToggle(LocalLookup, !s_squadPaused);
return;
}
if (!s_hostHasMod)
{
Notify.Event("The host doesn't have GTFOPause");
return;
}
bool flag = PauseState.Reasons.HasFlag(PauseReason.Remote);
s_dismissedSeq = null;
NetworkAPI.InvokeEvent<pRequest>("GTFOPause.Request", new pRequest
{
WantPaused = ((!flag) ? ((byte)1) : ((byte)0))
}, SNet.Master, (SNet_ChannelType)2);
}
public static void OnLocalForceResume()
{
Notify.PausedBy = null;
if (SNet.IsMaster)
{
if (s_squadPaused)
{
SetSquadPaused(paused: false, LocalLookup, applyLocally: false);
}
}
else if (PauseState.Reasons.HasFlag(PauseReason.Remote))
{
s_dismissedSeq = s_lastSeq;
}
}
private static void HostTick()
{
PruneModdedPlayers();
if (s_squadPaused && Now >= s_nextResend)
{
Broadcast();
}
}
private static void OnHello(ulong sender, pHello hello)
{
if (SNet.IsMaster)
{
if (hello.Protocol != 1)
{
Logger.Warn($"Player {sender} runs GTFOPause protocol {hello.Protocol}, expected {1}");
SendTo(sender, "GTFOPause.Deny", new pDeny
{
Reason = 5
});
}
else
{
s_moddedPlayers.Add(sender);
Logger.Info(NameOf(sender) + " has GTFOPause");
SendTo(sender, "GTFOPause.State", CurrentState());
}
}
}
private static void OnRequest(ulong sender, pRequest request)
{
if (SNet.IsMaster)
{
HostToggle(sender, request.WantPaused != 0);
}
}
private static void HostToggle(ulong requester, bool wantPaused)
{
ulong lookup;
if (!PauseRules.InLevel)
{
Deny(requester, DenyReason.NotInLevel, 0uL);
}
else if (!PauseConfig.AllowMultiplayerPause.Value)
{
Deny(requester, DenyReason.Disabled, 0uL);
}
else if (wantPaused == s_squadPaused)
{
if (requester != LocalLookup)
{
SendTo(requester, "GTFOPause.State", CurrentState());
}
}
else if (wantPaused && TryFindUnmoddedHuman(out lookup))
{
Deny(requester, DenyReason.MissingMod, lookup);
}
else if (Now - s_lastToggle < 1f)
{
Deny(requester, DenyReason.Cooldown, 0uL);
}
else
{
s_lastToggle = Now;
SetSquadPaused(wantPaused, requester, applyLocally: true);
}
}
private static void SetSquadPaused(bool paused, ulong by, bool applyLocally)
{
s_squadPaused = paused;
s_squadPausedBy = by;
s_seq++;
if (applyLocally)
{
ApplyRemote(paused, by);
}
Broadcast();
}
private static void Broadcast()
{
NetworkAPI.InvokeEvent<pState>("GTFOPause.State", CurrentState(), (SNet_ChannelType)2);
s_nextResend = Now + 2f;
}
private static pState CurrentState()
{
return new pState
{
Paused = (s_squadPaused ? ((byte)1) : ((byte)0)),
By = s_squadPausedBy,
Seq = s_seq
};
}
private static bool TryFindUnmoddedHuman(out ulong lookup)
{
lookup = 0uL;
ulong localLookup = LocalLookup;
List<SNet_Player> lobbyPlayers = SNet.LobbyPlayers;
if (lobbyPlayers == null)
{
return false;
}
for (int i = 0; i < lobbyPlayers.Count; i++)
{
SNet_Player val = lobbyPlayers[i];
if (!((Object)(object)val == (Object)null) && !val.IsBot && val.Lookup != localLookup && !s_moddedPlayers.Contains(val.Lookup))
{
lookup = val.Lookup;
return true;
}
}
return false;
}
private static void PruneModdedPlayers()
{
if (s_moddedPlayers.Count == 0)
{
return;
}
s_lobbyScratch.Clear();
List<SNet_Player> lobbyPlayers = SNet.LobbyPlayers;
if (lobbyPlayers != null)
{
for (int i = 0; i < lobbyPlayers.Count; i++)
{
if ((Object)(object)lobbyPlayers[i] != (Object)null)
{
s_lobbyScratch.Add(lobbyPlayers[i].Lookup);
}
}
}
s_moddedPlayers.RemoveWhere((ulong lookup) => !s_lobbyScratch.Contains(lookup));
}
private static void Deny(ulong requester, DenyReason reason, ulong player = 0uL)
{
pDeny pDeny = new pDeny
{
Reason = (byte)reason,
Player = player
};
if (requester == LocalLookup)
{
OnDeny(requester, pDeny);
}
else
{
SendTo(requester, "GTFOPause.Deny", pDeny);
}
}
private static void ClientTick()
{
if (!PauseRules.InLevel || !SNet.HasMaster || (Object)(object)SNet.Master == (Object)null)
{
s_helloMaster = 0uL;
return;
}
ulong lookup = SNet.Master.Lookup;
if (lookup != s_helloMaster)
{
s_helloMaster = lookup;
s_hostHasMod = false;
NetworkAPI.InvokeEvent<pHello>("GTFOPause.Hello", new pHello
{
Protocol = 1u
}, SNet.Master, (SNet_ChannelType)2);
}
if (PauseState.Reasons.HasFlag(PauseReason.Remote) && Now - s_lastStateTime > 6f)
{
PauseLogic.ForceResume("no word from the host");
}
}
private static void OnState(ulong sender, pState state)
{
if (SNet.IsMaster || !SNet.HasMaster || (Object)(object)SNet.Master == (Object)null || sender != SNet.Master.Lookup)
{
return;
}
s_hostHasMod = true;
s_lastStateTime = Now;
if (sender != s_lastSender || state.Seq >= s_lastSeq)
{
s_lastSender = sender;
s_lastSeq = state.Seq;
bool flag = state.Paused != 0;
if ((!flag || (state.Seq != s_dismissedSeq && PauseRules.InLevel)) && flag != PauseState.Reasons.HasFlag(PauseReason.Remote))
{
ApplyRemote(flag, state.By);
}
}
}
private static void OnDeny(ulong sender, pDeny deny)
{
switch ((DenyReason)deny.Reason)
{
case DenyReason.Disabled:
Notify.Event("Multiplayer pause is disabled by the host");
break;
case DenyReason.MissingMod:
Notify.Event(NameOf(deny.Player) + " doesn't have GTFOPause");
break;
case DenyReason.ProtocolMismatch:
Notify.Event("Your GTFOPause version doesn't match the host's");
break;
case DenyReason.NotInLevel:
case DenyReason.Cooldown:
break;
}
}
private static void ApplyRemote(bool paused, ulong by)
{
string text = NameOf(by);
if (paused)
{
Notify.PausedBy = text;
PauseState.Add(PauseReason.Remote);
Notify.Event(text + " paused the game");
}
else
{
Notify.PausedBy = null;
PauseState.Remove(PauseReason.Remote);
Notify.Event(text + " resumed the game");
}
}
private static void SendTo<T>(ulong lookup, string eventName, T payload) where T : struct
{
SNet_Player val = default(SNet_Player);
if (SNet.TryGetPlayer(lookup, ref val) && (Object)(object)val != (Object)null)
{
NetworkAPI.InvokeEvent<T>(eventName, payload, val, (SNet_ChannelType)2);
}
}
private static string NameOf(ulong lookup)
{
SNet_Player val = default(SNet_Player);
if (!SNet.TryGetPlayer(lookup, ref val) || !((Object)(object)val != (Object)null))
{
return "Someone";
}
return val.NickName;
}
}
}