using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("PauseMyServer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+1ea8ad8df758daf68ff6b614a195c2aad5e4bbec")] [assembly: AssemblyProduct("PauseMyServer")] [assembly: AssemblyTitle("PauseMyServer")] [assembly: AssemblyVersion("1.0.0.0")] namespace PauseMyServer; [HarmonyPatch(typeof(Game), "Pause")] internal static class Game_Pause_Patch { [HarmonyPostfix] private static void Postfix() { PauseSync.WantPause = true; } } [HarmonyPatch(typeof(Game), "Unpause")] internal static class Game_Unpause_Patch { [HarmonyPostfix] private static void Postfix() { PauseSync.WantPause = false; } } [HarmonyPatch(typeof(Game), "IsPaused")] internal static class Game_IsPaused_Patch { [HarmonyPostfix] private static void Postfix(ref bool __result) { if (!__result && PauseSync.ClientPaused && PauseSync.WantPause) { __result = true; } } } [HarmonyPatch(typeof(ZNet), "UpdateNetTime")] internal static class ZNet_UpdateNetTime_Patch { [HarmonyPrefix] private static bool Prefix() { return !PauseSync.ServerPaused; } } [HarmonyPatch(typeof(RandEventSystem), "FixedUpdate")] internal static class RandEventSystem_FixedUpdate_Patch { [HarmonyPrefix] private static bool Prefix() { return !PauseSync.ServerPaused; } } [HarmonyPatch(typeof(EnvMan), "UpdateTimeSkip")] internal static class EnvMan_UpdateTimeSkip_Patch { [HarmonyPrefix] private static bool Prefix() { return !PauseSync.ServerPaused; } } [HarmonyPatch(typeof(Game), "Start")] internal static class Game_Start_Patch { [HarmonyPostfix] private static void Postfix() { PauseSync.Reset(); PauseSync.Register(); } } [HarmonyPatch(typeof(ZNet), "Shutdown")] internal static class ZNet_Shutdown_Patch { [HarmonyPrefix] private static void Prefix() { PauseSync.Reset(); } } [BepInPlugin("DeathMonger.PauseMyServer", "Pause My Server", "1.0.0")] public class PauseMyServerMod : BaseUnityPlugin { public const string ModGuid = "DeathMonger.PauseMyServer"; public const string ModName = "Pause My Server"; public const string ModVersion = "1.0.0"; internal static ConfigEntry ModEnabled; internal static ConfigEntry ShowMessages; private readonly Harmony _harmony = new Harmony("DeathMonger.PauseMyServer"); internal static ManualLogSource Log { get; private set; } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; ModEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "Mod Enabled", true, "Master toggle for the entire mod. Set to false to disable every patch without removing the DLL. Requires a game restart to take effect."); ShowMessages = ((BaseUnityPlugin)this).Config.Bind("General", "Show Messages", true, "Show a small top-left HUD message when the world is paused, and when it resumes because another player came online while your menu was still open. Client-side only."); if (!ModEnabled.Value) { Log.LogInfo((object)"[PauseMyServer] Mod Enabled = false in config; skipping patches."); return; } _harmony.PatchAll(); Log.LogInfo((object)"[PauseMyServer] 1.0.0 loaded."); } private void Update() { if (ModEnabled.Value) { PauseSync.Update(); } } private void OnDestroy() { _harmony.UnpatchSelf(); } internal static void Message(string text) { if (ShowMessages != null && ShowMessages.Value && !((Object)(object)MessageHud.instance == (Object)null)) { MessageHud.instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true); } } } internal static class PauseSync { private const string RpcWant = "PMS_WantPause"; private const string RpcState = "PMS_PauseState"; private static readonly Dictionary _wants = new Dictionary(); private static readonly List _gone = new List(); private static string _pausedBy = ""; internal static bool WantPause; private static bool _sentWant; internal static bool ServerPaused { get; private set; } internal static bool ClientPaused { get; private set; } internal static void Register() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.Register("PMS_WantPause", (Action)RPC_WantPause); instance.Register("PMS_PauseState", (Action)RPC_PauseState); } } internal static void Reset() { ServerPaused = false; _wants.Clear(); _pausedBy = ""; ClientPaused = false; WantPause = false; _sentWant = false; } internal static void Update() { ZNet instance = ZNet.instance; if (!((Object)(object)instance == (Object)null) && ZRoutedRpc.instance != null) { if (instance.IsServer()) { UpdateServer(instance); } else { UpdateClient(); } } } private static void UpdateClient() { if (WantPause != _sentWant) { _sentWant = WantPause; ZRoutedRpc.instance.InvokeRoutedRPC("PMS_WantPause", new object[1] { WantPause }); } } private static void RPC_PauseState(long sender, bool paused) { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || instance.IsServer()) { return; } ZNetPeer serverPeer = instance.GetServerPeer(); if ((serverPeer == null || sender == serverPeer.m_uid) && paused != ClientPaused) { ClientPaused = paused; PauseMyServerMod.Log.LogInfo((object)(paused ? "[PauseMyServer] World paused." : "[PauseMyServer] World resumed.")); if (paused) { PauseMyServerMod.Message("World paused"); } else if (WantPause) { PauseMyServerMod.Message("World resumed: another player is online"); } } } private static void RPC_WantPause(long sender, bool want) { ZNet instance = ZNet.instance; if (!((Object)(object)instance == (Object)null) && instance.IsServer() && instance.GetPeer(sender) != null) { _wants[sender] = want; } } private static void UpdateServer(ZNet znet) { int num = 0; long key = 0L; string text = ""; List peers = znet.GetPeers(); for (int i = 0; i < peers.Count; i++) { ZNetPeer val = peers[i]; if (val.IsReady()) { num++; key = val.m_uid; text = val.m_playerName; } } if (_wants.Count > 0) { _gone.Clear(); foreach (long key2 in _wants.Keys) { if (znet.GetPeer(key2) == null) { _gone.Add(key2); } } for (int j = 0; j < _gone.Count; j++) { _wants.Remove(_gone[j]); } } bool value = default(bool); bool flag = !((Object)(object)Player.m_localPlayer != (Object)null) && num == 1 && _wants.TryGetValue(key, out value) && value; if (flag != ServerPaused) { ServerPaused = flag; if (flag) { _pausedBy = text; PauseMyServerMod.Log.LogInfo((object)("[PauseMyServer] World paused by " + text + ".")); } else { PauseMyServerMod.Log.LogInfo((object)$"[PauseMyServer] World resumed (was paused by {_pausedBy}; {num} player(s) connected)."); _pausedBy = ""; } ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PMS_PauseState", new object[1] { flag }); } } }