Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LessSleep v1.0.0
BepInEx/plugins/LessSleep.dll
Decompiled a week agousing 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 Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("LessSleep")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+87cc3a25a482ab4adefb1d55ae2bcabf371743b7")] [assembly: AssemblyProduct("LessSleep")] [assembly: AssemblyTitle("LessSleep")] [assembly: AssemblyVersion("1.0.0.0")] [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 LessSleep { internal static class ClientPatches { [HarmonyPatch(typeof(Chat), "SendText")] internal static class ChatSendTextPatch { private static bool Prefix(string text) { return !ClientState.TryChatVote(text); } } [HarmonyPatch(typeof(Terminal), "TryRunCommand")] internal static class TerminalTryRunCommandPatch { private static bool Prefix(string text) { if (string.IsNullOrEmpty(text)) { return true; } string text2 = text.Trim(); if (text2.StartsWith("/")) { text2 = text2.Substring(1); } if (text2.IndexOf(' ') >= 0) { return true; } return !ClientState.TryChatVote(text2); } } } internal static class ClientState { private static bool voteOpen; private static float helloNext; public static void Tick() { if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return; } if (Time.time >= helloNext) { helloNext = Time.time + 30f; Rpc.ToServer("LessSleep_Hello", "1.0.0"); } if (voteOpen && LessSleepPlugin.EnableVotePrompt.Value && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus())) { if (Input.GetKeyDown((KeyCode)121)) { CastVote(accept: true); } else if (Input.GetKeyDown((KeyCode)110)) { CastVote(accept: false); } } } public static bool TryChatVote(string text) { if (!voteOpen || !LessSleepPlugin.AllowChatYesNo.Value) { return false; } switch ((text ?? string.Empty).Trim().ToLowerInvariant()) { case "yes": case "y": CastVote(accept: true); return true; case "no": case "n": CastVote(accept: false); return true; default: return false; } } public static void OnVoteStart(string requester, float timeout, int yesNeeded, int awakeCount) { voteOpen = true; } public static void OnVoteUpdate(int yesVotes, int yesNeeded) { if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, $"Sleep vote: {yesVotes}/{yesNeeded}", 0, (Sprite)null, false, false); } } public static void OnVoteResult(bool passed, string reason) { voteOpen = false; } private static void CastVote(bool accept) { Rpc.ToServer("LessSleep_Vote", accept); if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, accept ? "Sleep vote: accepted" : "Sleep vote: declined", 0, (Sprite)null, false, false); } } } internal static class Net { public const string Hello = "LessSleep_Hello"; public const string VoteStart = "LessSleep_VoteStart"; public const string VoteUpdate = "LessSleep_VoteUpdate"; public const string VoteResult = "LessSleep_VoteResult"; public const string Vote = "LessSleep_Vote"; public const string VanillaShowMessage = "ShowMessage"; } [BepInPlugin("LessCx.LessSleep", "LessSleep", "1.0.0")] public class LessSleepPlugin : BaseUnityPlugin { public const string Guid = "LessCx.LessSleep"; public const string Version = "1.0.0"; internal static ConfigEntry<int> PercentInBedToStartVote; internal static ConfigEntry<int> MinimumPlayersInBed; internal static ConfigEntry<int> VotePercentOfAwakeToPass; internal static ConfigEntry<float> VoteTimeoutSeconds; internal static ConfigEntry<float> VoteCooldownSeconds; internal static ConfigEntry<bool> EnableVotePrompt; internal static ConfigEntry<bool> AllowChatYesNo; private Harmony harmony; private static ManualLogSource log; internal static void Log(string message) { ManualLogSource obj = log; if (obj != null) { obj.LogInfo((object)message); } } private void Awake() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Expected O, but got Unknown log = ((BaseUnityPlugin)this).Logger; PercentInBedToStartVote = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "PercentInBedToStartVote", 50, new ConfigDescription("Percentage of online players that must be in bed before a sleep vote starts.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); MinimumPlayersInBed = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "MinimumPlayersInBed", 1, new ConfigDescription("Minimum number of players in bed before a sleep vote can start.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); VotePercentOfAwakeToPass = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "VotePercentOfAwakeToPass", 50, new ConfigDescription("Percentage of awake LessSleep clients that must accept for the night to be skipped.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); VoteTimeoutSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "VoteTimeoutSeconds", 30f, new ConfigDescription("How long a sleep vote stays open before it fails.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 120f), Array.Empty<object>())); VoteCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "VoteCooldownSeconds", 60f, new ConfigDescription("Wait time after a failed or cancelled vote before a new one can start.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>())); EnableVotePrompt = ((BaseUnityPlugin)this).Config.Bind<bool>("Client", "EnableVotePrompt", true, "Show sleep vote prompts and allow voting with the Y and N keys."); AllowChatYesNo = ((BaseUnityPlugin)this).Config.Bind<bool>("Client", "AllowChatYesNo", true, "While a sleep vote is open, typing yes or no in chat casts your vote."); harmony = new Harmony("LessCx.LessSleep"); harmony.PatchAll(typeof(ServerPatches)); harmony.PatchAll(typeof(ClientPatches)); Rpc.Register(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"LessSleep 1.0.0 loaded."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private void Update() { ZNet instance = ZNet.instance; if (!((Object)(object)instance == (Object)null)) { Rpc.Register(); if (instance.IsServer()) { VoteManager.Tick(); } ClientState.Tick(); } } } internal static class Rpc { private static bool registered; public static void Register() { if (!registered) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { registered = true; instance.Register<string>("LessSleep_Hello", (Action<long, string>)OnHello); instance.Register<bool>("LessSleep_Vote", (Action<long, bool>)OnVote); instance.Register<string, float, int, int>("LessSleep_VoteStart", (Method<string, float, int, int>)OnVoteStart); instance.Register<int, int>("LessSleep_VoteUpdate", (Action<long, int, int>)OnVoteUpdate); instance.Register<bool, string>("LessSleep_VoteResult", (Action<long, bool, string>)OnVoteResult); } } } public static void SendToAll(string method, params object[] args) { ZRoutedRpc.instance.InvokeRoutedRPC(0L, method, args); } public static void ToServer(string method, params object[] args) { ZRoutedRpc.instance.InvokeRoutedRPC(method, args); } public static void MessageAll(string text) { SendToAll("ShowMessage", 1, text); } private static void OnHello(long sender, string version) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { VoteManager.RegisterModdedPeer(sender, version); } } private static void OnVote(long sender, bool accept) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { VoteManager.CastVote(sender, accept); } } private static void OnVoteStart(long sender, string requester, float timeout, int yesNeeded, int awakeCount) { ClientState.OnVoteStart(requester, timeout, yesNeeded, awakeCount); } private static void OnVoteUpdate(long sender, int yesVotes, int yesNeeded) { ClientState.OnVoteUpdate(yesVotes, yesNeeded); } private static void OnVoteResult(long sender, bool passed, string reason) { ClientState.OnVoteResult(passed, reason); } } internal static class ServerPatches { [HarmonyPatch(typeof(Game), "EverybodyIsTryingToSleep")] internal static class EverybodyIsTryingToSleepPatch { private static bool Prefix(ref bool __result) { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return true; } List<ZDO> allCharacterZDOS = instance.GetAllCharacterZDOS(); if (allCharacterZDOS == null || allCharacterZDOS.Count == 0) { return true; } int num = 0; HashSet<long> hashSet = new HashSet<long>(); foreach (ZDO item in allCharacterZDOS) { if (item.GetBool(ZDOVars.s_inBed, false)) { num++; } else { hashSet.Add(((ZDOID)(ref item.m_uid)).UserID); } } int count = allCharacterZDOS.Count; if (num >= count) { VoteManager.Cancel("everyone is already in bed"); VoteManager.ConsumePass(); return true; } int num2 = Math.Max(LessSleepPlugin.MinimumPlayersInBed.Value, (int)Math.Ceiling((double)count * ((double)LessSleepPlugin.PercentInBedToStartVote.Value / 100.0))); if (num >= num2) { if (VoteManager.IsVoting) { __result = false; return false; } if (VoteManager.PassedNow) { VoteManager.ConsumePass(); __result = true; return false; } if (Time.time < VoteManager.CooldownUntil) { __result = false; return false; } VoteManager.StartVote(FindRequesterName(allCharacterZDOS), VoteManager.FilterModdedAwake(hashSet)); __result = false; return false; } if (VoteManager.IsVoting) { VoteManager.Cancel("players left their beds"); } return true; } private static string FindRequesterName(List<ZDO> zdos) { //IL_003e: 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) //IL_0045: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) List<PlayerInfo> playerList = ZNet.instance.GetPlayerList(); if (playerList == null) { return "A player"; } foreach (ZDO zdo in zdos) { if (!zdo.GetBool(ZDOVars.s_inBed, false)) { continue; } foreach (PlayerInfo item in playerList) { if (item.m_characterID == zdo.m_uid && !string.IsNullOrEmpty(item.m_name)) { return item.m_name; } } break; } return "A player"; } } } internal static class VoteManager { private static readonly HashSet<long> moddedPeers = new HashSet<long>(); private static readonly Dictionary<long, bool> votes = new Dictionary<long, bool>(); private static bool voting; private static bool passed; private static string requester = "A player"; private static int yesNeeded; private static int awakeCount; private static float deadline; private static float cooldownUntil; public static bool IsVoting => voting; public static bool PassedNow => passed; public static float CooldownUntil => cooldownUntil; public static void RegisterModdedPeer(long peer, string version) { moddedPeers.Add(peer); } public static HashSet<long> FilterModdedAwake(HashSet<long> awakePeerIds) { PruneConnected(); HashSet<long> hashSet = new HashSet<long>(); foreach (long awakePeerId in awakePeerIds) { if (moddedPeers.Contains(awakePeerId)) { hashSet.Add(awakePeerId); } } return hashSet; } private static void PruneConnected() { //IL_002a: 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_0030: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) List<PlayerInfo> playerList = ZNet.instance.GetPlayerList(); if (playerList == null) { moddedPeers.Clear(); return; } HashSet<long> hashSet = new HashSet<long>(); foreach (PlayerInfo item in playerList) { if (item.m_characterID != ZDOID.None) { ZDOID characterID = item.m_characterID; hashSet.Add(((ZDOID)(ref characterID)).UserID); } } moddedPeers.IntersectWith(hashSet); } public static void StartVote(string requesterName, HashSet<long> moddedAwake) { requester = (string.IsNullOrEmpty(requesterName) ? "A player" : requesterName); awakeCount = moddedAwake.Count; if (awakeCount == 0) { Pass("nobody to ask"); return; } votes.Clear(); voting = true; passed = false; yesNeeded = Mathf.Max(1, Mathf.CeilToInt((float)awakeCount * ((float)LessSleepPlugin.VotePercentOfAwakeToPass.Value / 100f))); deadline = Time.time + LessSleepPlugin.VoteTimeoutSeconds.Value; Rpc.SendToAll("LessSleep_VoteStart", requester, LessSleepPlugin.VoteTimeoutSeconds.Value, yesNeeded, awakeCount); Rpc.MessageAll(requester + " wants to sleep. Press Y to accept or N to decline " + $"({yesNeeded} of {awakeCount} votes needed, {LessSleepPlugin.VoteTimeoutSeconds.Value:0}s). " + "You can also type yes or no in chat."); LessSleepPlugin.Log($"[LessSleep] Vote started by {requester}: {yesNeeded}/{awakeCount} needed."); } public static void CastVote(long peer, bool accept) { if (voting && moddedPeers.Contains(peer) && (!votes.TryGetValue(peer, out var value) || value != accept)) { votes[peer] = accept; BroadcastUpdate(); CheckOutcome(); } } public static void Tick() { if (voting) { if (Time.time >= deadline) { Fail("timed out"); } else { CheckOutcome(); } } } public static void Cancel(string reason) { if (voting) { voting = false; passed = false; cooldownUntil = Time.time + LessSleepPlugin.VoteCooldownSeconds.Value; Rpc.SendToAll("LessSleep_VoteResult", false, reason); Rpc.MessageAll("Sleep vote cancelled: " + reason + "."); LessSleepPlugin.Log("[LessSleep] Vote cancelled: " + reason + "."); } } public static void ConsumePass() { passed = false; cooldownUntil = Time.time + 10f; } private static void CheckOutcome() { int num = 0; foreach (bool value in votes.Values) { if (value) { num++; } } if (num >= yesNeeded) { Pass("vote passed"); } else if (votes.Count >= awakeCount) { Fail("not enough votes"); } } private static void BroadcastUpdate() { int num = 0; foreach (bool value in votes.Values) { if (value) { num++; } } Rpc.SendToAll("LessSleep_VoteUpdate", num, yesNeeded); } private static void Pass(string reason) { voting = false; passed = true; cooldownUntil = Time.time + 10f; Rpc.SendToAll("LessSleep_VoteResult", true, reason); Rpc.MessageAll("Sleep vote passed - skipping to morning."); LessSleepPlugin.Log("[LessSleep] Vote passed."); } private static void Fail(string reason) { voting = false; passed = false; cooldownUntil = Time.time + LessSleepPlugin.VoteCooldownSeconds.Value; Rpc.SendToAll("LessSleep_VoteResult", false, reason); Rpc.MessageAll("Sleep vote failed: " + reason + "."); LessSleepPlugin.Log("[LessSleep] Vote failed: " + reason + "."); } } }