using System; 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 CG; using CG.Input; using ExitGames.Client.Photon; using FMODUnity; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using UnityEngine; using VoidManager; using VoidManager.CustomGUI; using VoidManager.MPModChecks; using VoidManager.ModMessages; using VoidManager.Utilities; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("Pause")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+315f7f7c17aab02416bfb05682e09a6b9124f3fe")] [assembly: AssemblyProduct("Pause")] [assembly: AssemblyTitle("Allows the game to be paused.")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 Pause { [BepInPlugin("NihilityShift.Pause", "Pause", "1.1.0")] [BepInProcess("Void Crew.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class BepinPlugin : BaseUnityPlugin { internal static ManualLogSource Log; private void Awake() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Configs.Load(this); ((Object)new GameObject("PausedGUI", new Type[1] { typeof(PausedGUI) })).hideFlags = (HideFlags)61; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin NihilityShift.Pause is loaded!"); } } internal class Configs { internal static ConfigEntry pauseKeyConfig; internal static ConfigEntry playersCanPauseConfig; internal static void Load(BepinPlugin plugin) { pauseKeyConfig = ((BaseUnityPlugin)plugin).Config.Bind("Pause", "PauseKey", (KeyCode)278, (ConfigDescription)null); playersCanPauseConfig = ((BaseUnityPlugin)plugin).Config.Bind("Pause", "PlayersCanPause", false, (ConfigDescription)null); } } internal class GUI : ModSettingsMenu { public override string Name() { return "Pause"; } public override void Draw() { GUILayout.Label("", Array.Empty()); if (PhotonNetwork.IsMasterClient || (PauseManager.CanPause && GUILayout.Button(PauseManager.IsPaused ? "Resume" : "Pause", Array.Empty()))) { PauseManager.TryTogglePause(); } else { GUILayout.Button((PauseManager.IsPaused ? "Resume" : "Pause") + " not permitted by host", Array.Empty()); } GUILayout.Label("", Array.Empty()); if (GUITools.DrawCheckbox("Allow other players to pause/resume", ref Configs.playersCanPauseConfig)) { PauseManager.SendCanPause(); } GUITools.DrawChangeKeybindButton("Pause keybind", ref Configs.pauseKeyConfig); } } [HarmonyPatch("UnityEngine.UIElements.Panel, UnityEngine.UIElementsModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "DefaultTimeSinceStartupMs")] internal class HudTimersPatch { private static bool Prefix(ref long __result) { __result = (long)(Time.time * 1000f); return false; } } public class MyPluginInfo { public const string PLUGIN_GUID = "NihilityShift.Pause"; public const string PLUGIN_NAME = "Pause"; public const string USERS_PLUGIN_NAME = "Pause"; public const string PLUGIN_VERSION = "1.1.0"; public const string PLUGIN_DESCRIPTION = "Allows the game to be paused."; public const string PLUGIN_ORIGINAL_AUTHOR = "18107"; public const string PLUGIN_AUTHORS = "Dragon, 18107"; public const string PLUGIN_THUNDERSTORE_ID = "NihilityShift/Pause"; } internal class PausedGUI : MonoBehaviour, IShowCursorSource, IInputActionMapRequest { private bool guiActive = false; private Rect WindowPos; private PausedGUI() { } private void Update() { //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 (PauseManager.IsPaused != guiActive) { InputActionMapRequests.RemoveRequest((IInputActionMapRequest)(object)this); guiActive = PauseManager.IsPaused; if (guiActive) { WindowPos = new Rect((float)Screen.width / 4f, (float)Screen.height / 4f, (float)Screen.width / 2f, (float)Screen.height / 2f); } CursorUtility.ShowCursor((IShowCursorSource)(object)this, guiActive); } } private void OnGUI() { //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_0035: Expected O, but got Unknown //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (guiActive) { InputActionMapRequests.AddOrChangeRequest((IInputActionMapRequest)(object)this, (InputStateRequestType)2); GUI.Window(618107, WindowPos, new WindowFunction(WindowFunction), "Game Paused"); } } private void WindowFunction(int WindowID) { GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); if (PauseManager.pausePlayer != null) { GUILayout.Label("Game paused by " + PauseManager.pausePlayer.NickName, Array.Empty()); } else { GUILayout.Label("Game paused", Array.Empty()); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Label("", Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); if (PhotonNetwork.IsMasterClient || PauseManager.CanPause) { if (GUILayout.Button(" Resume ", Array.Empty())) { PauseManager.TryTogglePause(); } } else { GUILayout.Button((PauseManager.IsPaused ? "Resume" : "Pause") + " not permitted by host", Array.Empty()); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.FlexibleSpace(); } } internal class PauseManager : ModMessage { private enum MessageType { Pause, CanPause } private const int version = 2; private static bool _isPaused; internal static Player pausePlayer; internal static bool IsPaused { get { return _isPaused; } private set { if (value != _isPaused) { _isPaused = value; if (value) { PhotonNetwork.MinimalTimeScaleToDispatchInFixedUpdate = 0f; Time.timeScale = 0f; } else { Time.timeScale = 1f; PhotonNetwork.MinimalTimeScaleToDispatchInFixedUpdate = -1f; } RuntimeManager.PauseAllEvents(value); } } } internal static bool CanPause { get; private set; } internal static void TryTogglePause(Player pauser = null) { if (!PhotonNetwork.IsMasterClient) { BepinPlugin.Log.LogInfo((object)"TryToggle non-host triggered."); RequestPause(!IsPaused); return; } IsPaused = !IsPaused; BepinPlugin.Log.LogInfo((object)"Toggled Pause from handler"); ServerTimestampPatch.UpdateTiming(IsPaused); pausePlayer = pauser ?? PhotonNetwork.LocalPlayer; SendPause(IsPaused, pausePlayer); } internal static void Reset() { IsPaused = false; ServerTimestampPatch.PauseTotal = 0; } public override void Handle(object[] arguments, Player sender) { if (arguments.Length < 3) { return; } if ((int)arguments[0] != 2) { BepinPlugin.Log.LogInfo((object)$"Received version {(int)arguments[0]}, expected {2}"); } else if (PhotonNetwork.IsMasterClient) { BepinPlugin.Log.LogInfo((object)$"Recieved Pause Request ({(bool)arguments[2]}) message from {sender.NickName}"); if ((MessageType)arguments[1] == MessageType.Pause && Configs.playersCanPauseConfig.Value && (bool)arguments[2] != IsPaused) { pausePlayer = sender; TryTogglePause(pausePlayer); } } else { if (!sender.IsMasterClient) { return; } switch ((MessageType)arguments[1]) { case MessageType.Pause: BepinPlugin.Log.LogInfo((object)$"Recieved Pause ({(bool)arguments[2]}) message from {sender.NickName}"); IsPaused = (bool)arguments[2]; if (arguments.Length > 3) { pausePlayer = PhotonNetwork.CurrentRoom.GetPlayer((int)arguments[3], false); ServerTimestampPatch.PauseTotal = (int)arguments[4]; ServerTimestampPatch.UpdateTiming(paused: true); } break; case MessageType.CanPause: BepinPlugin.Log.LogInfo((object)("Recieved CanPause message from " + sender.NickName)); CanPause = (bool)arguments[2]; break; } } } internal static void RequestPause(bool pause) { if (!PhotonNetwork.IsMasterClient) { Send(new object[3] { 2, MessageType.Pause, pause }, PhotonNetwork.MasterClient); BepinPlugin.Log.LogInfo((object)"RequestPause sent."); } } internal static void SendPause(bool pause, Player pauser, params Player[] players) { if (PhotonNetwork.IsMasterClient) { Send(new object[5] { 2, MessageType.Pause, pause, pauser.ActorNumber, ServerTimestampPatch.GetPauseTotalForClient() }, players); BepinPlugin.Log.LogInfo((object)"SendPause sent."); } } internal static void SendCanPause(params Player[] players) { if (PhotonNetwork.IsMasterClient) { Send(new object[3] { 2, MessageType.CanPause, Configs.playersCanPauseConfig.Value }, players); } } private static void Send(object[] arguments, params Player[] players) { if (players.Length != 0) { ModMessage.Send("NihilityShift.Pause", ModMessage.GetIdentifier(typeof(PauseManager)), players, arguments, true); } else { ModMessage.Send("NihilityShift.Pause", ModMessage.GetIdentifier(typeof(PauseManager)), (ReceiverGroup)0, arguments, true); } } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal class ServerTimestampPatch { internal static int cachedServerTime; internal static int PauseTotal; private static FieldInfo StartupStopwatchFI = AccessTools.Field(typeof(PhotonNetwork), "StartupStopwatch"); internal static int GetPauseTotalForClient() { if (PauseManager.IsPaused) { return PauseTotal + UnmodifiedServerTimestamp() - cachedServerTime; } return PauseTotal; } public static int UnmodifiedServerTimestamp() { if (!PhotonNetwork.OfflineMode) { return ((PhotonPeer)PhotonNetwork.NetworkingClient.LoadBalancingPeer).ServerTimeInMilliSeconds; } if (StartupStopwatchFI.GetValue(null) != null && ((Stopwatch)StartupStopwatchFI.GetValue(null)).IsRunning) { return (int)((Stopwatch)StartupStopwatchFI.GetValue(null)).ElapsedMilliseconds; } return Environment.TickCount; } internal static void UpdateTiming(bool paused) { if (paused) { cachedServerTime = UnmodifiedServerTimestamp(); } else { PauseTotal += UnmodifiedServerTimestamp() - cachedServerTime; } } private static void Postfix(ref int __result) { if (PauseManager.IsPaused) { __result = cachedServerTime - PauseTotal; } else { __result -= PauseTotal; } } } public class VoidManagerPlugin : VoidPlugin { public override MultiplayerType MPType => (MultiplayerType)30; public override string Author => "Dragon, 18107"; public override string Description => "Allows the game to be paused."; public override string ThunderstoreID => "NihilityShift/Pause"; public VoidManagerPlugin() { Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "NihilityShift.Pause"); Events.Instance.PlayerEnteredRoom += delegate(object _, PlayerEventArgs playerEventArgs) { PauseManager.SendCanPause(playerEventArgs.player); PauseManager.SendPause(PauseManager.IsPaused, PauseManager.pausePlayer ?? PhotonNetwork.LocalPlayer, playerEventArgs.player); }; Events.Instance.MasterClientSwitched += delegate { PauseManager.SendCanPause(); PauseManager.SendPause(PauseManager.IsPaused, PauseManager.pausePlayer ?? PhotonNetwork.LocalPlayer); }; Events.Instance.LeftRoom += delegate { PauseManager.Reset(); }; Events.Instance.LateUpdate += delegate { //IL_0006: 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) if ((int)Configs.pauseKeyConfig.Value != 0 && UnityInput.Current.GetKeyDown(Configs.pauseKeyConfig.Value) && (!ServiceBase.Instance.CursorVisibilityControl.IsCursorShown || PauseManager.IsPaused)) { if (!PhotonNetwork.IsMasterClient && !PauseManager.CanPause) { Messaging.Notification("Not permitted by host", 8000L, false); } else { PauseManager.TryTogglePause(); } } }; } } }