using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Agents; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using Enemies; using GameData; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using Player; using SNetwork; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SleeperAlert")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SleeperAlert")] [assembly: AssemblyTitle("SleeperAlert")] [assembly: AssemblyVersion("1.0.0.0")] namespace SleeperAlert; [HarmonyPatch(typeof(PlayerChatManager), "PostChatMessageLocaly")] internal static class ChatMessagePatch { private static void Postfix(SNet_Player fromPlayer, string message, SNet_Player toPlayer) { try { WakeReporter.OnChatMessageDisplayed(message); } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 处理聊天消息时出错: " + ex)); } } } } internal struct DamageSnapshot { public float HealthBefore; } internal static class FriendlyFireUtil { public static float SafeHealth(Dam_PlayerDamageBase damageBase) { try { return ((Dam_SyncedDamageBase)damageBase).Health; } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 读取血量失败: " + ex.Message)); } return 0f; } } } [HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveBulletDamage")] internal static class BulletFriendlyFirePatch { private static void Prefix(Dam_PlayerDamageBase __instance, ref DamageSnapshot __state) { __state = new DamageSnapshot { HealthBefore = FriendlyFireUtil.SafeHealth(__instance) }; } private static void Postfix(Dam_PlayerDamageBase __instance, pBulletDamageData data, DamageSnapshot __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) FriendlyFireReporter.OnPlayerDamaged(__instance, data.source, __state); } } [HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveMeleeDamage")] internal static class MeleeFriendlyFirePatch { private static void Prefix(Dam_PlayerDamageBase __instance, ref DamageSnapshot __state) { __state = new DamageSnapshot { HealthBefore = FriendlyFireUtil.SafeHealth(__instance) }; } private static void Postfix(Dam_PlayerDamageBase __instance, pFullDamageData data, DamageSnapshot __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) FriendlyFireReporter.OnPlayerDamaged(__instance, data.source, __state); } } [HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveFireDamage")] internal static class FireFriendlyFirePatch { private static void Prefix(Dam_PlayerDamageBase __instance, ref DamageSnapshot __state) { __state = new DamageSnapshot { HealthBefore = FriendlyFireUtil.SafeHealth(__instance) }; } private static void Postfix(Dam_PlayerDamageBase __instance, pSmallDamageData data, DamageSnapshot __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) FriendlyFireReporter.OnPlayerDamaged(__instance, data.source, __state); } } internal static class FriendlyFireReporter { private sealed class FfBatch { public string AttackerName; public string VictimName; public float Damage; public float VictimHealthMax; public float LastEventTime; } private const float MinDamage = 0.01f; private static readonly Dictionary Batches = new Dictionary(); public static void OnPlayerDamaged(Dam_PlayerDamageBase victimDamage, pAgent source, DamageSnapshot snapshot) { //IL_002e: 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) if (!SleeperAlertPlugin.FriendlyFireEnabled.Value) { return; } try { float time = Time.time; Agent val = default(Agent); if (!((pAgent)(ref source)).TryGet(ref val) || (Object)(object)val == (Object)null || (int)val.Type != 0) { return; } PlayerAgent val2 = ((Il2CppObjectBase)val).TryCast(); if ((Object)(object)val2 == (Object)null) { return; } Agent baseAgent = ((Dam_SyncedDamageBase)victimDamage).GetBaseAgent(); if ((Object)(object)baseAgent == (Object)null || (int)baseAgent.Type != 0) { return; } PlayerAgent val3 = ((Il2CppObjectBase)baseAgent).TryCast(); if ((Object)(object)val3 == (Object)null) { return; } ulong stablePlayerKey = WakeReporter.GetStablePlayerKey(val2); ulong stablePlayerKey2 = WakeReporter.GetStablePlayerKey(val3); if (stablePlayerKey == stablePlayerKey2) { return; } float num = Math.Max(0f, snapshot.HealthBefore - ((Dam_SyncedDamageBase)victimDamage).Health); if (!(num < 0.01f)) { string key = stablePlayerKey + "|" + stablePlayerKey2; if (!Batches.TryGetValue(key, out var value)) { value = new FfBatch(); Batches[key] = value; } value.AttackerName = WakeReporter.ResolvePlayerName(val2); value.VictimName = WakeReporter.ResolvePlayerName(val3); value.Damage += num; value.VictimHealthMax = ((Dam_SyncedDamageBase)victimDamage).HealthMax; value.LastEventTime = time; FlushExpiredBatches(time); } } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 友伤检测出错: " + ex.Message)); } } } public static void Tick() { if (SleeperAlertPlugin.FriendlyFireEnabled.Value) { FlushExpiredBatches(Time.time); } } private static void FlushExpiredBatches(float now) { List> list = null; foreach (KeyValuePair batch in Batches) { if (now - batch.Value.LastEventTime >= SleeperAlertPlugin.BatchWindow.Value) { if (list == null) { list = new List>(); } list.Add(batch); } } if (list == null) { return; } foreach (KeyValuePair item in list) { Batches.Remove(item.Key); FlushBatch(item.Value); } } private static void FlushBatch(FfBatch batch) { try { float num = ((batch.VictimHealthMax > 0f) ? (batch.Damage / batch.VictimHealthMax * 100f) : batch.Damage); string text = SleeperAlertPlugin.FriendlyFireFormat.Value.Replace("{attacker}", batch.AttackerName).Replace("{victim}", batch.VictimName).Replace("{percent}", num.ToString("0.#")) .Replace("{damage}", batch.Damage.ToString("0.#")); if (!string.IsNullOrWhiteSpace(text)) { WakeReporter.Publish(text); } } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 友伤播报失败: " + ex.Message)); } } } } [BepInPlugin("com.gtfo.sleeperalert", "SleeperAlert", "1.4.2")] [BepInProcess("GTFO.exe")] public class SleeperAlertPlugin : BasePlugin { internal static ConfigFile ModConfig; internal static ConfigEntry Enabled; internal static ConfigEntry ShowBanner; internal static ConfigEntry ShowChat; internal static ConfigEntry BannerDuration; internal static ConfigEntry SkipPropagatedWakes; internal static ConfigEntry NearestFallbackDistance; internal static ConfigEntry BatchWindow; internal static ConfigEntry MessageFormat; internal static ConfigEntry FriendlyFireEnabled; internal static ConfigEntry FriendlyFireFormat; private Harmony _harmony; internal static SleeperAlertPlugin Instance { get; private set; } internal static ManualLogSource PluginLog { get { if (Instance == null) { return null; } return ((BasePlugin)Instance).Log; } } public override void Load() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Expected O, but got Unknown Instance = this; ModConfig = new ConfigFile(Path.Combine(Paths.ConfigPath, "SleeperAlert.cfg"), true); Enabled = ModConfig.Bind("General", "Enabled", true, "是否启用本插件(总开关,修改后重启游戏生效)"); ShowBanner = ModConfig.Bind("Display", "ShowBanner", true, "在屏幕中央显示黄色横幅播报"); ShowChat = ModConfig.Bind("Display", "ShowChat", true, "在聊天框发一条文字播报"); BannerDuration = ModConfig.Bind("Display", "BannerDuration", 5f, "横幅显示时长(秒)"); SkipPropagatedWakes = ModConfig.Bind("Behavior", "SkipPropagatedWakes", false, "是否跳过连锁惊醒(如 Scout 尖叫引发的后续惊醒)。默认 false=播报连锁惊醒,和聚合计数配合不会刷屏"); NearestFallbackDistance = ModConfig.Bind("Behavior", "NearestFallbackDistance", 15f, "无法确定元凶时,归因给距离怪物该范围内的最近玩家(米)"); BatchWindow = ModConfig.Bind("Behavior", "BatchWindow", 2.5f, "聚合窗口(秒):同一玩家在该时间内惊醒的多只怪会合并成一条播报,同名怪物显示只数"); MessageFormat = ModConfig.Bind("Display", "MessageFormat", "⚠ {player} 惊醒了{enemy}!", "播报文本模板,{player}=玩家名,{enemy}=怪物名(多只时会自动变成\"2只前锋、1只射手\"这种形式)"); FriendlyFireEnabled = ModConfig.Bind("General", "FriendlyFireEnabled", true, "友伤播报开关(玩家对玩家造成伤害时播报)"); FriendlyFireFormat = ModConfig.Bind("Display", "FriendlyFireFormat", "⚠ {attacker} 对 {victim} 造成了 {percent}% 友伤!", "友伤播报模板,{attacker}=攻击者,{victim}=受害者,{percent}=占受害者血量的百分比,{damage}=伤害点数"); _harmony = new Harmony("com.gtfo.sleeperalert"); _harmony.PatchAll(typeof(WakeUpPatch)); _harmony.PatchAll(typeof(ChatMessagePatch)); _harmony.PatchAll(typeof(UpdateTickPatch)); _harmony.PatchAll(typeof(BulletFriendlyFirePatch)); _harmony.PatchAll(typeof(MeleeFriendlyFirePatch)); _harmony.PatchAll(typeof(FireFriendlyFirePatch)); _harmony.PatchAll(typeof(ScoutScreamPatch)); ((BasePlugin)this).Log.LogInfo((object)"SleeperAlert v1.4.2 已加载。"); } } internal static class MyPluginInfo { public const string PLUGIN_GUID = "com.gtfo.sleeperalert"; public const string PLUGIN_NAME = "SleeperAlert"; public const string PLUGIN_VERSION = "1.4.2"; } [HarmonyPatch(typeof(ES_ScoutScream), "DoStartScream")] internal static class ScoutScreamPatch { private static void Postfix(ES_ScoutScream __instance) { try { WakeReporter.OnScoutScream(__instance); } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 处理哨兵尖叫出错: " + ex)); } } } } [HarmonyPatch(typeof(PlayerChatManager), "Update")] internal static class UpdateTickPatch { private static void Postfix() { try { WakeReporter.Tick(); FriendlyFireReporter.Tick(); } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 批次冲刷检查出错: " + ex)); } } } } internal static class WakeReporter { private sealed class PendingBatch { public ulong PlayerKey; public string PlayerName; public readonly List EnemyNames = new List(); public float LastEventTime; } private const string Marker = "\u200b"; private static readonly Dictionary ReportedAt = new Dictionary(); private static float _lastFlushTime = -100f; private static readonly Dictionary Batches = new Dictionary(); private static readonly (string Prefix, string Name)[] EnemyNameTable = new(string, string)[34] { ("Striker_Big_Bullrush", "冲撞大前锋"), ("Striker_Big_Shadow", "暗影大前锋"), ("Striker_Big_nightmare", "梦魇大前锋"), ("Striker_Big", "大前锋"), ("Striker_Boss", "前锋首领"), ("Striker_Child_Nightmare", "梦魇幼体前锋"), ("Striker_Child", "幼体前锋"), ("Striker_Bullrush", "冲撞前锋"), ("Striker_Wave_Fast", "疾速前锋"), ("Striker_Berserk", "狂暴前锋"), ("Striker", "前锋"), ("Shooter_Big_RapidFire", "速射巨型射手"), ("Shooter_Big_Infection", "感染巨型射手"), ("Shooter_Big", "巨型射手"), ("Shooter", "射手"), ("Scout_Bullrush", "冲撞侦察兵"), ("Scout_zoomer", "疾行侦察兵"), ("Scout_nightmare", "梦魇侦察兵"), ("Scout_Shadow", "暗影侦察兵"), ("Scout", "侦察兵"), ("Birther_Boss", "母体首领"), ("Birther", "母体"), ("MegaMother", "巨母体"), ("Cocoon", "虫茧"), ("Shadow", "暗影"), ("Tank_Boss", "坦克首领"), ("Tank", "坦克"), ("Flyer_Big", "巨型飞虫"), ("Flyer", "飞虫"), ("SquidBoss_Big_Complex", "巨型触手怪"), ("SquidBoss_Big", "巨型触手怪"), ("SquidBoss_VS", "触手怪"), ("Squidward", "触手怪"), ("Pouncer", "扑袭者") }; public static void OnSleeperWakeUp(ES_HibernateWakeUp state, bool isPropagatedWakeup) { if (!SleeperAlertPlugin.Enabled.Value || (isPropagatedWakeup && SleeperAlertPlugin.SkipPropagatedWakes.Value)) { return; } float time = Time.time; EnemyAI ai = ((ES_Base)state).m_ai; if (!((Object)(object)ai == (Object)null)) { Agent agent = ((AgentAI)ai).Agent; if (!((Object)(object)agent == (Object)null)) { HandleWake(ai, agent, time); } } } public static void OnScoutScream(ES_ScoutScream state) { if (!SleeperAlertPlugin.Enabled.Value) { return; } float time = Time.time; EnemyAI ai = ((ES_Base)state).m_ai; if (!((Object)(object)ai == (Object)null)) { Agent agent = ((AgentAI)ai).Agent; if (!((Object)(object)agent == (Object)null)) { HandleWake(ai, agent, time); } } } private static void HandleWake(EnemyAI ai, Agent agent, float now) { ushort globalID = agent.GlobalID; if (ReportedAt.TryGetValue(globalID, out var value) && now - value < 60f) { return; } ReportedAt[globalID] = now; if (ReportedAt.Count > 1024) { ReportedAt.Clear(); } PlayerAgent val = ResolveCulprit(ai, agent); if (!((Object)(object)val == (Object)null)) { string text = ResolvePlayerName(val); string text2 = ResolveEnemyName(agent); if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text2)) { AddToBatch(GetStablePlayerKey(val), text, text2, now); FlushExpiredBatches(now); } } } public static void Tick() { if (SleeperAlertPlugin.Enabled.Value) { FlushExpiredBatches(Time.time); } } internal static ulong GetStablePlayerKey(PlayerAgent player) { try { if ((Object)(object)player.Owner != (Object)null && player.Owner.Lookup != 0L) { return player.Owner.Lookup; } } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 获取玩家标识失败: " + ex.Message)); } } return ((Agent)player).GlobalID; } private static void AddToBatch(ulong key, string playerName, string enemyName, float now) { if (!Batches.TryGetValue(key, out var value)) { value = new PendingBatch { PlayerKey = key, PlayerName = playerName }; Batches[key] = value; } value.EnemyNames.Add(enemyName); value.PlayerName = playerName; value.LastEventTime = now; } private static void FlushExpiredBatches(float now) { List list = null; foreach (KeyValuePair batch in Batches) { if (now - batch.Value.LastEventTime >= SleeperAlertPlugin.BatchWindow.Value) { if (list == null) { list = new List(); } list.Add(batch.Value); } } if (list == null) { return; } foreach (PendingBatch item in list) { Batches.Remove(item.PlayerKey); FlushBatch(item); } } private static void FlushBatch(PendingBatch batch) { string text = BuildSummary(batch.EnemyNames); if (!string.IsNullOrWhiteSpace(text)) { string text2 = SleeperAlertPlugin.MessageFormat.Value.Replace("{player}", batch.PlayerName).Replace("{enemy}", text); if (!string.IsNullOrWhiteSpace(text2)) { Publish(text2); } } } internal static void Publish(string message) { _lastFlushTime = Time.time; ShowBanner(message); SendChatBroadcast(message); ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogDebug((object)("[SleeperAlert] " + message)); } } private static string BuildSummary(List enemyNames) { if (enemyNames.Count == 0) { return null; } List list = new List(); Dictionary dictionary = new Dictionary(); foreach (string enemyName in enemyNames) { if (!dictionary.ContainsKey(enemyName)) { dictionary[enemyName] = 0; list.Add(enemyName); } dictionary[enemyName]++; } List list2 = new List(); foreach (string item in list) { int num = dictionary[item]; list2.Add((num > 1) ? (num + "只" + item) : item); } return string.Join("、", list2); } private static PlayerAgent ResolveCulprit(EnemyAI ai, Agent agent) { //IL_0016: 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) try { Agent agent2 = ((AgentAI)ai).Target.m_agent; if ((Object)(object)agent2 != (Object)null && (int)agent2.Type == 0) { PlayerAgent val = ((Il2CppObjectBase)agent2).TryCast(); if ((Object)(object)val != (Object)null) { return val; } } } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 读取 AI 目标失败,改用最近玩家: " + ex.Message)); } } PlayerAgent result = default(PlayerAgent); if (PlayerManager.TryGetCloseEnoughPlayerAgent(agent.Position, SleeperAlertPlugin.NearestFallbackDistance.Value, ref result)) { return result; } return null; } private static string ResolveEnemyName(Agent agent) { try { EnemyAgent val = ((Il2CppObjectBase)agent).TryCast(); if ((Object)(object)val == (Object)null) { return "沉睡者"; } EnemyDataBlock enemyData = val.EnemyData; string text = ((GameDataBlockBase)(object)enemyData)?.name; if (!string.IsNullOrWhiteSpace(text)) { (string, string)[] enemyNameTable = EnemyNameTable; for (int i = 0; i < enemyNameTable.Length; i++) { var (value, result) = enemyNameTable[i]; if (text.StartsWith(value, StringComparison.OrdinalIgnoreCase)) { return result; } } ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogDebug((object)("[SleeperAlert] 未收录的敌人数据块名: " + text + " (pid=" + ((enemyData != null) ? ((GameDataBlockBase)(object)enemyData).persistentID.ToString() : "?") + "),请反馈给作者")); } return text; } } catch (Exception ex) { ManualLogSource pluginLog2 = SleeperAlertPlugin.PluginLog; if (pluginLog2 != null) { pluginLog2.LogWarning((object)("[SleeperAlert] 获取怪物名失败: " + ex.Message)); } } return "沉睡者"; } internal static string ResolvePlayerName(PlayerAgent player) { try { if ((Object)(object)player.Owner != (Object)null && !string.IsNullOrWhiteSpace(player.Owner.NickName)) { return player.Owner.NickName; } if ((Object)(object)player.Owner != (Object)null && !string.IsNullOrWhiteSpace(player.Owner.GetName())) { return player.Owner.GetName(); } if (!string.IsNullOrWhiteSpace(player.PlayerName)) { return player.PlayerName; } } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 获取玩家名失败: " + ex.Message)); } } return "Player_" + ((Agent)player).GlobalID; } private static void ShowBanner(string message) { if (!SleeperAlertPlugin.ShowBanner.Value) { return; } try { InteractionGuiLayer interactionLayer = GuiManager.InteractionLayer; if (interactionLayer != null) { interactionLayer.SetTimedMessage(message, SleeperAlertPlugin.BannerDuration.Value, (ePUIMessageStyle)3, 10); } } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 横幅显示失败: " + ex.Message)); } } } private static void SendChatBroadcast(string message) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: 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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown if (!SleeperAlertPlugin.ShowChat.Value) { return; } try { if (!SNet.IsMaster) { return; } PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); if ((Object)(object)localPlayerAgent == (Object)null) { return; } PlayerChatManager current = PlayerChatManager.Current; if ((Object)(object)current == (Object)null) { return; } string text = "\u200b" + message; int num = 50; try { int cHAT_MESSAGE_MAX_LENGTH = PlayerChatManager.CHAT_MESSAGE_MAX_LENGTH; if (cHAT_MESSAGE_MAX_LENGTH > 0 && cHAT_MESSAGE_MAX_LENGTH < 512) { num = cHAT_MESSAGE_MAX_LENGTH; } } catch (Exception) { } if (text.Length > num) { text = text.Substring(0, num); } pChatMessage val = new pChatMessage(); val.fromPlayer = new pPlayer { lookup = localPlayerAgent.Owner.Lookup, IsBot = localPlayerAgent.Owner.IsBot }; val.toPlayer = default(pPlayer); val.message = new pString50 { data = text }; try { current.m_sendChatMessage.Ask(val); return; } catch (Exception ex2) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 联网聊天发送失败,退化为本地显示: " + ex2.Message)); } } PlayerChatManager.PostChatMessageLocaly(localPlayerAgent.Owner, text, (SNet_Player)null); } catch (Exception ex3) { ManualLogSource pluginLog2 = SleeperAlertPlugin.PluginLog; if (pluginLog2 != null) { pluginLog2.LogWarning((object)("[SleeperAlert] 聊天消息发送失败: " + ex3.Message)); } } } public static void OnChatMessageDisplayed(string message) { try { if (!string.IsNullOrEmpty(message) && SleeperAlertPlugin.ShowBanner.Value && message.StartsWith("\u200b", StringComparison.Ordinal) && !(Time.time - _lastFlushTime < 3f)) { ShowBanner(message.Substring("\u200b".Length)); } } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 处理联网横幅失败: " + ex.Message)); } } } } [HarmonyPatch(typeof(ES_HibernateWakeUp), "DoWakeup")] internal static class WakeUpPatch { private static void Postfix(ES_HibernateWakeUp __instance, bool turn, int index, float delay, bool isPropagatedWakeup) { try { WakeReporter.OnSleeperWakeUp(__instance, isPropagatedWakeup); } catch (Exception ex) { ManualLogSource pluginLog = SleeperAlertPlugin.PluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[SleeperAlert] 处理惊醒事件时出错: " + ex)); } } } }