using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JG224.ModCore.API; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [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("SleepGuard")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.5.2.0")] [assembly: AssemblyInformationalVersion("0.5.2")] [assembly: AssemblyProduct("SleepGuard")] [assembly: AssemblyTitle("SleepGuard")] [assembly: AssemblyVersion("0.5.2.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 SleepGuard { internal enum BallotDecision { Open, Approved, Rejected } internal static class ApprovalRules { internal static void ApproveUnanswered(IEnumerable members, ISet sleepers, ISet approvals, ISet rejections, bool deadlineReached, bool explicitResponses) { if (!deadlineReached || explicitResponses) { return; } foreach (long member in members) { if (!sleepers.Contains(member) && !rejections.Contains(member)) { approvals.Add(member); } } } internal static int RequiredApprovals(int electorate, int percentage) { if (electorate <= 0) { return 1; } int num = Math.Max(1, Math.Min(100, percentage)); return Math.Max(1, (electorate * num + 99) / 100); } internal static BallotDecision BeforeDeadline(int approvals, int pending, int electorate, int percentage) { int num = RequiredApprovals(electorate, percentage); if (approvals >= num) { return BallotDecision.Approved; } if (approvals + pending >= num) { return BallotDecision.Open; } return BallotDecision.Rejected; } internal static BallotDecision AtDeadline(int approvals, int rejections, int percentage) { int num = approvals + rejections; if (num <= 0) { return BallotDecision.Rejected; } if (approvals < RequiredApprovals(num, percentage)) { return BallotDecision.Rejected; } return BallotDecision.Approved; } } internal static class BallotHistory { private static readonly Queue Entries = new Queue(); private static string _path; internal static void Reset() { Entries.Clear(); _path = null; } internal static void Record(int round, string reason, int electorate, int approvals, int rejections) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } EnsureLoaded(); string text = DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture) + $" | round {round} | {reason} | electorate={electorate}, approved={approvals}, rejected={rejections}"; Entries.Enqueue(text); while (Entries.Count > 50) { Entries.Dequeue(); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Rest history: " + text)); } try { string text2 = _path + ".tmp"; File.WriteAllLines(text2, Entries); if (File.Exists(_path)) { File.Replace(text2, _path, _path + ".bak"); } else { File.Move(text2, _path); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Could not save rest history: " + ex.Message)); } } } internal static IEnumerable Read() { EnsureLoaded(); return Entries.ToArray(); } private static void EnsureLoaded() { if (_path != null || (Object)(object)ZNet.instance == (Object)null) { return; } long worldUID = ZNet.instance.GetWorldUID(); _path = Path.Combine(Paths.ConfigPath, "jg224.SleepGuard.world-" + worldUID.ToString(CultureInfo.InvariantCulture) + ".history.txt"); try { if (!File.Exists(_path)) { return; } foreach (string item in File.ReadLines(_path)) { if (item.Length <= 512) { Entries.Enqueue(item); } while (Entries.Count > 50) { Entries.Dequeue(); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not read rest history: " + ex.Message)); } } } } internal enum RestSafety { Safe, Combat, BossThreat } internal readonly struct RestSafetyObservation { internal RestSafety Safety { get; } internal string PlayerName { get; } internal RestSafetyObservation(RestSafety safety, string playerName) { Safety = safety; PlayerName = playerName; } } internal static class CombatMonitor { private static readonly FieldInfo AiField = AccessTools.Field(typeof(Character), "m_baseAI"); private static readonly Dictionary RecentThreats = new Dictionary(); private static readonly List ExpiredThreats = new List(); internal static RestSafetyObservation Observe() { float realtimeSinceStartup = Time.realtimeSinceStartup; ExpiredThreats.Clear(); foreach (KeyValuePair recentThreat in RecentThreats) { if ((Object)(object)recentThreat.Key == (Object)null || ((Character)recentThreat.Key).InBed() || realtimeSinceStartup - recentThreat.Value >= (float)Plugin.CombatQuietSeconds.Value) { ExpiredThreats.Add(recentThreat.Key); } } for (int i = 0; i < ExpiredThreats.Count; i++) { RecentThreats.Remove(ExpiredThreats[i]); } ExpiredThreats.Clear(); bool flag = false; bool flag2 = false; string text = null; string text2 = null; List allCharacters = Character.GetAllCharacters(); if (allCharacters != null) { for (int j = 0; j < allCharacters.Count; j++) { Character val = allCharacters[j]; if ((Object)(object)val == (Object)null || val.IsDead()) { continue; } object? obj = AiField?.GetValue(val); BaseAI val2 = (BaseAI)((obj is BaseAI) ? obj : null); if ((Object)(object)val2 == (Object)null || !val2.IsAlerted()) { continue; } Character targetCreature = val2.GetTargetCreature(); Player val3 = (Player)(object)((targetCreature is Player) ? targetCreature : null); if ((Object)(object)val3 == (Object)null || ((Character)val3).IsDead() || ((Character)val3).InBed() || !BaseAI.IsEnemy(val, (Character)(object)val3)) { continue; } flag = true; RecentThreats[val3] = realtimeSinceStartup; string playerName = val3.GetPlayerName(); if (string.IsNullOrWhiteSpace(text)) { text = playerName; } if (val.IsBoss()) { flag2 = true; if (string.IsNullOrWhiteSpace(text2)) { text2 = playerName; } } } } if (flag2 && Plugin.HardBlockBosses.Value) { return new RestSafetyObservation(RestSafety.BossThreat, text2); } if (flag) { return new RestSafetyObservation(RestSafety.Combat, text); } Player val4 = null; float num = float.NegativeInfinity; foreach (KeyValuePair recentThreat2 in RecentThreats) { if (recentThreat2.Value > num) { val4 = recentThreat2.Key; num = recentThreat2.Value; } } if ((Object)(object)val4 != (Object)null) { return new RestSafetyObservation(RestSafety.Combat, val4.GetPlayerName()); } return new RestSafetyObservation(RestSafety.Safe, null); } internal static void Reset() { RecentThreats.Clear(); ExpiredThreats.Clear(); } } internal static class ConfigFileMigration { internal const string CurrentFileName = "jg224.SleepGuard.cfg"; internal const string LegacyFileName = "garst.SleepGuard.cfg"; internal static ConfigFile Open(BaseUnityPlugin plugin, string configDirectory, ManualLogSource log) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown if (MoveLegacy(configDirectory) && log != null) { log.LogInfo((object)"Renamed legacy config garst.SleepGuard.cfg to jg224.SleepGuard.cfg."); } return PluginConfigFiles.Attach(plugin, new ConfigFile(Path.Combine(configDirectory, "jg224.SleepGuard.cfg"), true, plugin.Info.Metadata)); } internal static bool MoveLegacy(string configDirectory) { Directory.CreateDirectory(configDirectory); if (ExactPath(configDirectory, "jg224.SleepGuard.cfg") != null) { return false; } string text = ExactPath(configDirectory, "garst.SleepGuard.cfg"); if (text == null) { return false; } File.Move(text, Path.Combine(configDirectory, "jg224.SleepGuard.cfg")); return true; } private static string ExactPath(string directory, string fileName) { return Directory.EnumerateFiles(directory, "*.cfg", SearchOption.TopDirectoryOnly).FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal)); } } internal static class VotePolicy { internal static bool TrustedServer(bool isServer, long sender, long serverUid, long localUid) { if (!isServer) { if (serverUid != 0L) { return sender == serverUid; } return false; } if (sender != 0L) { return sender == localUid; } return true; } internal static bool TryAnswer(string payload, out int round, out bool approved) { round = 0; approved = false; if (string.IsNullOrEmpty(payload) || payload.Length > 24) { return false; } int num = payload.IndexOf(','); if (num <= 0 || num != payload.Length - 2 || (payload[num + 1] != '0' && payload[num + 1] != '1') || !int.TryParse(payload.Substring(0, num), NumberStyles.None, CultureInfo.InvariantCulture, out round) || round <= 0) { return false; } approved = payload[num + 1] == '1'; return true; } internal static bool Eligible(bool ready, bool sleeping, bool includeLoading, float idleSeconds, int excludeAfterSeconds) { if (!(!ready && includeLoading)) { if (ready) { if (!sleeping && excludeAfterSeconds > 0) { return idleSeconds < (float)excludeAfterSeconds; } return true; } return false; } return true; } } internal sealed class VoteRateLimit { private readonly Dictionary _last = new Dictionary(); internal bool Accept(long playerId, float now) { if (playerId == 0L || float.IsNaN(now) || float.IsInfinity(now)) { return false; } if (_last.TryGetValue(playerId, out var value) && now - value < 0.5f) { return false; } _last[playerId] = now; return true; } internal void Remove(long playerId) { _last.Remove(playerId); } internal void Clear() { _last.Clear(); } } internal static class DebugCommands { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__RunTest; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__1_0; internal void b__1_0(ConsoleEventArgs args) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { args.Context.AddString("Run sleepguard_history on the server console or host."); return; } foreach (string item in BallotHistory.Read()) { args.Context.AddString(item); } } } private static bool _registered; internal static void Register() { //IL_0037: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0070: 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) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown if (_registered) { return; } object obj = <>O.<0>__RunTest; if (obj == null) { ConsoleEvent val = RunTest; <>O.<0>__RunTest = val; obj = (object)val; } new ConsoleCommand("sleepguard_test", "open a SleepGuard vote for single-player menu testing", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>c.<>9__1_0; if (obj2 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { args.Context.AddString("Run sleepguard_history on the server console or host."); return; } foreach (string item in BallotHistory.Read()) { args.Context.AddString(item); } }; <>c.<>9__1_0 = val2; obj2 = (object)val2; } new ConsoleCommand("sleepguard_history", "show the last 50 world ballot outcomes (server console/host)", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); _registered = true; } private static void RunTest(ConsoleEventArgs args) { if (Plugin.IsActive) { SleepBallot.TryStartSinglePlayerTest(out var message); args.Context.AddString("[SleepGuard] " + message); } } } [HarmonyPatch(typeof(Game), "EverybodyIsTryingToSleep")] internal static class RestDecisionPatch { [HarmonyPrefix] private static bool Prefix(ref bool __result) { if (!Plugin.Enabled.Value) { SleepBallot.CancelActiveSession(); return true; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return true; } return SleepBallot.ProcessSleepCheck(ref __result); } } [HarmonyPatch(typeof(Game), "Awake")] internal static class WorldResetPatch { [HarmonyPostfix] private static void Postfix() { CombatMonitor.Reset(); SleepBallot.ResetForWorld(); RestPrompt.ResetForWorld(); } } [HarmonyPatch(typeof(Game), "Start")] internal static class RestRpcRegistrationPatch { [HarmonyPostfix] private static void Postfix() { RestTransport.RegisterHandlers(); } } public enum AutomaticResponse { Ask, Approve, Reject } [BepInPlugin("garst.SleepGuard", "SleepGuard", "0.5.2")] [BepInDependency("com.jg224.modcore", "0.5.0")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "garst.SleepGuard"; public const string PluginName = "SleepGuard"; public const string PluginVersion = "0.5.2"; public const string ModCoreGuid = "com.jg224.modcore"; public const int ProtocolVersion = 2; public static readonly ModuleId ModuleId = new ModuleId("sleepguard"); internal static ManualLogSource Log; internal static ICoreServices Core; internal static ConfigEntry Enabled; internal static ConfigEntry CombatQuietSeconds; internal static ConfigEntry HardBlockBosses; internal static ConfigEntry AnnounceCombatBlocks; internal static ConfigEntry MinimumSleepers; internal static ConfigEntry ApprovalPercent; internal static ConfigEntry CountdownSeconds; internal static ConfigEntry ResponseSeconds; internal static ConfigEntry RetryDelaySeconds; internal static ConfigEntry IncludeLoadingPlayers; internal static ConfigEntry ExcludeIdleAfterSeconds; internal static ConfigEntry ClientResponse; internal static ConfigEntry ApproveVoteKey; internal static ConfigEntry RejectVoteKey; internal static ConfigEntry VerboseLogging; internal static ConfigEntry AlwaysRequireExplicitVote; private Harmony _harmony; private readonly List _registrations = new List(); private bool _shutDown; internal static bool IsActive { get; private set; } private void Awake() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00a5: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Expected O, but got Unknown //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Expected O, but got Unknown //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Expected O, but got Unknown //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Expected O, but got Unknown //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Expected O, but got Unknown //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Expected O, but got Unknown //IL_03e7: Unknown result type (might be due to invalid IL or missing references) IsActive = false; _shutDown = false; Log = ((BaseUnityPlugin)this).Logger; ConfigFile val = ConfigFileMigration.Open((BaseUnityPlugin)(object)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger); if (!ModCoreApi.IsAvailable) { throw new InvalidOperationException("ModCore did not initialize before SleepGuard."); } Core = ModCoreApi.Services; SemanticVersion val2 = default(SemanticVersion); if (!SemanticVersion.TryParse("0.5.2", ref val2)) { throw new InvalidOperationException("SleepGuard has invalid release version metadata."); } _registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "garst.SleepGuard", "SleepGuard", val2, 2, (ModuleSide)3, (ModuleRequirement)4, 0uL, 1, 1))); _registrations.Add(Core.Namespaces.Register(ModuleId, (NamespaceKind)6, "SG_", 1, Array.Empty())); _registrations.Add(RoutedRpcIngress.Register(ModuleId, new string[7] { "SG_ShowRestBallot", "SG_RestBallotState", "SG_AnswerRestBallot", "SG_CloseRestBallot", "SG_RestBallotOutcome", "SG_RestBlocked", "SG_RestCountdown" })); _registrations.Add(Core.Namespaces.Register(ModuleId, (NamespaceKind)8, "sleepguard.", 1, Array.Empty())); _registrations.Add(Core.Ui.Reserve(new UiReservation(ModuleId, (UiSurface)6, "center-screen.rest-ballot", 30, false))); Enabled = val.Bind("General", "Enabled", true, "Enable SleepGuard's combat-aware rest voting."); CombatQuietSeconds = val.Bind("General", "CombatQuietSeconds", 3, new ConfigDescription("Seconds without an alerted hostile creature targeting a living, awake player before rest may continue. Players in bed are ignored.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 60), Array.Empty())); HardBlockBosses = val.Bind("General", "HardBlockBosses", true, "Use a distinct hard-block reason while a boss is attacking an awake player. Players in bed are ignored."); AnnounceCombatBlocks = val.Bind("General", "AnnounceCombatBlocks", true, "Show connected players why an active rest request is paused."); MinimumSleepers = val.Bind("Voting", "MinimumSleepers", 1, new ConfigDescription("Connected players who must be in bed before a vote can begin.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); ApprovalPercent = val.Bind("Voting", "ApprovalPercent", 60, new ConfigDescription("Percentage required to advance the world to morning.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); CountdownSeconds = val.Bind("Voting", "CountdownSeconds", 5, new ConfigDescription("Lead-in time before voting opens. Set to 0 to open immediately.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 60), Array.Empty())); ResponseSeconds = val.Bind("Voting", "ResponseSeconds", 20, new ConfigDescription("Time allowed for responses. Unanswered eligible players approve at the deadline, except in explicit debug mode. Set to 0 to wait indefinitely.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 120), Array.Empty())); RetryDelaySeconds = val.Bind("Voting", "RetryDelaySeconds", 0, new ConfigDescription("Delay after a completed vote before another may begin.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 600), Array.Empty())); IncludeLoadingPlayers = val.Bind("Voting", "IncludeLoadingPlayers", false, "Include peers whose player character has not loaded. Server policy; defaults to excluding loading peers."); ExcludeIdleAfterSeconds = val.Bind("Voting", "ExcludeIdleAfterSeconds", 0, new ConfigDescription("Exclude ready players after this many seconds without observed movement or a vote. 0 disables idle exclusion; sleeping players always remain eligible.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 3600), Array.Empty())); ClientResponse = val.Bind("Client", "AutomaticResponse", AutomaticResponse.Ask, "Local preference for answering a rest vote."); ApproveVoteKey = val.Bind("Client", "ApproveVoteKey", new KeyboardShortcut((KeyCode)121, Array.Empty()), "Key used to approve a visible rest vote without interrupting gameplay."); RejectVoteKey = val.Bind("Client", "RejectVoteKey", new KeyboardShortcut((KeyCode)110, Array.Empty()), "Key used to decline a visible rest vote without interrupting gameplay."); VerboseLogging = val.Bind("Debug", "VerboseLogging", false, "Write state transitions and responses to the BepInEx log."); AlwaysRequireExplicitVote = val.Bind("Debug", "AlwaysRequireExplicitVote", false, "Testing only: require every player, including sleepers, to answer explicitly. Also enables the sleepguard_test command, which bypasses the bed requirement in single-player but still enforces combat safety."); DebugCommands.Register(); _harmony = new Harmony("garst.SleepGuard"); _harmony.PatchAll(); IsActive = true; Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Required combat-aware rest voting protocol ready."); Log.LogInfo((object)("SleepGuard v0.5.2 loaded. " + $"MinimumSleepers={MinimumSleepers.Value}, approval={ApprovalPercent.Value}%, " + $"countdown={CountdownSeconds.Value}s, response={ResponseSeconds.Value}s")); } private void Update() { if (IsActive) { RestTransport.ObservePopulation(); SleepBallot.TickSinglePlayerTest(); RestPrompt.Tick(); } } private void OnDestroy() { Shutdown(); } private void Shutdown() { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if (_shutDown) { return; } _shutDown = true; IsActive = false; RestPrompt.ResetForWorld(); SleepBallot.ResetForWorld(); CombatMonitor.Reset(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; for (int num = _registrations.Count - 1; num >= 0; num--) { try { _registrations[num].Dispose(); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Registration cleanup failed: " + ex.Message)); } } } _registrations.Clear(); if (Core != null) { Core.Metrics.RemoveOwner(ModuleId); } Core = null; } internal static void Trace(string message) { if (VerboseLogging != null && VerboseLogging.Value) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)message); } } } } internal static class RestPrompt { [CompilerGenerated] private static class <>O { public static UnityAction <0>__Approve; public static UnityAction <1>__Reject; } private const float PanelWidth = 660f; private const float PanelHeight = 172f; private const float TopOffset = 70f; private const float VerticalAnchor = 0.9f; private static readonly FieldInfo PopupInstance = AccessTools.Field(typeof(UnifiedPopup), "instance"); private static readonly FieldInfo PopupParent = AccessTools.Field(typeof(UnifiedPopup), "popupUIParent"); private static readonly FieldInfo HeaderText = AccessTools.Field(typeof(UnifiedPopup), "headerText"); private static readonly FieldInfo BodyText = AccessTools.Field(typeof(UnifiedPopup), "bodyText"); private static readonly FieldInfo LeftButton = AccessTools.Field(typeof(UnifiedPopup), "buttonLeft"); private static readonly FieldInfo LeftButtonText = AccessTools.Field(typeof(UnifiedPopup), "buttonLeftText"); private static RestSnapshot _snapshot; private static int _activeRound; private static int _lastCountdown = -1; private static bool _visible; private static bool _buildErrorLogged; private static GameObject _canvasObject; private static GameObject _panelObject; private static TMP_Text _statusText; private static TMP_Text _detailText; private static TMP_Text _hintText; private static TMP_Text _approveText; private static TMP_Text _rejectText; internal static void ReceivePrompt(long senderId, int round) { if (RestTransport.IsServerMessage(senderId) && round > 0 && round != _activeRound && !((Object)(object)Player.m_localPlayer == (Object)null)) { _activeRound = round; switch (Plugin.ClientResponse.Value) { case AutomaticResponse.Approve: RestTransport.SubmitResponse(round, approved: true); ShowMessage((MessageType)1, "Rest request approved automatically."); break; case AutomaticResponse.Reject: RestTransport.SubmitResponse(round, approved: false); ShowMessage((MessageType)1, "Rest request rejected automatically."); break; default: _visible = true; ShowHud(); break; } } } internal static void ReceiveSnapshot(long senderId, string encodedSnapshot) { if (RestTransport.IsServerMessage(senderId) && RestSnapshot.TryDecode(encodedSnapshot, out var snapshot)) { _snapshot = snapshot; UpdateHudText(); } } internal static void ReceiveCountdown(long senderId, int seconds) { if (RestTransport.IsServerMessage(senderId) && seconds >= 0 && seconds <= 60) { int num = ((_lastCountdown >= 0) ? 1 : 2); _lastCountdown = seconds; ShowMessage((MessageType)num, string.Format("A rest vote will open in {0} second{1}.", seconds, (seconds == 1) ? string.Empty : "s")); } } internal static void ReceiveClose(long senderId) { if (RestTransport.IsServerMessage(senderId)) { ClearLocalState(); } } internal static void ReceiveOutcome(long senderId, string message) { if (RestTransport.IsServerMessage(senderId) && message != null && message.Length <= 512) { ClearLocalState(); ShowMessage((MessageType)2, message); } } internal static void ReceiveNotice(long senderId, string message) { if (RestTransport.IsServerMessage(senderId) && message != null && message.Length <= 512) { ShowMessage((MessageType)2, message); } } internal static void Tick() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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) if (!_visible) { return; } ShowHud(); if (!CanAcceptVoteInput()) { return; } KeyboardShortcut value = Plugin.ApproveVoteKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { Approve(); return; } value = Plugin.RejectVoteKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { Reject(); } } internal static void ResetForWorld() { ClearLocalState(); DestroyHud(); } private static void ShowHud() { if (EnsureHud()) { _panelObject.SetActive(true); UpdateHudText(); } } private static bool EnsureHud() { if ((Object)(object)_panelObject != (Object)null) { return true; } object obj = PopupInstance?.GetValue(null); if (obj == null) { return false; } object? obj2 = PopupParent?.GetValue(obj); GameObject val = (GameObject)((obj2 is GameObject) ? obj2 : null); object? obj3 = HeaderText?.GetValue(obj); TMP_Text val2 = (TMP_Text)((obj3 is TMP_Text) ? obj3 : null); object? obj4 = BodyText?.GetValue(obj); TMP_Text val3 = (TMP_Text)((obj4 is TMP_Text) ? obj4 : null); object? obj5 = LeftButton?.GetValue(obj); Button val4 = (Button)((obj5 is Button) ? obj5 : null); object? obj6 = LeftButtonText?.GetValue(obj); TMP_Text val5 = (TMP_Text)((obj6 is TMP_Text) ? obj6 : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null) { return false; } try { BuildHud(FindPanelImage(val, val3, val4), val2, val3, val4, val5); _buildErrorLogged = false; return true; } catch (Exception ex) { DestroyHud(); if (!_buildErrorLogged) { _buildErrorLogged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not create the native rest voting HUD: " + ex.Message)); } } return false; } } private static void BuildHud(Image panelTemplate, TMP_Text headerTemplate, TMP_Text bodyTemplate, Button buttonTemplate, TMP_Text buttonTextTemplate) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_0102: 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_0119: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: 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_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Expected O, but got Unknown //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Expected O, but got Unknown _canvasObject = new GameObject("SleepGuard_VoteCanvas", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); Canvas component = _canvasObject.GetComponent(); component.renderMode = (RenderMode)0; component.sortingOrder = 25; CanvasScaler component2 = _canvasObject.GetComponent(); component2.uiScaleMode = (ScaleMode)1; component2.referenceResolution = new Vector2(1920f, 1080f); component2.screenMatchMode = (ScreenMatchMode)0; component2.matchWidthOrHeight = 0.5f; _panelObject = new GameObject("SleepGuard_RestVote", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); RectTransform component3 = _panelObject.GetComponent(); ((Transform)component3).SetParent(_canvasObject.transform, false); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.9f); component3.anchorMax = val; component3.anchorMin = val; component3.pivot = new Vector2(0.5f, 1f); component3.anchoredPosition = new Vector2(0f, -70f); component3.sizeDelta = new Vector2(660f, 172f); Image component4 = _panelObject.GetComponent(); CopyImageAppearance(panelTemplate, component4, new Color(0.18f, 0.12f, 0.07f, 0.96f)); ((Graphic)component4).raycastTarget = false; ((Graphic)CreateText("Title", _panelObject.transform, headerTemplate, "REST REQUEST", 25f, (FontStyles)1, new Vector2(0f, -23f), new Vector2(610f, 35f))).color = ((Graphic)headerTemplate).color; _statusText = CreateText("Status", _panelObject.transform, bodyTemplate, string.Empty, 20f, (FontStyles)1, new Vector2(0f, -57f), new Vector2(610f, 27f)); _detailText = CreateText("Details", _panelObject.transform, bodyTemplate, string.Empty, 17f, (FontStyles)0, new Vector2(0f, -82f), new Vector2(620f, 24f)); _hintText = CreateText("Hint", _panelObject.transform, bodyTemplate, string.Empty, 15f, (FontStyles)2, new Vector2(0f, -105f), new Vector2(610f, 22f)); Transform transform = _panelObject.transform; Vector2 position = new Vector2(-158f, -140f); Vector2 dimensions = new Vector2(292f, 40f); object obj = <>O.<0>__Approve; if (obj == null) { UnityAction val2 = Approve; <>O.<0>__Approve = val2; obj = (object)val2; } Button val3 = CreateButton("Approve", transform, buttonTemplate, position, dimensions, (UnityAction)obj); _approveText = CreateText("Label", ((Component)val3).transform, buttonTextTemplate, string.Empty, 18f, (FontStyles)1, Vector2.zero, new Vector2(280f, 36f)); CenterInParent(_approveText.rectTransform); Transform transform2 = _panelObject.transform; Vector2 position2 = new Vector2(158f, -140f); Vector2 dimensions2 = new Vector2(292f, 40f); object obj2 = <>O.<1>__Reject; if (obj2 == null) { UnityAction val4 = Reject; <>O.<1>__Reject = val4; obj2 = (object)val4; } Button val5 = CreateButton("Decline", transform2, buttonTemplate, position2, dimensions2, (UnityAction)obj2); _rejectText = CreateText("Label", ((Component)val5).transform, buttonTextTemplate, string.Empty, 18f, (FontStyles)1, Vector2.zero, new Vector2(280f, 36f)); CenterInParent(_rejectText.rectTransform); } private static Image FindPanelImage(GameObject popupParent, TMP_Text body, Button button) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) Transform parent = body.transform.parent; while ((Object)(object)parent != (Object)null) { Image component = ((Component)parent).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.sprite != (Object)null) { return component; } if ((Object)(object)parent == (Object)(object)popupParent.transform) { break; } parent = parent.parent; } Image component2 = ((Component)button).GetComponent(); Image result = null; float num = 0f; Image[] componentsInChildren = popupParent.GetComponentsInChildren(true); foreach (Image val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)component2) && !((Object)(object)val.sprite == (Object)null)) { Rect rect = ((Graphic)val).rectTransform.rect; float num2 = Mathf.Abs(((Rect)(ref rect)).width * ((Rect)(ref rect)).height); if (num2 > num) { result = val; num = num2; } } } return result; } private static TMP_Text CreateText(string name, Transform parent, TMP_Text template, string value, float size, FontStyles style, Vector2 position, Vector2 dimensions) { //IL_002e: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }); RectTransform component = val.GetComponent(); ((Transform)component).SetParent(parent, false); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 1f); component.anchorMax = val2; component.anchorMin = val2; component.pivot = new Vector2(0.5f, 0.5f); component.anchoredPosition = position; component.sizeDelta = dimensions; TextMeshProUGUI component2 = val.GetComponent(); ((TMP_Text)component2).text = value; ((TMP_Text)component2).font = template.font; ((TMP_Text)component2).fontSharedMaterial = template.fontSharedMaterial; ((TMP_Text)component2).fontSize = size; ((TMP_Text)component2).fontStyle = style; ((Graphic)component2).color = ((Graphic)template).color; ((TMP_Text)component2).alignment = (TextAlignmentOptions)514; ((TMP_Text)component2).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)component2).overflowMode = (TextOverflowModes)3; ((Graphic)component2).raycastTarget = false; return (TMP_Text)(object)component2; } private static void CenterInParent(RectTransform rect) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0035: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); rect.anchorMax = val; rect.anchorMin = val; rect.pivot = new Vector2(0.5f, 0.5f); rect.anchoredPosition = Vector2.zero; } private static Button CreateButton(string name, Transform parent, Button template, Vector2 position, Vector2 dimensions, UnityAction callback) { //IL_003b: 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) //IL_0061: 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) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[4] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button) }); RectTransform component = val.GetComponent(); ((Transform)component).SetParent(parent, false); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 1f); component.anchorMax = val2; component.anchorMin = val2; component.pivot = new Vector2(0.5f, 0.5f); component.anchoredPosition = position; component.sizeDelta = dimensions; Image component2 = val.GetComponent(); CopyImageAppearance(((Component)template).GetComponent(), component2, Color.white); Button component3 = val.GetComponent