Decompiled source of ServerAnnouncements v1.0.1
plugins/ServerAnnouncements/ServerAnnouncements.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CodeTalker.Networking; using CodeTalker.Packets; using HarmonyLib; using Mirror; using ServerAnnouncements.Channels; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("atl_host")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("atl_host")] [assembly: AssemblyCopyright("Copyright \ufffd\ufffd 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("45a14b12-0a67-4261-85f1-09da9e90923b")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace ServerAnnouncements { public static class AnnouncementCommandHandler { [HarmonyPatch(typeof(ChatBehaviour), "Cmd_SendChatMessage")] private static class ChatCommandPatch { [HarmonyPrefix] private static bool Prefix(ChatBehaviour __instance, string _message) { if (string.IsNullOrEmpty(_message) || !_message.StartsWith("/ntc")) { return true; } try { ProcessCommand(__instance, _message); } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogError((object)("Command processing error: " + ex.Message)); LocalMessage(__instance, "<color=#FF6666>[Notice] Command failed. Check log.</color>"); } return false; } } private const string CMD_PREFIX = "/ntc"; public static void Initialize() { ServerAnnouncementsPlugin.Log.LogInfo((object)"Notice command handler initialized."); } private static void ProcessCommand(ChatBehaviour chat, string input) { string[] array = input.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length < 2) { ShowHelp(chat); return; } string text = array[1].ToLower(); bool isHost = AnnouncementManager.IsHost(); switch (text) { case "help": ShowHelp(chat); break; case "toast": if (RequireHost(chat, isHost)) { float num = AnnouncementConfig.ToastDuration.Value; string text2; if (array.Length <= 3 || !float.TryParse(array[2], out var result)) { text2 = ((array.Length <= 2) ? AnnouncementConfig.JoinToastMessage.Value : string.Join(" ", array.Skip(2))); } else { num = result; text2 = string.Join(" ", array.Skip(3)); } AnnouncementNetworkHandler.BroadcastToast(text2, num, skipLocalDisplay: false, forceShow: true); LocalMessage(chat, $"<color=#FFD700>[Notice]</color> Toast sent ({num}s): {text2}"); } break; case "banner": if (RequireHost(chat, isHost)) { ShowBannerToAll(); LocalMessage(chat, "<color=#FFD700>[Notice]</color> Banner shown to all players."); } break; case "version": if (RequireHost(chat, isHost)) { if (array.Length > 2 && int.TryParse(array[2], out var result3)) { AnnouncementConfig.NoticeVersion.Value = result3; LocalMessage(chat, $"<color=#FFD700>[Notice]</color> Notice version set to {result3}"); } else { LocalMessage(chat, $"<color=#FFD700>[Notice]</color> Current version: {AnnouncementConfig.NoticeVersion.Value}"); } } break; case "duration": if (RequireHost(chat, isHost)) { if (array.Length > 2 && float.TryParse(array[2], out var result2)) { result2 = Mathf.Clamp(result2, 1f, 30f); AnnouncementConfig.ToastDuration.Value = result2; LocalMessage(chat, $"<color=#FFD700>[Notice]</color> Toast duration set to {result2}s"); } else { LocalMessage(chat, $"<color=#FFD700>[Notice]</color> Current duration: {AnnouncementConfig.ToastDuration.Value}s (1-30)"); } } break; case "title": if (RequireHost(chat, isHost)) { if (array.Length > 2) { string text5 = string.Join(" ", array.Skip(2)); AnnouncementConfig.BannerTitle.Value = text5; LocalMessage(chat, "<color=#FFD700>[Notice]</color> Banner title set to: " + text5); } else { LocalMessage(chat, "<color=#FFD700>[Notice]</color> Current title: " + AnnouncementConfig.BannerTitle.Value); } } break; case "content": if (RequireHost(chat, isHost)) { if (array.Length > 2) { string value = string.Join(" ", array.Skip(2)); AnnouncementConfig.BannerContent.Value = value; LocalMessage(chat, "<color=#FFD700>[Notice]</color> Banner content set."); } else { LocalMessage(chat, "<color=#FFD700>[Notice]</color> Current content: " + AnnouncementConfig.BannerContent.Value); } } break; case "joinmsg": if (RequireHost(chat, isHost)) { if (array.Length > 2) { string text4 = string.Join(" ", array.Skip(2)); AnnouncementConfig.JoinToastMessage.Value = text4; LocalMessage(chat, "<color=#FFD700>[Notice]</color> Join message set to: " + text4); } else { LocalMessage(chat, "<color=#FFD700>[Notice]</color> Current join message: " + AnnouncementConfig.JoinToastMessage.Value); } } break; case "tab": if (RequireHost(chat, isHost)) { if (array.Length > 2) { string text3 = string.Join(" ", array.Skip(2)); AnnouncementConfig.EasySettingsTab.Value = text3; LocalMessage(chat, "<color=#FFD700>[Notice]</color> EasySettings tab set to: " + text3); LocalMessage(chat, "<color=#AAAAAA>Restart game or reload settings for changes to take effect.</color>"); } else { LocalMessage(chat, "<color=#FFD700>[Notice]</color> Current tab: " + AnnouncementConfig.EasySettingsTab.Value); LocalMessage(chat, "<color=#AAAAAA>Examples: Moderation, Mods, Server</color>"); } } break; case "reload": if (RequireHost(chat, isHost)) { AnnouncementConfig.Reload(); LocalMessage(chat, "<color=#FFD700>[Notice]</color> Config reloaded."); } break; case "status": if (RequireHost(chat, isHost)) { ShowStatus(chat); } break; case "reset": AnnouncementManager.Instance.ResetAllAcknowledgments(); LocalMessage(chat, "<color=#FFD700>[Notice]</color> Notice acknowledgment history cleared."); break; default: LocalMessage(chat, "<color=#FF6666>[Notice]</color> Unknown command: " + text + ". Use /ntc help"); break; } } private static bool RequireHost(ChatBehaviour chat, bool isHost) { if (!isHost) { LocalMessage(chat, "<color=#FF6666>[Notice]</color> This command is host-only."); return false; } return true; } private static void LocalMessage(ChatBehaviour chat, string msg) { chat.New_ChatMessage(msg); } private static void ShowHelp(ChatBehaviour chat) { LocalMessage(chat, "<color=#FFD700>=== Server Notice Commands ===</color>"); LocalMessage(chat, "/ntc help - Show this help"); LocalMessage(chat, "/ntc toast [sec] [msg] - Send toast (Host)"); LocalMessage(chat, "/ntc banner - Show banner to all (Host)"); LocalMessage(chat, "/ntc duration [n] - Set toast duration (Host)"); LocalMessage(chat, "/ntc title [text] - Set banner title (Host)"); LocalMessage(chat, "/ntc content [text] - Set banner content (Host)"); LocalMessage(chat, "/ntc joinmsg [text] - Set join message (Host)"); LocalMessage(chat, "/ntc version [n] - Set notice version (Host)"); LocalMessage(chat, "/ntc tab [name] - Set EasySettings tab (Host)"); LocalMessage(chat, "/ntc status - Show status (Host)"); LocalMessage(chat, "/ntc reload - Reload config (Host)"); LocalMessage(chat, "/ntc reset - Reset your acknowledgments"); } private static void ShowStatus(ChatBehaviour chat) { LocalMessage(chat, "<color=#FFD700>=== Notice Status ===</color>"); LocalMessage(chat, $"Enabled: {AnnouncementConfig.EnableNoticeSystem.Value}"); LocalMessage(chat, $"Version: {AnnouncementConfig.NoticeVersion.Value}"); LocalMessage(chat, "Title: " + AnnouncementConfig.BannerTitle.Value); LocalMessage(chat, $"Join Toast: {AnnouncementConfig.ShowToastOnJoin.Value}"); LocalMessage(chat, $"Toast Duration: {AnnouncementConfig.ToastDuration.Value}s"); LocalMessage(chat, "Join Message: " + AnnouncementConfig.JoinToastMessage.Value); LocalMessage(chat, $"Vanilla Compat: {AnnouncementConfig.VanillaCompatMode.Value}"); } public static void SendToastNow(string message = null) { if (!AnnouncementManager.IsHost()) { ServerAnnouncementsPlugin.Log.LogWarning((object)"SendToastNow called but not host."); return; } string message2 = message ?? AnnouncementConfig.JoinToastMessage.Value; float value = AnnouncementConfig.ToastDuration.Value; AnnouncementNetworkHandler.BroadcastToast(message2, value, skipLocalDisplay: false, forceShow: true); } public static void ShowBannerToAll() { if (!AnnouncementManager.IsHost()) { ServerAnnouncementsPlugin.Log.LogWarning((object)"ShowBannerToAll called but not host."); } else { AnnouncementNetworkHandler.BroadcastBanner(forceShow: true); } } public static void SyncSettingsIfHost() { if (AnnouncementManager.IsHost()) { } } } public static class AnnouncementConfig { public static ConfigEntry<bool> EnableNoticeSystem; public static ConfigEntry<string> BannerTitle; public static ConfigEntry<string> BannerContent; public static ConfigEntry<string> BannerExtraContent; public static ConfigEntry<int> NoticeVersion; public static ConfigEntry<bool> ShowToastOnJoin; public static ConfigEntry<string> JoinToastMessage; public static ConfigEntry<float> ToastDuration; public static ConfigEntry<bool> VanillaCompatMode; public static ConfigEntry<bool> EnableNoticeDisplay; public static ConfigEntry<bool> PlayNoticeSound; public static ConfigEntry<string> EasySettingsTab; public static ConfigEntry<KeyCode> SettingsWindowKey; public static ConfigEntry<int> BannerTitleFontSize; public static ConfigEntry<int> BannerContentFontSize; public static ConfigEntry<int> BannerExtraContentFontSize; public static ConfigEntry<int> ToastFontSize; private static ConfigFile _config; public static void Initialize(ConfigFile config) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Expected O, but got Unknown //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Expected O, but got Unknown //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Expected O, but got Unknown //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Expected O, but got Unknown //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Expected O, but got Unknown _config = config; EnableNoticeSystem = config.Bind<bool>("Host", "EnableNoticeSystem", true, "Enable the server notice system (Host only)"); BannerTitle = config.Bind<string>("Host", "BannerTitle", "Server Rules", "Title displayed on the notice banner"); BannerContent = config.Bind<string>("Host", "BannerContent", "Welcome to the server!\nPlease follow the rules.", "Content displayed on the notice banner (supports \\n for new lines)"); BannerExtraContent = config.Bind<string>("Host", "BannerExtraContent", "", "Additional content displayed below the main banner content (supports \\n for new lines)"); NoticeVersion = config.Bind<int>("Host", "NoticeVersion", 1, new ConfigDescription("Increment to show banner again to all players", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 999), Array.Empty<object>())); ShowToastOnJoin = config.Bind<bool>("Host", "ShowToastOnJoin", true, "Show a toast message when players join"); JoinToastMessage = config.Bind<string>("Host", "JoinToastMessage", "Welcome to the server!", "Toast message shown when players join"); ToastDuration = config.Bind<float>("Host", "ToastDuration", 5f, new ConfigDescription("How long toast messages display (seconds)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>())); VanillaCompatMode = config.Bind<bool>("Host", "VanillaCompatMode", true, "Also send notices as regular chat messages for players without the mod"); EnableNoticeDisplay = config.Bind<bool>("Client", "EnableNoticeDisplay", true, "Show server notices (disable to hide all notices)"); PlayNoticeSound = config.Bind<bool>("Client", "PlayNoticeSound", true, "Play sound when notice appears"); EasySettingsTab = config.Bind<string>("UI", "EasySettingsTab", "Moderation", "Which EasySettings tab to show this mod in (e.g., 'Moderation', 'Mods', or custom name). Empty = default Mods tab."); SettingsWindowKey = config.Bind<KeyCode>("UI", "SettingsWindowKey", (KeyCode)293, "Key to open/close the settings window. Default: F12"); BannerTitleFontSize = config.Bind<int>("UI", "BannerTitleFontSize", 24, new ConfigDescription("Font size for banner title", (AcceptableValueBase)(object)new AcceptableValueRange<int>(12, 48), Array.Empty<object>())); BannerContentFontSize = config.Bind<int>("UI", "BannerContentFontSize", 16, new ConfigDescription("Font size for banner content", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 36), Array.Empty<object>())); BannerExtraContentFontSize = config.Bind<int>("UI", "BannerExtraContentFontSize", 14, new ConfigDescription("Font size for banner extra content", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 36), Array.Empty<object>())); ToastFontSize = config.Bind<int>("UI", "ToastFontSize", 20, new ConfigDescription("Font size for toast messages", (AcceptableValueBase)(object)new AcceptableValueRange<int>(12, 36), Array.Empty<object>())); } public static void Reload() { ConfigFile config = _config; if (config != null) { config.Reload(); } } } public struct DismissedBannerInfo { public int Version; public DateTime DismissedAt; } public class AnnouncementManager { private static AnnouncementManager _instance; private Dictionary<string, DismissedBannerInfo> _dismissedBanners = new Dictionary<string, DismissedBannerInfo>(); private string _currentServerId; private const float DISMISS_DURATION_HOURS = 24f; private bool _receivedBannerThisSession = false; private bool _receivedToastThisSession = false; public static AnnouncementManager Instance => _instance ?? (_instance = new AnnouncementManager()); public event Action<AnnouncementData> OnBannerReceived; public event Action<string, float, int> OnToastReceived; private AnnouncementManager() { LoadDismissedBanners(); } public void SetCurrentServer(string serverId) { _currentServerId = serverId; } public void ClearCurrentServer() { _currentServerId = null; _receivedBannerThisSession = false; _receivedToastThisSession = false; } public bool IsBannerDismissed(int version) { if (string.IsNullOrEmpty(_currentServerId)) { return false; } if (_dismissedBanners.TryGetValue(_currentServerId, out var value)) { if (value.Version != version) { return false; } if ((DateTime.Now - value.DismissedAt).TotalHours >= 24.0) { return false; } return true; } return false; } public void DismissBanner(int version) { if (!string.IsNullOrEmpty(_currentServerId)) { _dismissedBanners[_currentServerId] = new DismissedBannerInfo { Version = version, DismissedAt = DateTime.Now }; SaveDismissedBanners(); } } public void ResetAllAcknowledgments() { _dismissedBanners.Clear(); SaveDismissedBanners(); } public void HandleBannerReceived(AnnouncementData data, bool forceShow = false) { if (AnnouncementConfig.EnableNoticeDisplay.Value && (forceShow || !_receivedBannerThisSession) && (forceShow || !IsBannerDismissed(data.Version))) { _receivedBannerThisSession = true; this.OnBannerReceived?.Invoke(data); } } public void HandleToastReceived(string message, float duration, int fontSize = 20, bool forceShow = false) { if (AnnouncementConfig.EnableNoticeDisplay.Value && (forceShow || !_receivedToastThisSession)) { _receivedToastThisSession = true; this.OnToastReceived?.Invoke(message, duration, fontSize); } } private void LoadDismissedBanners() { try { string @string = PlayerPrefs.GetString("ServerAnnouncements_DismissedBanners", ""); if (string.IsNullOrEmpty(@string)) { return; } string[] array = @string.Split(new char[1] { ';' }); string[] array2 = array; foreach (string text in array2) { string[] array3 = text.Split(new char[1] { '|' }); if (array3.Length == 3 && int.TryParse(array3[1], out var result) && long.TryParse(array3[2], out var result2)) { _dismissedBanners[array3[0]] = new DismissedBannerInfo { Version = result, DismissedAt = new DateTime(result2) }; } } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("Failed to load dismissed banners: " + ex.Message)); } } private void SaveDismissedBanners() { try { List<string> list = new List<string>(); foreach (KeyValuePair<string, DismissedBannerInfo> dismissedBanner in _dismissedBanners) { list.Add($"{dismissedBanner.Key}|{dismissedBanner.Value.Version}|{dismissedBanner.Value.DismissedAt.Ticks}"); } PlayerPrefs.SetString("ServerAnnouncements_DismissedBanners", string.Join(";", list)); PlayerPrefs.Save(); } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("Failed to save dismissed banners: " + ex.Message)); } } public static bool IsHost() { return NetworkServer.active && (Object)(object)Player._mainPlayer != (Object)null && Player._mainPlayer._isHostPlayer; } public static string GetServerIdentifier() { if (NetworkClient.active && NetworkClient.connection != null) { return $"server_{NetworkClient.connection.connectionId}"; } return "local"; } } public struct AnnouncementData { public string Title; public string Content; public string ExtraContent; public int Version; public string ServerName; public int TitleFontSize; public int ContentFontSize; public int ExtraContentFontSize; public AnnouncementData(string title, string content, int version, string serverName = "", int titleFontSize = 24, int contentFontSize = 16, string extraContent = "", int extraContentFontSize = 14) { Title = title; Content = content; ExtraContent = extraContent; Version = version; ServerName = serverName; TitleFontSize = titleFontSize; ContentFontSize = contentFontSize; ExtraContentFontSize = extraContentFontSize; } } public class AnnouncementRouter { [CompilerGenerated] private sealed class <HandshakeAndNotifyCoroutine>d__13 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public int connectionId; public AnnouncementRouter <>4__this; private PlayerInfo <player>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <HandshakeAndNotifyCoroutine>d__13(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <player>5__1 = null; <>1__state = -2; } private bool MoveNext() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; case 1: <>1__state = -1; <player>5__1 = PlayerTypeManager.GetPlayer(connectionId); if (<player>5__1 == null) { return false; } <>4__this._modClientChannel.SendHandshake(connectionId); PlayerTypeManager.MarkHandshakeSent(connectionId); <>2__current = (object)new WaitForSeconds(3f); <>1__state = 2; return true; case 2: <>1__state = -1; PlayerTypeManager.CheckHandshakeTimeouts(); <player>5__1 = PlayerTypeManager.GetPlayer(connectionId); if (<player>5__1 == null) { return false; } if (AnnouncementConfig.EnableNoticeSystem.Value) { <>4__this.SendAnnouncementsToPlayer(connectionId); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static AnnouncementRouter _instance; private readonly HostChannel _hostChannel; private readonly ModClientChannel _modClientChannel; private readonly VanillaClientChannel _vanillaClientChannel; private float _lastBroadcastTime = 0f; private const float BROADCAST_COOLDOWN = 5f; private bool _hostReceivedThisSession = false; public static AnnouncementRouter Instance => _instance ?? (_instance = new AnnouncementRouter()); private AnnouncementRouter() { _hostChannel = new HostChannel(); _modClientChannel = new ModClientChannel(); _vanillaClientChannel = new VanillaClientChannel(); } public void ResetSession() { _hostReceivedThisSession = false; PlayerTypeManager.Reset(); } public void HandlePlayerConnected(int connectionId, ulong steamId = 0uL) { if (AnnouncementManager.IsHost()) { PlayerInfo playerInfo = PlayerTypeManager.RegisterPlayer(connectionId, steamId); if (playerInfo.Type != 0) { ((MonoBehaviour)ServerAnnouncementsPlugin.Instance).StartCoroutine(HandshakeAndNotifyCoroutine(connectionId)); } } } public void HandlePlayerDisconnected(int connectionId) { PlayerTypeManager.UnregisterPlayer(connectionId); } [IteratorStateMachine(typeof(<HandshakeAndNotifyCoroutine>d__13))] private IEnumerator HandshakeAndNotifyCoroutine(int connectionId) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <HandshakeAndNotifyCoroutine>d__13(0) { <>4__this = this, connectionId = connectionId }; } private void SendAnnouncementsToPlayer(int connectionId) { PlayerInfo player = PlayerTypeManager.GetPlayer(connectionId); if (player == null || player.NotificationSent) { return; } float duration = AnnouncementConfig.ToastDuration?.Value ?? 10f; int fontSize = AnnouncementConfig.ToastFontSize?.Value ?? 20; if (AnnouncementConfig.ShowToastOnJoin.Value) { string value = AnnouncementConfig.JoinToastMessage.Value; if (!string.IsNullOrEmpty(value)) { _modClientChannel.SendToast(connectionId, value, duration, fontSize, force: false); if (AnnouncementConfig.VanillaCompatMode.Value) { _vanillaClientChannel.SendToast(connectionId, value, duration, fontSize, force: false); } } } if (AnnouncementConfig.EnableNoticeSystem.Value) { AnnouncementData data = CreateBannerData(); if (!string.IsNullOrEmpty(data.Title) || !string.IsNullOrEmpty(data.Content)) { _modClientChannel.SendBanner(connectionId, data, force: false); if (AnnouncementConfig.VanillaCompatMode.Value) { _vanillaClientChannel.SendBanner(connectionId, data, force: false); } } } PlayerTypeManager.MarkNotified(connectionId); } public void BroadcastToast(string message, float duration, int fontSize, bool force = false, bool bypassRateLimit = false, bool skipLocalDisplay = false) { if (!NetworkServer.active) { ServerAnnouncementsPlugin.Log.LogWarning((object)"[Router] BroadcastToast called but server not active."); return; } if (!bypassRateLimit && !CheckRateLimit()) { ServerAnnouncementsPlugin.Log.LogWarning((object)"[Router] Broadcast rate limited."); return; } message = TruncateMessage(message); if (!skipLocalDisplay && (!_hostReceivedThisSession || force)) { _hostChannel.SendToast(0, message, duration, fontSize, force); _hostReceivedThisSession = true; } foreach (PlayerInfo client in PlayerTypeManager.GetClients()) { _modClientChannel.SendToast(client.ConnectionId, message, duration, fontSize, force); if (AnnouncementConfig.VanillaCompatMode.Value) { _vanillaClientChannel.SendToast(client.ConnectionId, message, duration, fontSize, force); } } _lastBroadcastTime = Time.time; } public void BroadcastBanner(bool force = false, bool bypassRateLimit = false, bool skipLocalDisplay = false) { if (!NetworkServer.active) { ServerAnnouncementsPlugin.Log.LogWarning((object)"[Router] BroadcastBanner called but server not active."); return; } if (!bypassRateLimit && !CheckRateLimit()) { ServerAnnouncementsPlugin.Log.LogWarning((object)"[Router] Broadcast rate limited."); return; } AnnouncementData data = CreateBannerData(); if (string.IsNullOrEmpty(data.Title) && string.IsNullOrEmpty(data.Content)) { ServerAnnouncementsPlugin.Log.LogWarning((object)"[Router] Banner has no content, skipping."); return; } if (!skipLocalDisplay && (!_hostReceivedThisSession || force)) { _hostChannel.SendBanner(0, data, force); _hostReceivedThisSession = true; } foreach (PlayerInfo client in PlayerTypeManager.GetClients()) { _modClientChannel.SendBanner(client.ConnectionId, data, force); if (AnnouncementConfig.VanillaCompatMode.Value) { _vanillaClientChannel.SendBanner(client.ConnectionId, data, force); } } _lastBroadcastTime = Time.time; } public void HandleClientHandshakeResponse(int connectionId, ulong steamId) { if (connectionId > 0) { PlayerTypeManager.HandleHandshakeResponse(connectionId, steamId); } else if (steamId != 0) { PlayerTypeManager.HandleHandshakeResponseBySteamId(steamId); } } private IAnnouncementChannel GetChannelForType(PlayerType type) { return type switch { PlayerType.Host => _hostChannel, PlayerType.ModClient => _modClientChannel, _ => _vanillaClientChannel, }; } private bool CheckRateLimit() { return Time.time - _lastBroadcastTime >= 5f; } private string TruncateMessage(string message) { if (!string.IsNullOrEmpty(message) && message.Length > 1000) { return message.Substring(0, 1000); } return message ?? ""; } private AnnouncementData CreateBannerData() { return new AnnouncementData(AnnouncementConfig.BannerTitle.Value ?? "", AnnouncementConfig.BannerContent.Value ?? "", AnnouncementConfig.NoticeVersion.Value, "", AnnouncementConfig.BannerTitleFontSize?.Value ?? 24, AnnouncementConfig.BannerContentFontSize?.Value ?? 16, AnnouncementConfig.BannerExtraContent?.Value ?? "", AnnouncementConfig.BannerExtraContentFontSize?.Value ?? 14); } } public static class CodeTalkerIntegration { private static bool _initialized; private static bool _available; private static bool _uiInitialized; public static bool IsAvailable => _available; public static void Initialize() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if (_initialized) { return; } _initialized = true; try { if (CodeTalkerNetwork.RegisterBinaryListener<AnnouncementBinaryPacket>(new BinaryPacketListener(OnPacketReceived))) { _available = true; ServerAnnouncementsPlugin.Log.LogInfo((object)"CodeTalker integration initialized successfully!"); } else { ServerAnnouncementsPlugin.Log.LogWarning((object)"CodeTalker listener registration failed."); _available = false; } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("CodeTalker integration failed: " + ex.Message)); _available = false; } } private static void EnsureUIInitialized() { if (_uiInitialized) { return; } try { AnnouncementToastUI.Initialize(); AnnouncementBannerUI.Initialize(); _uiInitialized = true; ServerAnnouncementsPlugin.Log.LogInfo((object)"UI components initialized via CodeTalker packet handler."); } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("EnsureUIInitialized error: " + ex.Message)); } } private static void OnPacketReceived(PacketHeader header, BinaryPacketBase packet) { try { if (!(packet is AnnouncementBinaryPacket announcementBinaryPacket)) { return; } ServerAnnouncementsPlugin.Log.LogInfo((object)$"Received CodeTalker announcement: Type={announcementBinaryPacket.Type}, FromHost={header.SenderIsLobbyOwner}, SenderID={header.SenderID}"); if (announcementBinaryPacket.Type == "ClientResponse") { if (AnnouncementManager.IsHost()) { ulong senderID = header.SenderID; if (senderID != 0) { PlayerTypeManager.HandleHandshakeResponseBySteamId(senderID); } } } else if (!header.SenderIsLobbyOwner) { ServerAnnouncementsPlugin.Log.LogWarning((object)("Received CodeTalker " + announcementBinaryPacket.Type + " from non-host, ignoring.")); } else { ProcessReceivedPacket(announcementBinaryPacket); } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogError((object)("Error processing CodeTalker packet: " + ex.Message)); } } private static void ProcessReceivedPacket(AnnouncementBinaryPacket packet) { EnsureUIInitialized(); switch (packet.Type) { case "Toast": AnnouncementManager.Instance?.HandleToastReceived(packet.Content, packet.Duration, packet.ToastFontSize, packet.ForceShow); break; case "Banner": { AnnouncementData data = new AnnouncementData(packet.Title, packet.Content, packet.Version, "", packet.TitleFontSize, packet.ContentFontSize, packet.ExtraContent, packet.ExtraFontSize); AnnouncementManager.Instance?.HandleBannerReceived(data, packet.ForceShow); break; } case "Handshake": AnnouncementNetworkHandler.HandleHandshake("ServerAnnouncements_v1_CT"); ServerAnnouncementsPlugin.Log.LogInfo((object)"Received CodeTalker handshake from server."); SendClientResponse(); break; default: ServerAnnouncementsPlugin.Log.LogWarning((object)("Unknown announcement type: " + packet.Type)); break; } } private static bool SendPacket(AnnouncementBinaryPacket packet) { if (!_available) { return false; } try { CodeTalkerNetwork.SendNetworkPacket((BinaryPacketBase)(object)packet); ServerAnnouncementsPlugin.Log.LogInfo((object)("Sent CodeTalker packet: Type=" + packet.Type)); return true; } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogError((object)("Failed to send CodeTalker packet: " + ex.Message)); return false; } } public static bool BroadcastToast(string message, float duration, int fontSize, bool forceShow) { if (!_available) { return false; } AnnouncementBinaryPacket packet = new AnnouncementBinaryPacket { Type = "Toast", Content = (message ?? ""), Duration = duration, ToastFontSize = fontSize, ForceShow = forceShow }; return SendPacket(packet); } public static bool BroadcastBanner(string title, string content, string extraContent, int version, int titleFontSize, int contentFontSize, int extraFontSize, bool forceShow) { if (!_available) { return false; } AnnouncementBinaryPacket packet = new AnnouncementBinaryPacket { Type = "Banner", Title = (title ?? ""), Content = (content ?? ""), ExtraContent = (extraContent ?? ""), Version = version, TitleFontSize = titleFontSize, ContentFontSize = contentFontSize, ExtraFontSize = extraFontSize, ForceShow = forceShow }; return SendPacket(packet); } public static bool SendHandshake() { if (!_available) { return false; } AnnouncementBinaryPacket packet = new AnnouncementBinaryPacket { Type = "Handshake", Content = "ServerAnnouncements_v1" }; return SendPacket(packet); } public static bool SendClientResponse() { if (!_available) { return false; } int num = -1; try { if (NetworkClient.connection != null) { num = NetworkClient.connection.connectionId; } } catch { } AnnouncementBinaryPacket packet = new AnnouncementBinaryPacket { Type = "ClientResponse", Content = num.ToString() }; bool flag = SendPacket(packet); if (flag) { ServerAnnouncementsPlugin.Log.LogInfo((object)$"Sent client response to server (connectionId={num})"); } return flag; } } public class AnnouncementBinaryPacket : BinaryPacketBase { public override string PacketSignature => "SA_ANN_v1"; public string Type { get; set; } = ""; public string Title { get; set; } = ""; public string Content { get; set; } = ""; public string ExtraContent { get; set; } = ""; public int Version { get; set; } public float Duration { get; set; } public int TitleFontSize { get; set; } public int ContentFontSize { get; set; } public int ExtraFontSize { get; set; } public int ToastFontSize { get; set; } public bool ForceShow { get; set; } public override byte[] Serialize() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(EscapePipe(Type)); stringBuilder.Append("|"); stringBuilder.Append(EscapePipe(Title)); stringBuilder.Append("|"); stringBuilder.Append(EscapePipe(Content)); stringBuilder.Append("|"); stringBuilder.Append(EscapePipe(ExtraContent)); stringBuilder.Append("|"); stringBuilder.Append(Version); stringBuilder.Append("|"); stringBuilder.Append(Duration.ToString("F2")); stringBuilder.Append("|"); stringBuilder.Append(TitleFontSize); stringBuilder.Append("|"); stringBuilder.Append(ContentFontSize); stringBuilder.Append("|"); stringBuilder.Append(ExtraFontSize); stringBuilder.Append("|"); stringBuilder.Append(ToastFontSize); stringBuilder.Append("|"); stringBuilder.Append(ForceShow ? "1" : "0"); return Encoding.UTF8.GetBytes(stringBuilder.ToString()); static string EscapePipe(string s) { return (s ?? "").Replace("|", "\\|").Replace("\n", "\\n"); } } public override void Deserialize(byte[] data) { try { string @string = Encoding.UTF8.GetString(data); string[] array = SplitByUnescapedPipe(@string); if (array.Length >= 11) { Type = UnescapePipe(array[0]); Title = UnescapePipe(array[1]); Content = UnescapePipe(array[2]); ExtraContent = UnescapePipe(array[3]); int.TryParse(array[4], out var result); Version = result; float.TryParse(array[5], out var result2); Duration = result2; int.TryParse(array[6], out var result3); TitleFontSize = result3; int.TryParse(array[7], out var result4); ContentFontSize = result4; int.TryParse(array[8], out var result5); ExtraFontSize = result5; int.TryParse(array[9], out var result6); ToastFontSize = result6; ForceShow = array[10] == "1"; } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogError((object)("AnnouncementBinaryPacket.Deserialize error: " + ex.Message)); } static string UnescapePipe(string s) { return (s ?? "").Replace("\\|", "|").Replace("\\n", "\n"); } } private static string[] SplitByUnescapedPipe(string input) { List<string> list = new List<string>(); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < input.Length; i++) { if (input[i] == '|' && (i == 0 || input[i - 1] != '\\')) { list.Add(stringBuilder.ToString()); stringBuilder.Clear(); } else { stringBuilder.Append(input[i]); } } list.Add(stringBuilder.ToString()); return list.ToArray(); } } public static class AnnouncementNetworkHandler { private static bool _initialized; private static bool _serverHasNoticeMod; private static string _serverModSignature; private const string HANDSHAKE_PREFIX = "[NTC:HS]"; private const string TOAST_PREFIX = "[NTC:T]"; private const string BANNER_PREFIX = "[NTC:B]"; private const string CLIENT_RESPONSE_PREFIX = "[NTC:CR]"; public static bool ServerHasNoticeMod => _serverHasNoticeMod; public static void Initialize() { if (!_initialized) { _initialized = true; } } public static void ResetHandshake() { _serverHasNoticeMod = false; _serverModSignature = null; AnnouncementRouter.Instance.ResetSession(); } public static void HandleHandshake(string signature) { _serverHasNoticeMod = true; _serverModSignature = signature; } public static bool IsSoloMode() { try { Type type = Type.GetType("AtlyssNetworkManager, Assembly-CSharp"); if (type != null) { FieldInfo field = type.GetField("_current", BindingFlags.Static | BindingFlags.Public); if (field != null) { object value = field.GetValue(null); if (value != null) { FieldInfo field2 = type.GetField("_soloMode", BindingFlags.Instance | BindingFlags.Public); if (field2 != null) { return (bool)field2.GetValue(value); } } } } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("IsSoloMode check failed: " + ex.Message)); } return false; } public static void BroadcastToast(string message, float duration, bool skipLocalDisplay = false, bool forceShow = false, bool bypassRateLimit = false, bool skipVanillaCompat = false) { int fontSize = AnnouncementConfig.ToastFontSize?.Value ?? 20; AnnouncementRouter.Instance.BroadcastToast(message, duration, fontSize, forceShow, bypassRateLimit, skipLocalDisplay); } public static void BroadcastBanner(bool forceShow = false, bool skipLocalDisplay = false, bool bypassRateLimit = false, bool skipVanillaCompat = false) { AnnouncementRouter.Instance.BroadcastBanner(forceShow, bypassRateLimit, skipLocalDisplay); } public static void SendHandshake() { } public static bool TryParseHandshake(string message, out string signature) { signature = null; if (string.IsNullOrEmpty(message) || !message.StartsWith("[NTC:HS]")) { return false; } signature = message.Substring("[NTC:HS]".Length); if (signature.EndsWith("<>")) { signature = signature.Substring(0, signature.Length - 2); } return !string.IsNullOrEmpty(signature); } public static bool TryParseToast(string message, out string text, out float duration, out int fontSize, out bool forceShow) { text = null; duration = 0f; fontSize = 20; forceShow = false; if (string.IsNullOrEmpty(message) || !message.StartsWith("[NTC:T]")) { return false; } try { string text2 = message.Substring("[NTC:T]".Length); if (text2.EndsWith("<>")) { text2 = text2.Substring(0, text2.Length - 2); } if (text2.EndsWith("|FORCE")) { forceShow = true; text2 = text2.Substring(0, text2.Length - 6); } string[] array = text2.Split(new char[1] { '|' }, 3); if (array.Length < 3) { if (array.Length >= 2) { if (!float.TryParse(array[0], out duration)) { return false; } text = array[1]; return true; } return false; } if (!float.TryParse(array[0], out duration)) { return false; } if (!int.TryParse(array[1], out fontSize)) { fontSize = 20; } text = array[2]; return true; } catch { return false; } } public static bool TryParseBanner(string message, out AnnouncementData data, out bool forceShow) { data = default(AnnouncementData); forceShow = false; if (string.IsNullOrEmpty(message) || !message.StartsWith("[NTC:B]")) { return false; } try { string text = message.Substring("[NTC:B]".Length); if (text.EndsWith("<>")) { text = text.Substring(0, text.Length - 2); } if (text.EndsWith("|FORCE")) { forceShow = true; text = text.Substring(0, text.Length - 6); } string[] array = text.Split(new char[1] { '|' }, 7); if (array.Length >= 7) { if (!int.TryParse(array[0], out var result)) { return false; } if (!int.TryParse(array[1], out var result2)) { result2 = 24; } if (!int.TryParse(array[2], out var result3)) { result3 = 16; } if (!int.TryParse(array[3], out var result4)) { result4 = 14; } data = new AnnouncementData(array[4], array[5].Replace("\\|", "|"), result, "", result2, result3, array[6].Replace("\\|", "|"), result4); return true; } if (array.Length >= 5) { if (!int.TryParse(array[0], out var result5)) { return false; } if (!int.TryParse(array[1], out var result6)) { result6 = 24; } if (!int.TryParse(array[2], out var result7)) { result7 = 16; } data = new AnnouncementData(array[3], array[4].Replace("\\|", "|"), result5, "", result6, result7); return true; } if (array.Length >= 3) { if (!int.TryParse(array[0], out var result8)) { return false; } data = new AnnouncementData(array[1], array[2].Replace("\\|", "|"), result8); return true; } return false; } catch { return false; } } } [HarmonyPatch] public static class NoticeNetworkPatches { private static readonly string[] VANILLA_PREFIXES = new string[3] { "[Notice]", "[Banner]", " > " }; private static bool _uiInitialized = false; [HarmonyPatch(typeof(ChatBehaviour), "New_ChatMessage")] [HarmonyPrefix] private static bool OnReceiveChatMessage(string _message) { if (string.IsNullOrEmpty(_message)) { return true; } try { string[] vANILLA_PREFIXES = VANILLA_PREFIXES; foreach (string value in vANILLA_PREFIXES) { if (_message.Contains(value)) { return false; } } if (AnnouncementNetworkHandler.TryParseToast(_message, out var text, out var duration, out var fontSize, out var forceShow)) { AnnouncementManager.Instance?.HandleToastReceived(text, duration, fontSize, forceShow); return false; } if (AnnouncementNetworkHandler.TryParseBanner(_message, out var data, out var forceShow2)) { AnnouncementManager.Instance?.HandleBannerReceived(data, forceShow2); return false; } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("OnReceiveChatMessage error: " + ex.Message)); } return true; } [HarmonyPatch(typeof(ChatBehaviour), "OnServerMessage")] [HarmonyPrefix] private static bool OnServerMessageReceived(ref object __0) { try { if (__0 == null) { return true; } Type type = __0.GetType(); FieldInfo field = type.GetField("servMsg"); if (field == null) { return true; } string text = field.GetValue(__0) as string; if (string.IsNullOrEmpty(text)) { return true; } EnsureUIInitialized(); string[] vANILLA_PREFIXES = VANILLA_PREFIXES; foreach (string value in vANILLA_PREFIXES) { if (text.Contains(value)) { return false; } } if (AnnouncementNetworkHandler.TryParseHandshake(text, out var signature)) { AnnouncementNetworkHandler.HandleHandshake(signature); SendClientResponse(); return false; } if (text.StartsWith("[NTC:") && !AnnouncementNetworkHandler.ServerHasNoticeMod) { AnnouncementNetworkHandler.HandleHandshake("ServerAnnouncements_Auto"); } if (AnnouncementManager.IsHost() && (text.StartsWith("[NTC:T]") || text.StartsWith("[NTC:B]"))) { return false; } if (AnnouncementNetworkHandler.TryParseToast(text, out var text2, out var duration, out var fontSize, out var forceShow)) { AnnouncementManager.Instance?.HandleToastReceived(text2, duration, fontSize, forceShow); return false; } if (AnnouncementNetworkHandler.TryParseBanner(text, out var data, out var forceShow2)) { AnnouncementManager.Instance?.HandleBannerReceived(data, forceShow2); return false; } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("OnServerMessageReceived error: " + ex.Message)); } return true; } private static void EnsureUIInitialized() { if (_uiInitialized) { return; } try { AnnouncementToastUI.Initialize(); AnnouncementBannerUI.Initialize(); _uiInitialized = true; } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("EnsureUIInitialized error: " + ex.Message)); } } private static void SendClientResponse() { CodeTalkerIntegration.SendClientResponse(); } } public enum PlayerType { Host, ModClient, VanillaClient, Pending } public class PlayerInfo { public int ConnectionId { get; set; } public ulong SteamId { get; set; } public PlayerType Type { get; set; } public DateTime ConnectedAt { get; set; } public DateTime? HandshakeSentAt { get; set; } public bool HandshakeResponseReceived { get; set; } public bool NotificationSent { get; set; } public PlayerInfo(int connectionId, ulong steamId = 0uL) { ConnectionId = connectionId; SteamId = steamId; Type = ((connectionId != 0) ? PlayerType.Pending : PlayerType.Host); ConnectedAt = DateTime.Now; HandshakeSentAt = null; HandshakeResponseReceived = false; NotificationSent = false; } } public static class PlayerTypeManager { private static readonly Dictionary<int, PlayerInfo> _players = new Dictionary<int, PlayerInfo>(); private const float HANDSHAKE_TIMEOUT = 5f; public static PlayerInfo RegisterPlayer(int connectionId, ulong steamId = 0uL) { if (_players.TryGetValue(connectionId, out var value)) { if (steamId != 0) { value.SteamId = steamId; } return value; } PlayerInfo playerInfo = new PlayerInfo(connectionId, steamId); _players[connectionId] = playerInfo; return playerInfo; } public static void UnregisterPlayer(int connectionId) { _players.Remove(connectionId); } public static void MarkHandshakeSent(int connectionId) { if (_players.TryGetValue(connectionId, out var value)) { value.HandshakeSentAt = DateTime.Now; } } public static void HandleHandshakeResponse(int connectionId, ulong steamId) { if (!_players.TryGetValue(connectionId, out var value)) { value = RegisterPlayer(connectionId, steamId); } if (steamId != 0) { value.SteamId = steamId; } value.HandshakeResponseReceived = true; value.Type = PlayerType.ModClient; } public static void HandleHandshakeResponseBySteamId(ulong steamId) { PlayerInfo playerInfo = _players.Values.FirstOrDefault((PlayerInfo p) => p.SteamId == steamId); if (playerInfo != null) { playerInfo.HandshakeResponseReceived = true; playerInfo.Type = PlayerType.ModClient; return; } using IEnumerator<PlayerInfo> enumerator = _players.Values.Where((PlayerInfo p) => p.Type == PlayerType.Pending && p.SteamId == 0).GetEnumerator(); if (enumerator.MoveNext()) { PlayerInfo current = enumerator.Current; current.SteamId = steamId; current.HandshakeResponseReceived = true; current.Type = PlayerType.ModClient; } } public static void CheckHandshakeTimeouts() { DateTime now = DateTime.Now; foreach (PlayerInfo value in _players.Values) { if (value.Type == PlayerType.Pending && value.HandshakeSentAt.HasValue && !value.HandshakeResponseReceived) { double totalSeconds = (now - value.HandshakeSentAt.Value).TotalSeconds; if (totalSeconds >= 5.0) { value.Type = PlayerType.VanillaClient; } } } } public static PlayerType GetPlayerType(int connectionId) { if (connectionId == 0) { return PlayerType.Host; } if (_players.TryGetValue(connectionId, out var value)) { return value.Type; } return PlayerType.Pending; } public static PlayerInfo GetPlayer(int connectionId) { _players.TryGetValue(connectionId, out var value); return value; } public static IEnumerable<PlayerInfo> GetPlayersByType(PlayerType type) { return _players.Values.Where((PlayerInfo p) => p.Type == type); } public static IEnumerable<PlayerInfo> GetClients() { return _players.Values.Where((PlayerInfo p) => p.Type != PlayerType.Host); } public static void MarkNotified(int connectionId) { if (_players.TryGetValue(connectionId, out var value)) { value.NotificationSent = true; } } public static bool IsNotified(int connectionId) { if (_players.TryGetValue(connectionId, out var value)) { return value.NotificationSent; } return false; } public static void Reset() { _players.Clear(); } public static void SyncWithNetworkConnections() { if (!NetworkServer.active) { Reset(); return; } HashSet<int> activeConnectionIds = new HashSet<int>(); foreach (KeyValuePair<int, NetworkConnectionToClient> connection in NetworkServer.connections) { if (connection.Value != null) { activeConnectionIds.Add(connection.Key); } } List<int> list = _players.Keys.Where((int id) => !activeConnectionIds.Contains(id)).ToList(); foreach (int item in list) { _players.Remove(item); } } public static string GetStatusSummary() { int num = _players.Values.Count((PlayerInfo p) => p.Type == PlayerType.Host); int num2 = _players.Values.Count((PlayerInfo p) => p.Type == PlayerType.ModClient); int num3 = _players.Values.Count((PlayerInfo p) => p.Type == PlayerType.VanillaClient); int num4 = _players.Values.Count((PlayerInfo p) => p.Type == PlayerType.Pending); return $"Players: {_players.Count} total (Host={num}, Mod={num2}, Vanilla={num3}, Pending={num4})"; } } public static class PlayerConnectionPatches { private static bool _initialized; public static void Initialize() { if (_initialized) { return; } try { AnnouncementToastUI.Initialize(); AnnouncementBannerUI.Initialize(); AnnouncementNetworkHandler.Initialize(); _initialized = true; } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogError((object)("PlayerConnectionPatches init error: " + ex.Message)); } } public static void OnDisconnect() { try { _initialized = false; AnnouncementManager.Instance.ClearCurrentServer(); AnnouncementNetworkHandler.ResetHandshake(); } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("OnDisconnect error: " + ex.Message)); } } } [HarmonyPatch] public static class SafeGamePatches { [CompilerGenerated] private sealed class <DelayedInitialize>d__2 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private string <serverId>5__1; private bool <isHost>5__2; private bool <initError>5__3; private int <waitFrames>5__4; private Exception <ex>5__5; private string <toastMessage>5__6; private float <duration>5__7; private int <fontSize>5__8; private Exception <ex>5__9; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedInitialize>d__2(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <serverId>5__1 = null; <ex>5__5 = null; <toastMessage>5__6 = null; <ex>5__9 = null; <>1__state = -2; } private bool MoveNext() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Invalid comparison between Unknown and I4 //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; case 1: <>1__state = -1; <serverId>5__1 = null; <isHost>5__2 = false; <initError>5__3 = false; try { <serverId>5__1 = AnnouncementManager.GetServerIdentifier(); AnnouncementManager.Instance.SetCurrentServer(<serverId>5__1); PlayerConnectionPatches.Initialize(); if (AnnouncementNetworkHandler.IsSoloMode()) { ServerAnnouncementsPlugin.Log.LogInfo((object)"Solo mode detected - notice system disabled."); <initError>5__3 = true; } else { <isHost>5__2 = AnnouncementManager.IsHost(); ServerAnnouncementsPlugin.Log.LogInfo((object)$"Connected to server: {<serverId>5__1} (isHost={<isHost>5__2})"); } } catch (Exception ex) { <ex>5__5 = ex; ServerAnnouncementsPlugin.Log.LogError((object)("DelayedInitialize init error: " + <ex>5__5.Message)); <initError>5__3 = true; } if (<initError>5__3) { return false; } if (!<isHost>5__2 || !AnnouncementConfig.EnableNoticeSystem.Value) { return false; } <waitFrames>5__4 = 0; goto IL_01af; case 2: <>1__state = -1; <waitFrames>5__4++; if (!((Object)(object)Player._mainPlayer != (Object)null) || (int)Player._mainPlayer._currentGameCondition != 1 || Player._mainPlayer._bufferingStatus) { goto IL_01af; } goto IL_01c2; case 3: { <>1__state = -1; try { AnnouncementRouter.Instance.HandlePlayerConnected(0, 0uL); if (AnnouncementConfig.ShowToastOnJoin.Value) { <toastMessage>5__6 = AnnouncementConfig.JoinToastMessage.Value; <duration>5__7 = AnnouncementConfig.ToastDuration.Value; <fontSize>5__8 = AnnouncementConfig.ToastFontSize?.Value ?? 20; AnnouncementRouter.Instance.BroadcastToast(<toastMessage>5__6, <duration>5__7, <fontSize>5__8, force: true, bypassRateLimit: true); <toastMessage>5__6 = null; } if (AnnouncementConfig.EnableNoticeSystem.Value) { AnnouncementRouter.Instance.BroadcastBanner(force: true, bypassRateLimit: true); } } catch (Exception ex) { <ex>5__9 = ex; ServerAnnouncementsPlugin.Log.LogError((object)("DelayedInitialize broadcast error: " + <ex>5__9.Message)); } return false; } IL_01af: if (<waitFrames>5__4 < 300) { <>2__current = null; <>1__state = 2; return true; } goto IL_01c2; IL_01c2: <>2__current = (object)new WaitForSeconds(2f); <>1__state = 3; return true; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static bool _localPlayerInitialized; [HarmonyPatch(typeof(Player), "Awake")] [HarmonyPostfix] private static void OnPlayerAwake(Player __instance) { try { if (!_localPlayerInitialized && (Object)(object)ServerAnnouncementsPlugin.Instance != (Object)null) { _localPlayerInitialized = true; ((MonoBehaviour)ServerAnnouncementsPlugin.Instance).StartCoroutine(DelayedInitialize()); } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("OnPlayerAwake error: " + ex.Message)); } } [IteratorStateMachine(typeof(<DelayedInitialize>d__2))] private static IEnumerator DelayedInitialize() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedInitialize>d__2(0); } public static void ResetState() { _localPlayerInitialized = false; PlayerConnectionPatches.OnDisconnect(); } } [HarmonyPatch] public static class PlayerJoinPatches { [CompilerGenerated] private sealed class <HandleNewPlayerCoroutine>d__4 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private int <newestConnectionId>5__1; private Exception <ex>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <HandleNewPlayerCoroutine>d__4(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <ex>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; case 1: <>1__state = -1; try { <newestConnectionId>5__1 = FindNewestConnection(); if (<newestConnectionId>5__1 > 0) { AnnouncementRouter.Instance.HandlePlayerConnected(<newestConnectionId>5__1, 0uL); } } catch (Exception ex) { <ex>5__2 = ex; ServerAnnouncementsPlugin.Log.LogError((object)("HandleNewPlayerCoroutine error: " + <ex>5__2.Message)); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static float _lastBroadcastTime; private const float DUPLICATE_PREVENTION_TIME = 10f; public static void Reset() { _lastBroadcastTime = 0f; } [HarmonyPatch(typeof(HostConsole), "Init_ServerMessage")] [HarmonyPostfix] private static void OnServerMessage(string _message) { try { if (_message.Contains(" disconnected")) { if (AnnouncementManager.IsHost()) { PlayerTypeManager.SyncWithNetworkConnections(); } } else { if (!_message.Contains(" connected") || !AnnouncementManager.IsHost() || !AnnouncementConfig.EnableNoticeSystem.Value || AnnouncementNetworkHandler.IsSoloMode()) { return; } float time = Time.time; if (!(time - _lastBroadcastTime < 10f)) { _lastBroadcastTime = time; if ((Object)(object)ServerAnnouncementsPlugin.Instance != (Object)null) { ((MonoBehaviour)ServerAnnouncementsPlugin.Instance).StartCoroutine(HandleNewPlayerCoroutine()); } } } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("OnServerMessage error: " + ex.Message)); } } [IteratorStateMachine(typeof(<HandleNewPlayerCoroutine>d__4))] private static IEnumerator HandleNewPlayerCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <HandleNewPlayerCoroutine>d__4(0); } private static int FindNewestConnection() { int num = 0; if (NetworkServer.connections != null) { foreach (KeyValuePair<int, NetworkConnectionToClient> connection in NetworkServer.connections) { if (connection.Key > num) { num = connection.Key; } } } return num; } } [HarmonyPatch] public static class DisconnectPatches { [HarmonyPatch(typeof(AtlyssNetworkManager), "OnStopClient")] [HarmonyPostfix] private static void OnStopClient() { try { SafeGamePatches.ResetState(); PlayerJoinPatches.Reset(); } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("OnStopClient error: " + ex.Message)); } } } [BepInPlugin("com.eleen.atlyss.serverannouncements", "Server Announcements", "1.0.0")] [BepInProcess("ATLYSS.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class ServerAnnouncementsPlugin : BaseUnityPlugin { public const string PLUGIN_GUID = "com.eleen.atlyss.serverannouncements"; public const string PLUGIN_NAME = "Server Announcements"; public const string PLUGIN_VERSION = "1.0.0"; private const string OLD_PLUGIN_GUID = "com.github.servnotice"; internal static ManualLogSource Log; internal static ServerAnnouncementsPlugin Instance; private static bool _easySettingsLoaded; private Harmony _harmony; public static bool IsEasySettingsAvailable => _easySettingsLoaded; private void Awake() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; MigrateOldConfig(); AnnouncementConfig.Initialize(((BaseUnityPlugin)this).Config); _harmony = new Harmony("com.eleen.atlyss.serverannouncements"); _harmony.PatchAll(); AnnouncementCommandHandler.Initialize(); AnnouncementSettingsWindow.Initialize(); TryInitializeEasySettings(); CodeTalkerIntegration.Initialize(); Log.LogInfo((object)string.Format("{0} v{1} loaded. Press F12 for settings. EasySettings: {2}, CodeTalker: {3}", "Server Announcements", "1.0.0", _easySettingsLoaded, CodeTalkerIntegration.IsAvailable)); } private void MigrateOldConfig() { try { string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath); string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; string text = Path.Combine(directoryName, "com.github.servnotice.cfg"); if (File.Exists(configFilePath)) { Log.LogInfo((object)"New config file exists, skipping migration."); } else if (File.Exists(text)) { File.Copy(text, configFilePath); Log.LogInfo((object)"Migrated config from com.github.servnotice.cfg to com.eleen.atlyss.serverannouncements.cfg"); string text2 = text + ".migrated"; if (!File.Exists(text2)) { File.Move(text, text2); Log.LogInfo((object)"Old config renamed to com.github.servnotice.cfg.migrated"); } } else { Log.LogInfo((object)"No existing config found, will create new one."); } } catch (Exception ex) { Log.LogWarning((object)("Config migration failed: " + ex.Message)); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void TryInitializeEasySettings() { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); _easySettingsLoaded = assemblies.Any((Assembly a) => a.GetName().Name == "EasySettings"); if (_easySettingsLoaded) { EasySettingsIntegration.Initialize(); Log.LogInfo((object)"EasySettings integration initialized."); } } catch (Exception ex) { Log.LogWarning((object)("EasySettings integration failed: " + ex.Message)); _easySettingsLoaded = false; } } } internal static class EasySettingsIntegration { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func<Assembly, bool> <>9__4_0; public static Func<MethodInfo, bool> <>9__6_3; public static Func<MethodInfo, bool> <>9__6_4; public static Func<MethodInfo, bool> <>9__6_5; public static Func<MethodInfo, bool> <>9__6_6; public static UnityAction <>9__6_0; public static UnityAction <>9__6_1; public static UnityAction <>9__6_2; internal bool <IsAvailable>b__4_0(Assembly a) { return a.GetName().Name == "EasySettings"; } internal bool <CreateSettingsUI>b__6_3(MethodInfo x) { return x.Name == "AddToggle"; } internal bool <CreateSettingsUI>b__6_4(MethodInfo x) { return x.Name == "AddTextField"; } internal bool <CreateSettingsUI>b__6_5(MethodInfo x) { return x.Name == "AddSlider"; } internal bool <CreateSettingsUI>b__6_6(MethodInfo x) { return x.Name == "AddAdvancedSlider"; } internal void <CreateSettingsUI>b__6_0() { AnnouncementManager.Instance.ResetAllAcknowledgments(); ShowFeedback("Notice history cleared!"); } internal void <CreateSettingsUI>b__6_1() { if (AnnouncementManager.IsHost()) { AnnouncementCommandHandler.SendToastNow(); ShowFeedback("Toast sent!"); } else { ShowFeedback("Host only!"); } } internal void <CreateSettingsUI>b__6_2() { if (AnnouncementManager.IsHost()) { AnnouncementCommandHandler.ShowBannerToAll(); ShowFeedback("Banner shown to all!"); } else { ShowFeedback("Host only!"); } } } [CompilerGenerated] private sealed class <>c__DisplayClass6_0 { public string currentKey; internal void <CreateSettingsUI>b__7() { ShowFeedback("Press " + currentKey + " to open settings and change keybind there."); } } private static bool _initialized; private static Assembly _easySettingsAssembly; private static Type _settingsType; private static Type _settingsTabType; public static bool IsAvailable() { try { _easySettingsAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "EasySettings"); return _easySettingsAssembly != null; } catch { return false; } } public static void Initialize() { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown if (_initialized || (_easySettingsAssembly == null && !IsAvailable())) { return; } try { _settingsType = _easySettingsAssembly.GetType("Nessie.ATLYSS.EasySettings.Settings"); _settingsTabType = _easySettingsAssembly.GetType("Nessie.ATLYSS.EasySettings.SettingsTab"); if (_settingsType == null || _settingsTabType == null) { ServerAnnouncementsPlugin.Log.LogWarning((object)"EasySettings types not found."); return; } PropertyInfo property = _settingsType.GetProperty("OnInitialized", BindingFlags.Static | BindingFlags.Public); if (property != null) { object? value = property.GetValue(null); UnityEvent val = (UnityEvent)((value is UnityEvent) ? value : null); if (val != null) { val.AddListener(new UnityAction(CreateSettingsUI)); } } PropertyInfo property2 = _settingsType.GetProperty("OnApplySettings", BindingFlags.Static | BindingFlags.Public); if (property2 != null) { object? value2 = property2.GetValue(null); UnityEvent val2 = (UnityEvent)((value2 is UnityEvent) ? value2 : null); if (val2 != null) { val2.AddListener(new UnityAction(OnApplySettings)); } } _initialized = true; ServerAnnouncementsPlugin.Log.LogInfo((object)"EasySettings integration initialized via reflection."); } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogError((object)("EasySettings reflection init error: " + ex.Message)); } } private static void CreateSettingsUI() { //IL_0c26: Unknown result type (might be due to invalid IL or missing references) //IL_0c2b: Unknown result type (might be due to invalid IL or missing references) //IL_0c31: Expected O, but got Unknown //IL_053a: Unknown result type (might be due to invalid IL or missing references) //IL_0609: Unknown result type (might be due to invalid IL or missing references) //IL_060e: Unknown result type (might be due to invalid IL or missing references) //IL_0614: Expected O, but got Unknown //IL_053f: Unknown result type (might be due to invalid IL or missing references) //IL_0553: Expected O, but got I4 //IL_05a0: Unknown result type (might be due to invalid IL or missing references) //IL_05a6: Expected O, but got Unknown //IL_0bc6: Unknown result type (might be due to invalid IL or missing references) //IL_0bcb: Unknown result type (might be due to invalid IL or missing references) //IL_0bd1: Expected O, but got Unknown //IL_053f->IL053f: Incompatible stack types: O vs I4 //IL_053a->IL053f: Incompatible stack types: I4 vs O //IL_053a->IL053f: Incompatible stack types: O vs I4 try { object obj = null; string text = AnnouncementConfig.EasySettingsTab?.Value ?? ""; if (!string.IsNullOrEmpty(text) && text.ToLower() != "mods") { MethodInfo method = _settingsType.GetMethod("GetOrAddCustomTab", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (method != null) { obj = method.Invoke(null, new object[1] { text }); ServerAnnouncementsPlugin.Log.LogInfo((object)("Using custom EasySettings tab: " + text)); } else { ServerAnnouncementsPlugin.Log.LogWarning((object)"GetOrAddCustomTab not found, falling back to ModTab"); } } if (obj == null) { PropertyInfo property = _settingsType.GetProperty("ModTab", BindingFlags.Static | BindingFlags.Public); if (property == null) { ServerAnnouncementsPlugin.Log.LogWarning((object)"ModTab property not found."); return; } obj = property.GetValue(null); ServerAnnouncementsPlugin.Log.LogInfo((object)"Using default ModTab for EasySettings"); } if (obj == null) { return; } MethodInfo[] methods = _settingsTabType.GetMethods(BindingFlags.Instance | BindingFlags.Public); MethodInfo method2 = _settingsTabType.GetMethod("AddHeader", new Type[1] { typeof(string) }); MethodInfo method3 = _settingsTabType.GetMethod("AddSpace", Type.EmptyTypes); MethodInfo method4 = _settingsTabType.GetMethod("AddButton", new Type[2] { typeof(string), typeof(UnityAction) }); MethodInfo methodInfo = null; foreach (MethodInfo item in methods.Where((MethodInfo x) => x.Name == "AddToggle")) { ParameterInfo[] parameters = item.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string)) { methodInfo = item; break; } } MethodInfo methodInfo2 = null; foreach (MethodInfo item2 in methods.Where((MethodInfo x) => x.Name == "AddTextField")) { ParameterInfo[] parameters2 = item2.GetParameters(); if (parameters2.Length >= 2 && parameters2[0].ParameterType == typeof(string)) { methodInfo2 = item2; break; } } MethodInfo methodInfo3 = null; foreach (MethodInfo item3 in methods.Where((MethodInfo x) => x.Name == "AddSlider")) { ParameterInfo[] parameters3 = item3.GetParameters(); if (parameters3.Length >= 1) { if (parameters3[0].ParameterType == typeof(string) && parameters3.Length >= 2) { methodInfo3 = item3; break; } if (parameters3[0].ParameterType.IsGenericType && methodInfo3 == null) { methodInfo3 = item3; } } } MethodInfo methodInfo4 = null; foreach (MethodInfo item4 in methods.Where((MethodInfo x) => x.Name == "AddAdvancedSlider")) { ParameterInfo[] parameters4 = item4.GetParameters(); if (parameters4.Length >= 1) { if (parameters4[0].ParameterType == typeof(string) && parameters4.Length >= 2) { methodInfo4 = item4; break; } if (parameters4[0].ParameterType.IsGenericType && methodInfo4 == null) { methodInfo4 = item4; } } } method2?.Invoke(obj, new object[1] { "Server Notice - Client" }); if (methodInfo != null) { try { methodInfo.Invoke(obj, new object[2] { "Show Notices", AnnouncementConfig.EnableNoticeDisplay }); } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddToggle ShowNotices failed: " + ex.Message)); } try { methodInfo.Invoke(obj, new object[2] { "Play Sound", AnnouncementConfig.PlayNoticeSound }); } catch (Exception ex2) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddToggle PlaySound failed: " + ex2.Message)); } } if (method4 != null) { try { <>c__DisplayClass6_0 CS$<>8__locals0 = new <>c__DisplayClass6_0(); object obj2 = CS$<>8__locals0; ConfigEntry<KeyCode> settingsWindowKey = AnnouncementConfig.SettingsWindowKey; int num; if (settingsWindowKey != null) { obj2 = settingsWindowKey.Value; num = (int)obj2; } else { num = 293; obj2 = num; num = (int)obj2; } KeyCode val = (KeyCode)obj2; ((<>c__DisplayClass6_0)num).currentKey = ((object)(KeyCode)(ref val)).ToString(); method4.Invoke(obj, new object[2] { "Settings Key: " + CS$<>8__locals0.currentKey + " (Change in " + CS$<>8__locals0.currentKey + " window)", (object)(UnityAction)delegate { ShowFeedback("Press " + CS$<>8__locals0.currentKey + " to open settings and change keybind there."); } }); } catch (Exception ex3) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddButton KeyInfo failed: " + ex3.Message)); } } if (method4 != null) { try { object obj3 = obj; object[] obj4 = new object[2] { "Reset Acknowledged Notices", null }; object obj5 = <>c.<>9__6_0; if (obj5 == null) { UnityAction val2 = delegate { AnnouncementManager.Instance.ResetAllAcknowledgments(); ShowFeedback("Notice history cleared!"); }; <>c.<>9__6_0 = val2; obj5 = (object)val2; } obj4[1] = obj5; method4.Invoke(obj3, obj4); } catch (Exception ex4) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddButton Reset failed: " + ex4.Message)); } } method3?.Invoke(obj, null); method2?.Invoke(obj, new object[1] { "Server Notice - Host (Host Only)" }); if (methodInfo != null) { try { methodInfo.Invoke(obj, new object[2] { "Enable System", AnnouncementConfig.EnableNoticeSystem }); } catch (Exception ex5) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddToggle EnableSystem failed: " + ex5.Message)); } } if (methodInfo2 != null) { try { ParameterInfo[] parameters5 = methodInfo2.GetParameters(); if (parameters5.Length == 2) { methodInfo2.Invoke(obj, new object[2] { "Banner Title", AnnouncementConfig.BannerTitle }); } else if (parameters5.Length == 3) { methodInfo2.Invoke(obj, new object[3] { "Banner Title", AnnouncementConfig.BannerTitle, "Title..." }); } } catch (Exception ex6) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddTextField Title failed: " + ex6.Message)); } try { ParameterInfo[] parameters6 = methodInfo2.GetParameters(); object obj6 = null; if (parameters6.Length == 2) { obj6 = methodInfo2.Invoke(obj, new object[2] { "Banner Content", AnnouncementConfig.BannerContent }); } else if (parameters6.Length == 3) { obj6 = methodInfo2.Invoke(obj, new object[3] { "Banner Content", AnnouncementConfig.BannerContent, "Content (use \\n for newlines)..." }); } if (obj6 != null) { ConfigureMultiLineTextField(obj6); } } catch (Exception ex7) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddTextField Content failed: " + ex7.Message)); } } else { ServerAnnouncementsPlugin.Log.LogWarning((object)"AddTextField method not found - skipping text fields"); } if (methodInfo4 != null) { try { ParameterInfo[] parameters7 = methodInfo4.GetParameters(); if (parameters7.Length == 1) { methodInfo4.Invoke(obj, new object[1] { AnnouncementConfig.NoticeVersion }); } else if (parameters7.Length == 2 && parameters7[0].ParameterType == typeof(string)) { methodInfo4.Invoke(obj, new object[2] { "Notice Version", AnnouncementConfig.NoticeVersion }); } } catch (Exception ex8) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddAdvSlider Version failed: " + ex8.Message)); } } method3?.Invoke(obj, null); if (methodInfo != null) { try { methodInfo.Invoke(obj, new object[2] { "Toast on Join", AnnouncementConfig.ShowToastOnJoin }); } catch (Exception ex9) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddToggle ToastOnJoin failed: " + ex9.Message)); } } if (methodInfo2 != null) { try { ParameterInfo[] parameters8 = methodInfo2.GetParameters(); if (parameters8.Length == 2) { methodInfo2.Invoke(obj, new object[2] { "Join Message", AnnouncementConfig.JoinToastMessage }); } else if (parameters8.Length == 3) { methodInfo2.Invoke(obj, new object[3] { "Join Message", AnnouncementConfig.JoinToastMessage, "Message..." }); } } catch (Exception ex10) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddTextField JoinMessage failed: " + ex10.Message)); } } if (methodInfo != null) { try { methodInfo.Invoke(obj, new object[2] { "Vanilla Compat Mode", AnnouncementConfig.VanillaCompatMode }); } catch (Exception ex11) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddToggle VanillaCompat failed: " + ex11.Message)); } } method3?.Invoke(obj, null); method2?.Invoke(obj, new object[1] { "Font Sizes" }); if (methodInfo4 != null) { try { ParameterInfo[] parameters9 = methodInfo4.GetParameters(); if (parameters9.Length >= 2 && parameters9[0].ParameterType == typeof(string)) { methodInfo4.Invoke(obj, new object[2] { "Banner Title Size", AnnouncementConfig.BannerTitleFontSize }); methodInfo4.Invoke(obj, new object[2] { "Banner Content Size", AnnouncementConfig.BannerContentFontSize }); methodInfo4.Invoke(obj, new object[2] { "Toast Size", AnnouncementConfig.ToastFontSize }); } else if (parameters9.Length == 1) { methodInfo4.Invoke(obj, new object[1] { AnnouncementConfig.BannerTitleFontSize }); methodInfo4.Invoke(obj, new object[1] { AnnouncementConfig.BannerContentFontSize }); methodInfo4.Invoke(obj, new object[1] { AnnouncementConfig.ToastFontSize }); } } catch (Exception ex12) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddAdvSlider FontSizes failed: " + ex12.Message)); } } method3?.Invoke(obj, null); if (!(method4 != null)) { return; } try { object obj7 = obj; object[] obj8 = new object[2] { "Send Toast Now", null }; object obj9 = <>c.<>9__6_1; if (obj9 == null) { UnityAction val3 = delegate { if (AnnouncementManager.IsHost()) { AnnouncementCommandHandler.SendToastNow(); ShowFeedback("Toast sent!"); } else { ShowFeedback("Host only!"); } }; <>c.<>9__6_1 = val3; obj9 = (object)val3; } obj8[1] = obj9; method4.Invoke(obj7, obj8); } catch (Exception ex13) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddButton SendToast failed: " + ex13.Message)); } try { object obj10 = obj; object[] obj11 = new object[2] { "Show Banner to All", null }; object obj12 = <>c.<>9__6_2; if (obj12 == null) { UnityAction val4 = delegate { if (AnnouncementManager.IsHost()) { AnnouncementCommandHandler.ShowBannerToAll(); ShowFeedback("Banner shown to all!"); } else { ShowFeedback("Host only!"); } }; <>c.<>9__6_2 = val4; obj12 = (object)val4; } obj11[1] = obj12; method4.Invoke(obj10, obj11); } catch (Exception ex14) { ServerAnnouncementsPlugin.Log.LogWarning((object)("AddButton ShowBanner failed: " + ex14.Message)); } } catch (Exception ex15) { ServerAnnouncementsPlugin.Log.LogError((object)("Failed to create EasySettings UI: " + ex15.Message)); } } private static void OnApplySettings() { try { AnnouncementCommandHandler.SyncSettingsIfHost(); } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogError((object)("Failed to apply settings: " + ex.Message)); } } private static void ConfigureMultiLineTextField(object textFieldElement) { try { PropertyInfo property = textFieldElement.GetType().GetProperty("InputField", BindingFlags.Instance | BindingFlags.Public); if (!(property == null)) { object? value = property.GetValue(textFieldElement); InputField val = (InputField)((value is InputField) ? value : null); if (!((Object)(object)val == (Object)null)) { val.characterLimit = 500; } } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("ConfigureMultiLineTextField failed: " + ex.Message)); } } private static void ShowFeedback(string message) { try { ErrorPromptTextManager val = Object.FindObjectOfType<ErrorPromptTextManager>(); if ((Object)(object)val != (Object)null) { val.Init_ErrorPrompt(message); } } catch { } } } public class AnnouncementBannerUI : MonoBehaviour { private static AnnouncementBannerUI _instance; private GameObject _bannerPanel; private Text _titleText; private Text _contentText; private Button _confirmButton; private Button _dismissButton; private Button _closeXButton; private CanvasGroup _canvasGroup; private AnnouncementData _currentData; private bool _isShowing; public static void Initialize() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("ServerNotice_BannerUI"); Object.DontDestroyOnLoad((Object)(object)val); _instance = val.AddComponent<AnnouncementBannerUI>(); AnnouncementManager.Instance.OnBannerReceived += _instance.ShowBanner; } } private void Awake() { CreateBannerUI(); } private void OnDestroy() { if (AnnouncementManager.Instance != null) { AnnouncementManager.Instance.OnBannerReceived -= ShowBanner; } } private void CreateBannerUI() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) try { Canvas val = CreateDedicatedCanvas(); if ((Object)(object)val == (Object)null) { ServerAnnouncementsPlugin.Log.LogError((object)"Failed to create canvas for Banner UI!"); return; } _bannerPanel = new GameObject("BannerPanel"); _bannerPanel.transform.SetParent(((Component)val).transform, false); RectTransform val2 = _bannerPanel.AddComponent<RectTransform>(); val2.anchorMin = new Vector2(0.5f, 0.5f); val2.anchorMax = new Vector2(0.5f, 0.5f); val2.sizeDelta = new Vector2(550f, 400f); val2.anchoredPosition = Vector2.zero; Image val3 = _bannerPanel.AddComponent<Image>(); ((Graphic)val3).color = new Color(0.1f, 0.1f, 0.15f, 0.98f); _canvasGroup = _bannerPanel.AddComponent<CanvasGroup>(); CreateBannerContent(); _bannerPanel.SetActive(false); } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogError((object)("Failed to create banner UI: " + ex.Message + "\n" + ex.StackTrace)); } } private Canvas CreateDedicatedCanvas() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ServerNotice_BannerCanvas"); Object.DontDestroyOnLoad((Object)(object)val); Canvas val2 = val.AddComponent<Canvas>(); val2.renderMode = (RenderMode)0; val2.sortingOrder = 10000; CanvasScaler val3 = val.AddComponent<CanvasScaler>(); val3.uiScaleMode = (ScaleMode)1; val3.referenceResolution = new Vector2(1920f, 1080f); val3.matchWidthOrHeight = 0.5f; val.AddComponent<GraphicRaycaster>(); return val2; } private void CreateBannerContent() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Header"); val.transform.SetParent(_bannerPanel.transform, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(1f, 1f); val2.sizeDelta = new Vector2(0f, 50f); val2.anchoredPosition = new Vector2(0f, -25f); Image val3 = val.AddComponent<Image>(); ((Graphic)val3).color = new Color(0.15f, 0.15f, 0.25f, 1f); GameObject val4 = new GameObject("Title"); val4.transform.SetParent(val.transform, false); RectTransform val5 = val4.AddComponent<RectTransform>(); val5.anchorMin = Vector2.zero; val5.anchorMax = Vector2.one; val5.offsetMin = new Vector2(15f, 5f); val5.offsetMax = new Vector2(-80f, -5f); _titleText = val4.AddComponent<Text>(); _titleText.fontSize = 26; _titleText.fontStyle = (FontStyle)1; _titleText.alignment = (TextAnchor)3; ((Graphic)_titleText).color = new Color(1f, 0.85f, 0.4f); SetFont(_titleText); CreateCloseButton(val.transform); GameObject val6 = new GameObject("Content"); val6.transform.SetParent(_bannerPanel.transform, false); RectTransform val7 = val6.AddComponent<RectTransform>(); val7.anchorMin = new Vector2(0f, 0f); val7.anchorMax = new Vector2(1f, 1f); val7.offsetMin = new Vector2(20f, 80f); val7.offsetMax = new Vector2(-20f, -60f); _contentText = val6.AddComponent<Text>(); _contentText.fontSize = 18; _contentText.alignment = (TextAnchor)0; ((Graphic)_contentText).color = new Color(0.95f, 0.95f, 0.95f); _contentText.supportRichText = true; _contentText.horizontalOverflow = (HorizontalWrapMode)0; _contentText.verticalOverflow = (VerticalWrapMode)1; SetFont(_contentText); CreateConfirmButton(); } private void CreateCloseButton(Transform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("CloseXButton"); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = new Vector2(1f, 0.5f); val2.anchorMax = new Vector2(1f, 0.5f); val2.sizeDelta = new Vector2(40f, 40f); val2.anchoredPosition = new Vector2(-25f, 0f); Image val3 = val.AddComponent<Image>(); ((Graphic)val3).color = new Color(0.5f, 0.2f, 0.2f); _closeXButton = val.AddComponent<Button>(); ((Selectable)_closeXButton).targetGraphic = (Graphic)(object)val3; ((UnityEvent)_closeXButton.onClick).AddListener(new UnityAction(OnCloseX)); GameObject val4 = new GameObject("XText"); val4.transform.SetParent(val.transform, false); RectTransform val5 = val4.AddComponent<RectTransform>(); val5.anchorMin = Vector2.zero; val5.anchorMax = Vector2.one; Text val6 = val4.AddComponent<Text>(); val6.text = "X"; val6.fontSize = 22; val6.fontStyle = (FontStyle)1; val6.alignment = (TextAnchor)4; ((Graphic)val6).color = Color.white; SetFont(val6); } private void CreateConfirmButton() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Expected O, but got Unknown //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected O, but got Unknown //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Expected O, but got Unknown //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Expected O, but got Unknown //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Expected O, but got Unknown //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ButtonContainer"); val.transform.SetParent(_bannerPanel.transform, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = new Vector2(0f, 0f); val2.anchorMax = new Vector2(1f, 0f); val2.sizeDelta = new Vector2(0f, 60f); val2.anchoredPosition = new Vector2(0f, 35f); GameObject val3 = new GameObject("ConfirmButton"); val3.transform.SetParent(val.transform, false); RectTransform val4 = val3.AddComponent<RectTransform>(); val4.anchorMin = new Vector2(0.25f, 0.5f); val4.anchorMax = new Vector2(0.25f, 0.5f); val4.sizeDelta = new Vector2(140f, 45f); val4.anchoredPosition = Vector2.zero; Image val5 = val3.AddComponent<Image>(); ((Graphic)val5).color = new Color(0.25f, 0.45f, 0.25f); _confirmButton = val3.AddComponent<Button>(); ((Selectable)_confirmButton).targetGraphic = (Graphic)(object)val5; ((UnityEvent)_confirmButton.onClick).AddListener(new UnityAction(OnConfirm)); GameObject val6 = new GameObject("Text"); val6.transform.SetParent(val3.transform, false); RectTransform val7 = val6.AddComponent<RectTransform>(); val7.anchorMin = Vector2.zero; val7.anchorMax = Vector2.one; Text val8 = val6.AddComponent<Text>(); val8.text = "OK"; val8.fontSize = 18; val8.fontStyle = (FontStyle)1; val8.alignment = (TextAnchor)4; ((Graphic)val8).color = Color.white; SetFont(val8); GameObject val9 = new GameObject("DismissButton"); val9.transform.SetParent(val.transform, false); RectTransform val10 = val9.AddComponent<RectTransform>(); val10.anchorMin = new Vector2(0.75f, 0.5f); val10.anchorMax = new Vector2(0.75f, 0.5f); val10.sizeDelta = new Vector2(200f, 45f); val10.anchoredPosition = Vector2.zero; Image val11 = val9.AddComponent<Image>(); ((Graphic)val11).color = new Color(0.4f, 0.35f, 0.2f); _dismissButton = val9.AddComponent<Button>(); ((Selectable)_dismissButton).targetGraphic = (Graphic)(object)val11; ((UnityEvent)_dismissButton.onClick).AddListener(new UnityAction(OnDismiss)); GameObject val12 = new GameObject("Text"); val12.transform.SetParent(val9.transform, false); RectTransform val13 = val12.AddComponent<RectTransform>(); val13.anchorMin = Vector2.zero; val13.anchorMax = Vector2.one; Text val14 = val12.AddComponent<Text>(); val14.text = "Don't show for a while"; val14.fontSize = 14; val14.alignment = (TextAnchor)4; ((Graphic)val14).color = Color.white; SetFont(val14); } private void SetFont(Text text) { try { Text val = Object.FindObjectOfType<Text>(); if ((Object)(object)val != (Object)null && (Object)(object)val.font != (Object)null) { text.font = val.font; return; } text.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf"); if ((Object)(object)text.font == (Object)null) { text.font = Font.CreateDynamicFontFromOSFont("Arial", 20); } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogWarning((object)("SetFont error: " + ex.Message)); } } public void ShowBanner(AnnouncementData data) { if ((Object)(object)_bannerPanel == (Object)null) { ServerAnnouncementsPlugin.Log.LogWarning((object)"Banner UI not initialized!"); } else { if (_isShowing) { return; } try { _currentData = data; _titleText.text = data.Title; string text = data.Content.Replace("\\n", "\n"); if (!string.IsNullOrEmpty(data.ExtraContent)) { int num = ((data.ExtraContentFontSize > 0) ? data.ExtraContentFontSize : 14); string arg = data.ExtraContent.Replace("\\n", "\n"); text += $"\n\n<size={num}>{arg}</size>"; } _contentText.text = text; _titleText.fontSize = ((data.TitleFontSize > 0) ? data.TitleFontSize : 24); _contentText.fontSize = ((data.ContentFontSize > 0) ? data.ContentFontSize : 16); _bannerPanel.SetActive(true); _canvasGroup.alpha = 1f; _isShowing = true; if (AnnouncementConfig.PlayNoticeSound.Value) { PlayNotificationSound(); } } catch (Exception ex) { ServerAnnouncementsPlugin.Log.LogError((object)("Failed to show banner: " + ex.Message)); } } } private void OnConfirm() { HideBanner(); } private void OnDismiss() { AnnouncementManager.Instance.DismissBanner(_currentData.Version); HideBanner(); AnnouncementManager.Instance.HandleToastReceived("Notice hidden for 24 hours or until content changes.", 3f, 16); } private void OnCloseX() { OnConfirm(); } private void HideBanner() { if ((Object)(object)_bannerPanel != (Object)null) { _bannerPanel.SetActive(false); } _isShowing = false; } private void PlayNotificationSound() { } public static void ForceShow() { if (!((Object)(object)_instance == (Object)null)) { AnnouncementData data = new AnnouncementData(AnnouncementConfig.BannerTitle.Value, AnnouncementConfig.BannerContent.Value, AnnouncementConfig.NoticeVersion.Value); _instance.ShowBanner(data); } } } public class AnnouncementSettingsWindow : MonoBehaviour { private static AnnouncementSettingsWindow _instance; private bool _isVisible = false; private Rect _windowRect = new Rect(100f, 100f, 500f, 750f); private Vector2 _scrollPosition = Vector2.zero; private string _tempBannerTitle; private string _tempBannerContent; private string _tempBannerExtraContent; private string _tempJoinMessage; private string _tempToastDuration; private string _tempNoticeVersion; private float _tempBannerTitleFontSize; private float _tempBannerContentFontSize; private float _tempBannerExtraContentFontSize; private float _tempToastFontSize; private bool _waitingForKey = false; private KeyCode _tempSettingsKey; private GUIStyle _windowStyle; private GUIStyle _headerStyle; private GUIStyle _labelStyle; private GUIStyle _textFieldStyle; private GUIStyle _textAreaStyle; private GUIStyle _buttonStyle; private GUIStyle _toggleStyle; private bool _stylesInitialized = false; public static void Initialize() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_instance