Decompiled source of ServerAnnouncements v1.1.0

plugins/ServerAnnouncements/ServerAnnouncements.dll

Decompiled 2 weeks ago
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.LogDebug((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())
			{
			}
		}
	}
	[Serializable]
	public class ModeratorSharedContent
	{
		public string ToastMessage = "Moderator Announcement";

		public float ToastDuration = 5f;

		public int ToastFontSize = 20;

		public string BannerTitle = "Moderator Notice";

		public string BannerHighlight = "";

		public string BannerContent = "";

		public string BannerExtraContent = "";

		public int TitleFontSize = 24;

		public int HighlightFontSize = 18;

		public int ContentFontSize = 16;

		public int ExtraFontSize = 14;

		public int SoundType = 0;

		public float SoundVolume = 0.5f;

		public bool ScheduleEnabled = false;

		public float ScheduleInterval = 300f;

		public bool ScheduleToast = true;

		public bool ScheduleBanner = false;

		public string LastEditedBy = "";

		public long LastEditedTimestamp = 0L;

		public DateTime LastEditedAt => DateTimeOffset.FromUnixTimeSeconds(LastEditedTimestamp).LocalDateTime;

		public void SetLastEdited(string steamId)
		{
			LastEditedBy = steamId ?? "";
			LastEditedTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
		}

		public string Serialize()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(Escape(ToastMessage));
			stringBuilder.Append("|");
			stringBuilder.Append(ToastDuration.ToString("F2"));
			stringBuilder.Append("|");
			stringBuilder.Append(ToastFontSize);
			stringBuilder.Append("|");
			stringBuilder.Append(Escape(BannerTitle));
			stringBuilder.Append("|");
			stringBuilder.Append(Escape(BannerHighlight));
			stringBuilder.Append("|");
			stringBuilder.Append(Escape(BannerContent));
			stringBuilder.Append("|");
			stringBuilder.Append(Escape(BannerExtraContent));
			stringBuilder.Append("|");
			stringBuilder.Append(TitleFontSize);
			stringBuilder.Append("|");
			stringBuilder.Append(HighlightFontSize);
			stringBuilder.Append("|");
			stringBuilder.Append(ContentFontSize);
			stringBuilder.Append("|");
			stringBuilder.Append(ExtraFontSize);
			stringBuilder.Append("|");
			stringBuilder.Append(SoundType);
			stringBuilder.Append("|");
			stringBuilder.Append(SoundVolume.ToString("F2"));
			stringBuilder.Append("|");
			stringBuilder.Append(Escape(LastEditedBy));
			stringBuilder.Append("|");
			stringBuilder.Append(LastEditedTimestamp);
			stringBuilder.Append("|");
			stringBuilder.Append(ScheduleEnabled ? "1" : "0");
			stringBuilder.Append("|");
			stringBuilder.Append(ScheduleInterval.ToString("F1"));
			stringBuilder.Append("|");
			stringBuilder.Append(ScheduleToast ? "1" : "0");
			stringBuilder.Append("|");
			stringBuilder.Append(ScheduleBanner ? "1" : "0");
			return stringBuilder.ToString();
			static string Escape(string s)
			{
				return (s ?? "").Replace("|", "\\|").Replace("\n", "\\n");
			}
		}

		public static ModeratorSharedContent Deserialize(string data)
		{
			ModeratorSharedContent moderatorSharedContent = new ModeratorSharedContent();
			if (string.IsNullOrEmpty(data))
			{
				return moderatorSharedContent;
			}
			try
			{
				string[] array = SplitByUnescapedPipe(data);
				if (array.Length >= 15)
				{
					moderatorSharedContent.ToastMessage = Unescape(array[0]);
					float.TryParse(array[1], out moderatorSharedContent.ToastDuration);
					int.TryParse(array[2], out moderatorSharedContent.ToastFontSize);
					moderatorSharedContent.BannerTitle = Unescape(array[3]);
					moderatorSharedContent.BannerHighlight = Unescape(array[4]);
					moderatorSharedContent.BannerContent = Unescape(array[5]);
					moderatorSharedContent.BannerExtraContent = Unescape(array[6]);
					int.TryParse(array[7], out moderatorSharedContent.TitleFontSize);
					int.TryParse(array[8], out moderatorSharedContent.HighlightFontSize);
					int.TryParse(array[9], out moderatorSharedContent.ContentFontSize);
					int.TryParse(array[10], out moderatorSharedContent.ExtraFontSize);
					int.TryParse(array[11], out moderatorSharedContent.SoundType);
					float.TryParse(array[12], out moderatorSharedContent.SoundVolume);
					moderatorSharedContent.LastEditedBy = Unescape(array[13]);
					long.TryParse(array[14], out moderatorSharedContent.LastEditedTimestamp);
				}
				if (array.Length >= 19)
				{
					moderatorSharedContent.ScheduleEnabled = array[15] == "1";
					float.TryParse(array[16], out moderatorSharedContent.ScheduleInterval);
					moderatorSharedContent.ScheduleToast = array[17] == "1";
					moderatorSharedContent.ScheduleBanner = array[18] == "1";
				}
			}
			catch (Exception ex)
			{
				ServerAnnouncementsPlugin.Log.LogWarning((object)("[ModeratorSharedContent] Deserialize error: " + ex.Message));
			}
			return moderatorSharedContent;
			static string Unescape(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 void CopyFrom(ModeratorSharedContent other)
		{
			if (other != null)
			{
				ToastMessage = other.ToastMessage;
				ToastDuration = other.ToastDuration;
				ToastFontSize = other.ToastFontSize;
				BannerTitle = other.BannerTitle;
				BannerHighlight = other.BannerHighlight;
				BannerContent = other.BannerContent;
				BannerExtraContent = other.BannerExtraContent;
				TitleFontSize = other.TitleFontSize;
				HighlightFontSize = other.HighlightFontSize;
				ContentFontSize = other.ContentFontSize;
				ExtraFontSize = other.ExtraFontSize;
				SoundType = other.SoundType;
				SoundVolume = other.SoundVolume;
				LastEditedBy = other.LastEditedBy;
				LastEditedTimestamp = other.LastEditedTimestamp;
				ScheduleEnabled = other.ScheduleEnabled;
				ScheduleInterval = other.ScheduleInterval;
				ScheduleToast = other.ScheduleToast;
				ScheduleBanner = other.ScheduleBanner;
			}
		}
	}
	public static class ModeratorContentManager
	{
		private static ModeratorSharedContent _sharedContent = new ModeratorSharedContent();

		private static string _saveFilePath = null;

		public static ModeratorSharedContent SharedContent => _sharedContent;

		public static void Initialize()
		{
			_saveFilePath = Path.Combine(Paths.ConfigPath, "ServerAnnouncements_ModeratorContent.json");
			LoadFromFile();
		}

		public static void OnServerStarted()
		{
			if (AnnouncementManager.IsHost() && _sharedContent.ScheduleEnabled)
			{
				AnnouncementRouter.Instance.StartSharedScheduler();
			}
		}

		public static void UpdateContent(ModeratorSharedContent content, string editorSteamId, bool syncToOthers = true)
		{
			if (content == null)
			{
				return;
			}
			_sharedContent.CopyFrom(content);
			_sharedContent.SetLastEdited(editorSteamId);
			if (AnnouncementManager.IsHost())
			{
				SaveToFile();
				AnnouncementRouter.Instance.UpdateSharedSchedulerState();
				if (syncToOthers)
				{
					BroadcastContentSync();
				}
			}
		}

		public static void UpdateFromSync(string serializedData)
		{
			ModeratorSharedContent other = ModeratorSharedContent.Deserialize(serializedData);
			_sharedContent.CopyFrom(other);
		}

		public static void BroadcastContentSync()
		{
			if (AnnouncementManager.IsHost())
			{
				string serializedContent = _sharedContent.Serialize();
				CodeTalkerIntegration.BroadcastModeratorContentSync(serializedContent);
			}
		}

		public static void SendContentUpdateToHost()
		{
			if (AnnouncementManager.IsHost())
			{
				_sharedContent.SetLastEdited(PermissionHelper.GetLocalSteamId());
				SaveToFile();
				BroadcastContentSync();
			}
			else
			{
				string serializedContent = _sharedContent.Serialize();
				CodeTalkerIntegration.SendModeratorContentUpdate(serializedContent);
			}
		}

		private static void SaveToFile()
		{
			if (string.IsNullOrEmpty(_saveFilePath))
			{
				return;
			}
			try
			{
				string contents = _sharedContent.Serialize();
				File.WriteAllText(_saveFilePath, contents);
				ServerAnnouncementsPlugin.Log.LogDebug((object)("[ModeratorContent] Saved to " + _saveFilePath));
			}
			catch (Exception ex)
			{
				ServerAnnouncementsPlugin.Log.LogWarning((object)("[ModeratorContent] Save error: " + ex.Message));
			}
		}

		private static void LoadFromFile()
		{
			if (string.IsNullOrEmpty(_saveFilePath) || !File.Exists(_saveFilePath))
			{
				return;
			}
			try
			{
				string data = File.ReadAllText(_saveFilePath);
				ModeratorSharedContent moderatorSharedContent = ModeratorSharedContent.Deserialize(data);
				if (moderatorSharedContent != null)
				{
					_sharedContent.CopyFrom(moderatorSharedContent);
					ServerAnnouncementsPlugin.Log.LogInfo((object)("[ModeratorContent] Loaded from file. Last edited by " + _sharedContent.LastEditedBy));
				}
			}
			catch (Exception ex)
			{
				ServerAnnouncementsPlugin.Log.LogWarning((object)("[ModeratorContent] Load error: " + ex.Message));
			}
		}

		public static void Clear()
		{
			_sharedContent = new ModeratorSharedContent();
		}
	}
	public static class AnnouncementConfig
	{
		public static ConfigEntry<bool> EnableNoticeSystem;

		public static ConfigEntry<string> BannerTitle;

		public static ConfigEntry<string> BannerHighlight;

		public static ConfigEntry<string> BannerContent;

		public static ConfigEntry<string> BannerExtraContent;

		public static ConfigEntry<int> NoticeVersion;

		public static ConfigEntry<bool> ShowToastOnJoin;

		public static ConfigEntry<bool> ShowBannerOnJoin;

		public static ConfigEntry<bool> ShowToastOnJoinSelf;

		public static ConfigEntry<bool> ShowBannerOnJoinSelf;

		public static ConfigEntry<string> JoinToastMessage;

		public static ConfigEntry<float> ToastDuration;

		public static ConfigEntry<bool> VanillaCompatMode;

		public static ConfigEntry<string> ModeratorSteamIds;

		public static ConfigEntry<float> BroadcastCooldown;

		public static ConfigEntry<bool> EnableScheduledAnnouncement;

		public static ConfigEntry<float> ScheduledAnnouncementInterval;

		public static ConfigEntry<bool> ScheduledAnnouncementToast;

		public static ConfigEntry<bool> ScheduledAnnouncementBanner;

		public static ConfigEntry<bool> ScheduledAnnouncementIncludeHost;

		public static ConfigEntry<bool> ScheduledAnnouncementPlaySound;

		public static ConfigEntry<int> ScheduledSoundIndex;

		public static ConfigEntry<float> ScheduledSoundVolume;

		public static ConfigEntry<bool> EnableNoticeDisplay;

		public static ConfigEntry<bool> PlayNoticeSound;

		public static ConfigEntry<int> NotificationSoundIndex;

		public static ConfigEntry<float> NotificationSoundVolume;

		public static ConfigEntry<bool> IgnoreNormalAnnouncementSound;

		public static ConfigEntry<bool> IgnoreScheduledAnnouncementSound;

		public static ConfigEntry<string> EasySettingsTab;

		public static ConfigEntry<KeyCode> SettingsWindowKey;

		public static ConfigEntry<int> BannerTitleFontSize;

		public static ConfigEntry<int> BannerHighlightFontSize;

		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_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Expected O, but got Unknown
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Expected O, but got Unknown
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Expected O, but got Unknown
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Expected O, but got Unknown
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Expected O, but got Unknown
			//IL_03e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f2: Expected O, but got Unknown
			//IL_0455: Unknown result type (might be due to invalid IL or missing references)
			//IL_045f: Expected O, but got Unknown
			//IL_0484: Unknown result type (might be due to invalid IL or missing references)
			//IL_048e: Expected O, but got Unknown
			//IL_04b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bd: Expected O, but got Unknown
			//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ec: Expected O, but got Unknown
			//IL_0511: Unknown result type (might be due to invalid IL or missing references)
			//IL_051b: 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");
			BannerHighlight = config.Bind<string>("Host", "BannerHighlight", "", "Highlighted content displayed before main content (visible to vanilla clients)");
			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 the server");
			ShowBannerOnJoin = config.Bind<bool>("Host", "ShowBannerOnJoin", true, "Show the notice banner when players join the server");
			ShowToastOnJoinSelf = config.Bind<bool>("Host", "ShowToastOnJoinSelf", true, "Show a toast message to the host when hosting starts");
			ShowBannerOnJoinSelf = config.Bind<bool>("Host", "ShowBannerOnJoinSelf", true, "Show the notice banner to the host when hosting starts");
			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");
			ModeratorSteamIds = config.Bind<string>("Host", "ModeratorSteamIds", "", "Comma-separated list of Steam IDs that have moderator permissions (can send announcements). Example: 76561198012345678,76561198087654321");
			BroadcastCooldown = config.Bind<float>("Host", "BroadcastCooldown", 3f, new ConfigDescription("Minimum seconds between manual broadcasts (prevents spam)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			EnableScheduledAnnouncement = config.Bind<bool>("Host", "EnableScheduledAnnouncement", false, "Enable automatic scheduled announcements");
			ScheduledAnnouncementInterval = config.Bind<float>("Host", "ScheduledAnnouncementInterval", 300f, new ConfigDescription("Interval between scheduled announcements (seconds). Min 5s for testing, Max 24 hours.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 86400f), Array.Empty<object>()));
			ScheduledAnnouncementToast = config.Bind<bool>("Host", "ScheduledAnnouncementToast", true, "Send toast message in scheduled announcements");
			ScheduledAnnouncementBanner = config.Bind<bool>("Host", "ScheduledAnnouncementBanner", false, "Send banner in scheduled announcements");
			ScheduledAnnouncementIncludeHost = config.Bind<bool>("Host", "ScheduledAnnouncementIncludeHost", false, "Include host in scheduled announcements (if false, only clients receive)");
			ScheduledAnnouncementPlaySound = config.Bind<bool>("Host", "ScheduledAnnouncementPlaySound", false, "Play notification sound for scheduled announcements (separate from normal notice sound setting)");
			ScheduledSoundIndex = config.Bind<int>("Host", "ScheduledSoundIndex", 0, new ConfigDescription("Sound type for scheduled announcements", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 7), Array.Empty<object>()));
			ScheduledSoundVolume = config.Bind<float>("Host", "ScheduledSoundVolume", 0.5f, new ConfigDescription("Volume for scheduled announcement sounds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			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");
			IgnoreNormalAnnouncementSound = config.Bind<bool>("Client", "IgnoreNormalAnnouncementSound", false, "Ignore sounds from normal (non-scheduled) announcements sent by host");
			IgnoreScheduledAnnouncementSound = config.Bind<bool>("Client", "IgnoreScheduledAnnouncementSound", false, "Ignore sounds from scheduled announcements sent by host");
			NotificationSoundIndex = config.Bind<int>("Client", "NotificationSoundIndex", 0, new ConfigDescription("Index of the sound to play for notifications (use Sound Test in settings to find)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			NotificationSoundVolume = config.Bind<float>("Client", "NotificationSoundVolume", 0.5f, new ConfigDescription("Volume of notification sounds", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			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)291, "Key to open/close the settings window. Default: F10");
			BannerTitleFontSize = config.Bind<int>("UI", "BannerTitleFontSize", 24, new ConfigDescription("Font size for banner title", (AcceptableValueBase)(object)new AcceptableValueRange<int>(12, 48), Array.Empty<object>()));
			BannerHighlightFontSize = config.Bind<int>("UI", "BannerHighlightFontSize", 18, new ConfigDescription("Font size for banner highlight content", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 36), 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;

		private bool? _lastBannerSoundOverride = null;

		private bool? _lastToastSoundOverride = null;

		private int _lastSoundType = 0;

		private float _lastSoundVolume = 0.5f;

		private bool _lastIsFromNetwork = false;

		private bool _lastIsScheduled = 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, bool? playSound = null, int soundType = -1, float soundVolume = -1f)
		{
			if (AnnouncementConfig.EnableNoticeDisplay.Value && (forceShow || !_receivedBannerThisSession) && (forceShow || !IsBannerDismissed(data.Version)))
			{
				_lastBannerSoundOverride = playSound;
				_lastSoundType = ((soundType >= 0) ? soundType : (AnnouncementConfig.NotificationSoundIndex?.Value ?? 0));
				_lastSoundVolume = ((soundVolume >= 0f) ? soundVolume : (AnnouncementConfig.NotificationSoundVolume?.Value ?? 0.5f));
				_lastIsFromNetwork = soundType >= 0;
				_lastIsScheduled = false;
				_receivedBannerThisSession = true;
				this.OnBannerReceived?.Invoke(data);
			}
		}

		public void HandleToastReceived(string message, float duration, int fontSize = 20, bool forceShow = false, bool? playSound = null, int soundType = -1, float soundVolume = -1f)
		{
			if (AnnouncementConfig.EnableNoticeDisplay.Value && (forceShow || !_receivedToastThisSession))
			{
				_lastToastSoundOverride = playSound;
				_lastSoundType = ((soundType >= 0) ? soundType : (AnnouncementConfig.NotificationSoundIndex?.Value ?? 0));
				_lastSoundVolume = ((soundVolume >= 0f) ? soundVolume : (AnnouncementConfig.NotificationSoundVolume?.Value ?? 0.5f));
				_lastIsFromNetwork = soundType >= 0;
				_lastIsScheduled = false;
				_receivedToastThisSession = true;
				this.OnToastReceived?.Invoke(message, duration, fontSize);
			}
		}

		public void SetLastIsScheduled(bool isScheduled)
		{
			_lastIsScheduled = isScheduled;
		}

		public bool ShouldPlayBannerSound()
		{
			if (_lastBannerSoundOverride.HasValue)
			{
				return _lastBannerSoundOverride.Value;
			}
			if (_lastIsFromNetwork && !IsHost())
			{
				if (_lastIsScheduled)
				{
					ConfigEntry<bool> ignoreScheduledAnnouncementSound = AnnouncementConfig.IgnoreScheduledAnnouncementSound;
					if (ignoreScheduledAnnouncementSound != null && ignoreScheduledAnnouncementSound.Value)
					{
						return false;
					}
				}
				if (!_lastIsScheduled)
				{
					ConfigEntry<bool> ignoreNormalAnnouncementSound = AnnouncementConfig.IgnoreNormalAnnouncementSound;
					if (ignoreNormalAnnouncementSound != null && ignoreNormalAnnouncementSound.Value)
					{
						return false;
					}
				}
			}
			return AnnouncementConfig.PlayNoticeSound?.Value ?? true;
		}

		public bool ShouldPlayToastSound()
		{
			if (_lastToastSoundOverride.HasValue)
			{
				return _lastToastSoundOverride.Value;
			}
			if (_lastIsFromNetwork && !IsHost())
			{
				if (_lastIsScheduled)
				{
					ConfigEntry<bool> ignoreScheduledAnnouncementSound = AnnouncementConfig.IgnoreScheduledAnnouncementSound;
					if (ignoreScheduledAnnouncementSound != null && ignoreScheduledAnnouncementSound.Value)
					{
						return false;
					}
				}
				if (!_lastIsScheduled)
				{
					ConfigEntry<bool> ignoreNormalAnnouncementSound = AnnouncementConfig.IgnoreNormalAnnouncementSound;
					if (ignoreNormalAnnouncementSound != null && ignoreNormalAnnouncementSound.Value)
					{
						return false;
					}
				}
			}
			return AnnouncementConfig.PlayNoticeSound?.Value ?? true;
		}

		public int GetLastSoundType()
		{
			return _lastSoundType;
		}

		public float GetLastSoundVolume()
		{
			return _lastSoundVolume;
		}

		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 Highlight;

		public string Content;

		public string ExtraContent;

		public int Version;

		public string ServerName;

		public int TitleFontSize;

		public int HighlightFontSize;

		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, string highlight = "", int highlightFontSize = 18)
		{
			Title = title;
			Highlight = highlight;
			Content = content;
			ExtraContent = extraContent;
			Version = version;
			ServerName = serverName;
			TitleFontSize = titleFontSize;
			HighlightFontSize = highlightFontSize;
			ContentFontSize = contentFontSize;
			ExtraContentFontSize = extraContentFontSize;
		}
	}
	public enum PermissionLevel
	{
		Client,
		Moderator,
		Host
	}
	public static class PermissionHelper
	{
		private static HashSet<string> _moderatorCache = null;

		private static string _lastModeratorConfig = null;

		private static HashSet<string> _syncedModeratorList = new HashSet<string>();

		private static bool _hasSyncedList = false;

		public static bool HasSyncedList => _hasSyncedList;

		public static PermissionLevel GetCurrentPermission()
		{
			if (AnnouncementManager.IsHost())
			{
				return PermissionLevel.Host;
			}
			string localSteamId = GetLocalSteamId();
			if (!string.IsNullOrEmpty(localSteamId) && IsModerator(localSteamId))
			{
				return PermissionLevel.Moderator;
			}
			return PermissionLevel.Client;
		}

		public static bool IsModerator(string steamId)
		{
			if (string.IsNullOrEmpty(steamId))
			{
				return false;
			}
			if (AnnouncementManager.IsHost())
			{
				RefreshCacheIfNeeded();
				return _moderatorCache.Contains(steamId.Trim());
			}
			return _syncedModeratorList.Contains(steamId.Trim());
		}

		public static void UpdateSyncedModeratorList(IEnumerable<string> moderatorIds)
		{
			_syncedModeratorList.Clear();
			if (moderatorIds != null)
			{
				foreach (string moderatorId in moderatorIds)
				{
					if (!string.IsNullOrEmpty(moderatorId))
					{
						_syncedModeratorList.Add(moderatorId.Trim());
					}
				}
			}
			_hasSyncedList = true;
		}

		public static void ClearSyncedModeratorList()
		{
			_syncedModeratorList.Clear();
			_hasSyncedList = false;
		}

		public static bool CanSendAnnouncements()
		{
			return GetCurrentPermission() >= PermissionLevel.Moderator;
		}

		public static bool CanDelegatePermissions()
		{
			return GetCurrentPermission() >= PermissionLevel.Host;
		}

		public static string GetLocalSteamId()
		{
			try
			{
				Player mainPlayer = Player._mainPlayer;
				if ((Object)(object)mainPlayer != (Object)null)
				{
					return mainPlayer._steamID ?? "";
				}
			}
			catch (Exception ex)
			{
				ServerAnnouncementsPlugin.Log.LogDebug((object)("[PermissionHelper] Failed to get local Steam ID: " + ex.Message));
			}
			return "";
		}

		public static List<string> GetModeratorList()
		{
			RefreshCacheIfNeeded();
			return _moderatorCache.ToList();
		}

		public static bool AddModerator(string steamId)
		{
			if (string.IsNullOrEmpty(steamId))
			{
				return false;
			}
			steamId = steamId.Trim();
			if (!IsValidSteamId(steamId))
			{
				ServerAnnouncementsPlugin.Log.LogWarning((object)("[PermissionHelper] Invalid Steam ID format: " + steamId));
				return false;
			}
			RefreshCacheIfNeeded();
			if (_moderatorCache.Contains(steamId))
			{
				return false;
			}
			_moderatorCache.Add(steamId);
			SaveModeratorList();
			ServerAnnouncementsPlugin.Log.LogInfo((object)("[PermissionHelper] Added moderator: " + steamId));
			return true;
		}

		public static bool RemoveModerator(string steamId)
		{
			if (string.IsNullOrEmpty(steamId))
			{
				return false;
			}
			steamId = steamId.Trim();
			RefreshCacheIfNeeded();
			if (!_moderatorCache.Contains(steamId))
			{
				return false;
			}
			_moderatorCache.Remove(steamId);
			SaveModeratorList();
			ServerAnnouncementsPlugin.Log.LogInfo((object)("[PermissionHelper] Removed moderator: " + steamId));
			return true;
		}

		public static bool IsValidSteamId(string steamId)
		{
			if (string.IsNullOrEmpty(steamId))
			{
				return false;
			}
			steamId = steamId.Trim();
			if (steamId.Length != 17)
			{
				return false;
			}
			if (!steamId.StartsWith("7656"))
			{
				return false;
			}
			return steamId.All(char.IsDigit);
		}

		public static string GetPermissionName(PermissionLevel level)
		{
			return level switch
			{
				PermissionLevel.Host => "Host", 
				PermissionLevel.Moderator => "Moderator", 
				_ => "Client", 
			};
		}

		private static void RefreshCacheIfNeeded()
		{
			string text = AnnouncementConfig.ModeratorSteamIds?.Value ?? "";
			if (_moderatorCache != null && !(_lastModeratorConfig != text))
			{
				return;
			}
			_moderatorCache = new HashSet<string>();
			_lastModeratorConfig = text;
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			string[] array2 = array;
			foreach (string text2 in array2)
			{
				string text3 = text2.Trim();
				if (!string.IsNullOrEmpty(text3))
				{
					_moderatorCache.Add(text3);
				}
			}
		}

		private static void SaveModeratorList()
		{
			if (AnnouncementConfig.ModeratorSteamIds != null)
			{
				string text = string.Join(",", _moderatorCache);
				AnnouncementConfig.ModeratorSteamIds.Value = text;
				_lastModeratorConfig = text;
			}
			BroadcastModeratorSync();
		}

		public static void BroadcastModeratorSync()
		{
			if (AnnouncementManager.IsHost())
			{
				RefreshCacheIfNeeded();
				string moderatorList = string.Join(",", _moderatorCache);
				CodeTalkerIntegration.BroadcastPermissionSync(moderatorList);
			}
		}

		public static void ClearCache()
		{
			_moderatorCache = null;
			_lastModeratorConfig = null;
			_syncedModeratorList.Clear();
			_hasSyncedList = false;
		}
	}
	public enum NotificationSoundType
	{
		None,
		Slap,
		LowHealthWarning,
		LexiconBell,
		PartyInvite,
		TriggerMessage,
		Lockout,
		ErrorPrompt
	}
	public static class SoundHelper
	{
		private static AudioSource _cachedAudioSource;

		private static Dictionary<NotificationSoundType, AudioClip> _soundClips = new Dictionary<NotificationSoundType, AudioClip>();

		private static bool _initialized = false;

		private static List<NotificationSoundType> _availableSounds = new List<NotificationSoundType>();

		private static string[] _availableSoundNames = null;

		private static readonly Dictionary<NotificationSoundType, string[]> SoundFieldNames = new Dictionary<NotificationSoundType, string[]>
		{
			{
				NotificationSoundType.Slap,
				new string[1] { "_slap" }
			},
			{
				NotificationSoundType.LowHealthWarning,
				new string[1] { "_lowHealthWarning" }
			},
			{
				NotificationSoundType.LexiconBell,
				new string[1] { "_lexiconBell" }
			},
			{
				NotificationSoundType.PartyInvite,
				new string[1] { "_partyInviteSfx" }
			},
			{
				NotificationSoundType.TriggerMessage,
				new string[1] { "_triggerMessageTone" }
			},
			{
				NotificationSoundType.Lockout,
				new string[2] { "lockout", "_lockout" }
			},
			{
				NotificationSoundType.ErrorPrompt,
				new string[3] { "ui_errorPrompt", "_errorPrompt", "_ui_errorPrompt" }
			}
		};

		private static readonly Dictionary<NotificationSoundType, string> SoundDisplayNames = new Dictionary<NotificationSoundType, string>
		{
			{
				NotificationSoundType.None,
				"None (Silent)"
			},
			{
				NotificationSoundType.Slap,
				"Slap"
			},
			{
				NotificationSoundType.LowHealthWarning,
				"Low Health Warning"
			},
			{
				NotificationSoundType.LexiconBell,
				"Lexicon Bell"
			},
			{
				NotificationSoundType.PartyInvite,
				"Party Invite"
			},
			{
				NotificationSoundType.TriggerMessage,
				"Trigger Message"
			},
			{
				NotificationSoundType.Lockout,
				"Lockout"
			},
			{
				NotificationSoundType.ErrorPrompt,
				"Error Prompt"
			}
		};

		public static int AvailableSoundCount => _availableSounds?.Count ?? 1;

		public static int SoundCount => Enum.GetValues(typeof(NotificationSoundType)).Length;

		public static List<NotificationSoundType> GetAvailableSounds()
		{
			EnsureInitialized();
			return _availableSounds;
		}

		public static string[] GetAvailableSoundNames()
		{
			EnsureInitialized();
			if (_availableSoundNames == null)
			{
				List<string> list = new List<string>();
				foreach (NotificationSoundType availableSound in _availableSounds)
				{
					list.Add(SoundDisplayNames.TryGetValue(availableSound, out var value) ? value : availableSound.ToString());
				}
				_availableSoundNames = list.ToArray();
			}
			return _availableSoundNames;
		}

		public static NotificationSoundType GetSoundTypeByAvailableIndex(int availableIndex)
		{
			EnsureInitialized();
			if (availableIndex >= 0 && availableIndex < _availableSounds.Count)
			{
				return _availableSounds[availableIndex];
			}
			return NotificationSoundType.None;
		}

		public static int GetAvailableIndexForSoundType(NotificationSoundType soundType)
		{
			EnsureInitialized();
			return _availableSounds.IndexOf(soundType);
		}

		public static void PlaySound(NotificationSoundType soundType, float volume = 0.5f)
		{
			if (soundType == NotificationSoundType.None)
			{
				return;
			}
			EnsureInitialized();
			if (!_soundClips.TryGetValue(soundType, out var value) || (Object)(object)value == (Object)null)
			{
				FindSoundClip(soundType);
				_soundClips.TryGetValue(soundType, out value);
			}
			if ((Object)(object)value == (Object)null)
			{
				ServerAnnouncementsPlugin.Log.LogDebug((object)$"[SoundHelper] Sound clip not found for {soundType}");
				return;
			}
			AudioSource audioSource = GetAudioSource();
			if ((Object)(object)audioSource != (Object)null)
			{
				audioSource.PlayOneShot(value, volume);
			}
		}

		public static void PlayNotificationSound()
		{
			int soundType = AnnouncementConfig.NotificationSoundIndex?.Value ?? 0;
			float volume = AnnouncementConfig.NotificationSoundVolume?.Value ?? 0.5f;
			PlaySound((NotificationSoundType)soundType, volume);
		}

		public static void PlaySoundByIndex(int index, float volume)
		{
			if (index >= 0 && index < SoundCount)
			{
				PlaySound((NotificationSoundType)index, volume);
			}
		}

		public static void RefreshSounds()
		{
			_initialized = false;
			_soundClips.Clear();
			_availableSounds.Clear();
			_availableSoundNames = null;
			EnsureInitialized();
		}

		private static void EnsureInitialized()
		{
			if (_initialized)
			{
				return;
			}
			_initialized = true;
			_availableSounds.Clear();
			_availableSoundNames = null;
			_availableSounds.Add(NotificationSoundType.None);
			try
			{
				foreach (NotificationSoundType value in Enum.GetValues(typeof(NotificationSoundType)))
				{
					if (value != 0)
					{
						FindSoundClip(value);
						if (_soundClips.ContainsKey(value) && (Object)(object)_soundClips[value] != (Object)null)
						{
							_availableSounds.Add(value);
						}
					}
				}
				ServerAnnouncementsPlugin.Log.LogDebug((object)$"[SoundHelper] Initialized. Found {_soundClips.Count} sounds.");
			}
			catch (Exception ex)
			{
				ServerAnnouncementsPlugin.Log.LogWarning((object)("[SoundHelper] Init error: " + ex.Message));
			}
		}

		private static void FindSoundClip(NotificationSoundType soundType)
		{
			if (!SoundFieldNames.TryGetValue(soundType, out var value))
			{
				return;
			}
			MonoBehaviour[] array = Object.FindObjectsOfType<MonoBehaviour>();
			MonoBehaviour[] array2 = array;
			foreach (MonoBehaviour val in array2)
			{
				Type type = ((object)val).GetType();
				string[] array3 = value;
				foreach (string name in array3)
				{
					FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null && field.FieldType == typeof(AudioClip))
					{
						object? value2 = field.GetValue(val);
						AudioClip val2 = (AudioClip)((value2 is AudioClip) ? value2 : null);
						if ((Object)(object)val2 != (Object)null)
						{
							_soundClips[soundType] = val2;
							return;
						}
					}
				}
			}
		}

		private static AudioSource GetAudioSource()
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			if ((Object)(object)_cachedAudioSource != (Object)null && (Object)(object)((Component)_cachedAudioSource).gameObject != (Object)null)
			{
				return _cachedAudioSource;
			}
			ActionBarManager val = Object.FindObjectOfType<ActionBarManager>();
			if ((Object)(object)val != (Object)null)
			{
				FieldInfo field = ((object)val).GetType().GetField("_aSrc", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null)
				{
					object? value = field.GetValue(val);
					_cachedAudioSource = (AudioSource)((value is AudioSource) ? value : null);
					if ((Object)(object)_cachedAudioSource != (Object)null)
					{
						return _cachedAudioSource;
					}
				}
			}
			AudioSource[] array = Object.FindObjectsOfType<AudioSource>();
			if (array.Length != 0)
			{
				_cachedAudioSource = array[0];
				return _cachedAudioSource;
			}
			GameObject val2 = new GameObject("ServerNotice_AudioSource");
			Object.DontDestroyOnLoad((Object)(object)val2);
			_cachedAudioSource = val2.AddComponent<AudioSource>();
			return _cachedAudioSource;
		}

		public static void ClearCache()
		{
			_cachedAudioSource = null;
			_initialized = false;
			_soundClips.Clear();
			_availableSounds.Clear();
			_availableSoundNames = null;
		}
	}
	public class AnnouncementRouter
	{
		[CompilerGenerated]
		private sealed class <HandshakeAndNotifyCoroutine>d__33 : 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__33(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();
			}
		}

		[CompilerGenerated]
		private sealed class <SchedulerCoroutine>d__22 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public AnnouncementRouter <>4__this;

			private float <interval>5__1;

			private bool <sendToast>5__2;

			private bool <sendBanner>5__3;

			private bool <includeHost>5__4;

			private bool <playSound>5__5;

			private int <schedSoundType>5__6;

			private float <schedSoundVolume>5__7;

			private string <message>5__8;

			private float <duration>5__9;

			private int <fontSize>5__10;

			private AnnouncementData <data>5__11;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SchedulerCoroutine>d__22(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<message>5__8 = null;
				<data>5__11 = default(AnnouncementData);
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0046: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>4__this._lastScheduledTime = Time.time;
					break;
				case 1:
				{
					<>1__state = -1;
					ConfigEntry<bool> enableScheduledAnnouncement = AnnouncementConfig.EnableScheduledAnnouncement;
					if (enableScheduledAnnouncement == null || !enableScheduledAnnouncement.Value || !AnnouncementManager.IsHost() || !NetworkServer.active)
					{
						break;
					}
					<interval>5__1 = AnnouncementConfig.ScheduledAnnouncementInterval?.Value ?? 300f;
					if (Time.time - <>4__this._lastScheduledTime < <interval>5__1)
					{
						break;
					}
					<>4__this._lastScheduledTime = Time.time;
					<sendToast>5__2 = AnnouncementConfig.ScheduledAnnouncementToast?.Value ?? true;
					<sendBanner>5__3 = AnnouncementConfig.ScheduledAnnouncementBanner?.Value ?? false;
					<includeHost>5__4 = AnnouncementConfig.ScheduledAnnouncementIncludeHost?.Value ?? false;
					<playSound>5__5 = AnnouncementConfig.ScheduledAnnouncementPlaySound?.Value ?? false;
					<schedSoundType>5__6 = AnnouncementConfig.ScheduledSoundIndex?.Value ?? 0;
					<schedSoundVolume>5__7 = AnnouncementConfig.ScheduledSoundVolume?.Value ?? 0.5f;
					if (<sendToast>5__2)
					{
						<message>5__8 = AnnouncementConfig.JoinToastMessage?.Value ?? "Server Announcement";
						<duration>5__9 = AnnouncementConfig.ToastDuration?.Value ?? 5f;
						<fontSize>5__10 = AnnouncementConfig.ToastFontSize?.Value ?? 20;
						<>4__this.BroadcastScheduledToast(<message>5__8, <duration>5__9, <fontSize>5__10, <schedSoundType>5__6, <schedSoundVolume>5__7);
						if (<includeHost>5__4)
						{
							<>4__this._hostChannel.SendToastWithSoundOption(<message>5__8, <duration>5__9, <fontSize>5__10, force: true, <playSound>5__5);
						}
						<message>5__8 = null;
					}
					if (<sendBanner>5__3)
					{
						<data>5__11 = <>4__this.CreateBannerData();
						<>4__this.BroadcastScheduledBanner(<data>5__11, <schedSoundType>5__6, <schedSoundVolume>5__7);
						if (<includeHost>5__4)
						{
							<>4__this._hostChannel.SendBannerWithSoundOption(<data>5__11, force: true, <playSound>5__5);
						}
						<data>5__11 = default(AnnouncementData);
					}
					ServerAnnouncementsPlugin.Log.LogDebug((object)$"[Router] Scheduled announcement sent. Toast: {<sendToast>5__2}, Banner: {<sendBanner>5__3}, IncludeHost: {<includeHost>5__4}, PlaySound: {<playSound>5__5}");
					break;
				}
				}
				<>2__current = (object)new WaitForSeconds(1f);
				<>1__state = 1;
				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();
			}
		}

		[CompilerGenerated]
		private sealed class <SharedSchedulerCoroutine>d__30 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public AnnouncementRouter <>4__this;

			private ModeratorSharedContent <content>5__1;

			private float <interval>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SharedSchedulerCoroutine>d__30(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<content>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0046: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>4__this._lastSharedScheduledTime = Time.time;
					break;
				case 1:
					<>1__state = -1;
					<content>5__1 = ModeratorContentManager.SharedContent;
					if (!<content>5__1.ScheduleEnabled || !AnnouncementManager.IsHost() || !NetworkServer.active)
					{
						break;
					}
					<interval>5__2 = <content>5__1.ScheduleInterval;
					if (<interval>5__2 < 5f)
					{
						<interval>5__2 = 5f;
					}
					if (!(Time.time - <>4__this._lastSharedScheduledTime < <interval>5__2))
					{
						<>4__this._lastSharedScheduledTime = Time.time;
						if (<content>5__1.ScheduleToast)
						{
							<>4__this.BroadcastScheduledToast(<content>5__1.ToastMessage, <content>5__1.ToastDuration, <content>5__1.ToastFontSize, <content>5__1.SoundType, <content>5__1.SoundVolume);
						}
						if (<content>5__1.ScheduleBanner)
						{
							<>4__this.BroadcastSharedBanner(force: true, bypassRateLimit: true);
						}
						ServerAnnouncementsPlugin.Log.LogDebug((object)$"[Router] Shared scheduled announcement sent. Toast: {<content>5__1.ScheduleToast}, Banner: {<content>5__1.ScheduleBanner}");
						<content>5__1 = null;
					}
					break;
				}
				<>2__current = (object)new WaitForSeconds(1f);
				<>1__state = 1;
				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 AnnouncementRouter _instance;

		private readonly HostChannel _hostChannel;

		private readonly ModClientChannel _modClientChannel;

		private readonly VanillaClientChannel _vanillaClientChannel;

		private float _lastBroadcastTime = 0f;

		private float _lastScheduledTime = 0f;

		private bool _schedulerRunning = false;

		private Coroutine _schedulerCoroutine = null;

		private float _lastSharedScheduledTime = 0f;

		private bool _sharedSchedulerRunning = false;

		private Coroutine _sharedSchedulerCoroutine = null;

		private bool _hostReceivedThisSession = false;

		public static AnnouncementRouter Instance => _instance ?? (_instance = new AnnouncementRouter());

		public bool IsSchedulerRunning => _schedulerRunning;

		public bool IsSharedSchedulerRunning => _sharedSchedulerRunning;

		private AnnouncementRouter()
		{
			_hostChannel = new HostChannel();
			_modClientChannel = new ModClientChannel();
			_vanillaClientChannel = new VanillaClientChannel();
		}

		public void ResetSession()
		{
			_hostReceivedThisSession = false;
			PlayerTypeManager.Reset();
			StopScheduler();
			StopSharedScheduler();
		}

		public void StartScheduler()
		{
			if (!_schedulerRunning && !((Object)(object)ServerAnnouncementsPlugin.Instance == (Object)null))
			{
				_schedulerCoroutine = ((MonoBehaviour)ServerAnnouncementsPlugin.Instance).StartCoroutine(SchedulerCoroutine());
				_schedulerRunning = true;
				ServerAnnouncementsPlugin.Log.LogInfo((object)"[Router] Scheduled announcements started.");
				ModeratorContentManager.OnServerStarted();
			}
		}

		public void StopScheduler()
		{
			if (_schedulerRunning)
			{
				if (_schedulerCoroutine != null && (Object)(object)ServerAnnouncementsPlugin.Instance != (Object)null)
				{
					((MonoBehaviour)ServerAnnouncementsPlugin.Instance).StopCoroutine(_schedulerCoroutine);
					_schedulerCoroutine = null;
				}
				_schedulerRunning = false;
				ServerAnnouncementsPlugin.Log.LogInfo((object)"[Router] Scheduled announcements stopped.");
			}
		}

		public float GetTimeUntilNextAnnouncement()
		{
			if (!_schedulerRunning)
			{
				return -1f;
			}
			float num = AnnouncementConfig.ScheduledAnnouncementInterval?.Value ?? 300f;
			float num2 = Time.time - _lastScheduledTime;
			return Mathf.Max(0f, num - num2);
		}

		public void ResetSchedulerTimer()
		{
			_lastScheduledTime = Time.time;
		}

		[IteratorStateMachine(typeof(<SchedulerCoroutine>d__22))]
		private IEnumerator SchedulerCoroutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SchedulerCoroutine>d__22(0)
			{
				<>4__this = this
			};
		}

		public void StartSharedScheduler()
		{
			if (!_sharedSchedulerRunning && !((Object)(object)ServerAnnouncementsPlugin.Instance == (Object)null))
			{
				_sharedSchedulerCoroutine = ((MonoBehaviour)ServerAnnouncementsPlugin.Instance).StartCoroutine(SharedSchedulerCoroutine());
				_sharedSchedulerRunning = true;
				ServerAnnouncementsPlugin.Log.LogInfo((object)"[Router] Shared content scheduler started.");
			}
		}

		public void StopSharedScheduler()
		{
			if (_sharedSchedulerRunning)
			{
				if (_sharedSchedulerCoroutine != null && (Object)(object)ServerAnnouncementsPlugin.Instance != (Object)null)
				{
					((MonoBehaviour)ServerAnnouncementsPlugin.Instance).StopCoroutine(_sharedSchedulerCoroutine);
					_sharedSchedulerCoroutine = null;
				}
				_sharedSchedulerRunning = false;
				ServerAnnouncementsPlugin.Log.LogInfo((object)"[Router] Shared content scheduler stopped.");
			}
		}

		public float GetTimeUntilNextSharedAnnouncement()
		{
			if (!_sharedSchedulerRunning)
			{
				return -1f;
			}
			ModeratorSharedContent sharedContent = ModeratorContentManager.SharedContent;
			float scheduleInterval = sharedContent.ScheduleInterval;
			float num = Time.time - _lastSharedScheduledTime;
			return Mathf.Max(0f, scheduleInterval - num);
		}

		public void ResetSharedSchedulerTimer()
		{
			_lastSharedScheduledTime = Time.time;
		}

		public void UpdateSharedSchedulerState()
		{
			ModeratorSharedContent sharedContent = ModeratorContentManager.SharedContent;
			if (sharedContent.ScheduleEnabled && !_sharedSchedulerRunning)
			{
				StartSharedScheduler();
			}
			else if (!sharedContent.ScheduleEnabled && _sharedSchedulerRunning)
			{
				StopSharedScheduler();
			}
		}

		[IteratorStateMachine(typeof(<SharedSchedulerCoroutine>d__30))]
		private IEnumerator SharedSchedulerCoroutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SharedSchedulerCoroutine>d__30(0)
			{
				<>4__this = this
			};
		}

		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__33))]
		private IEnumerator HandshakeAndNotifyCoroutine(int connectionId)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <HandshakeAndNotifyCoroutine>d__33(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.ShowBannerOnJoin.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 BroadcastSharedBanner(bool force = true, bool bypassRateLimit = false)
		{
			if (!NetworkServer.active)
			{
				ServerAnnouncementsPlugin.Log.LogWarning((object)"[Router] BroadcastSharedBanner called but server not active.");
				return;
			}
			if (!bypassRateLimit && !CheckRateLimit())
			{
				ServerAnnouncementsPlugin.Log.LogWarning((object)"[Router] Broadcast rate limited.");
				return;
			}
			ModeratorSharedContent sharedContent = ModeratorContentManager.SharedContent;
			AnnouncementData data = new AnnouncementData(sharedContent.BannerTitle, sharedContent.BannerContent, 1, "", sharedContent.TitleFontSize, sharedContent.ContentFontSize, sharedContent.BannerExtraContent, sharedContent.ExtraFontSize, sharedContent.BannerHighlight, sharedContent.HighlightFontSize);
			if (string.IsNullOrEmpty(data.Title) && string.IsNullOrEmpty(data.Content))
			{
				ServerAnnouncementsPlugin.Log.LogWarning((object)"[Router] Shared banner has no content, skipping.");
				return;
			}
			_hostChannel.SendBanner(0, data, force);
			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;
			ServerAnnouncementsPlugin.Log.LogDebug((object)"[Router] Shared banner broadcasted.");
		}

		private void BroadcastScheduledToast(string message, float duration, int fontSize, int soundType, float soundVolume)
		{
			if (!NetworkServer.active)
			{
				return;
			}
			message = TruncateMessage(message);
			foreach (PlayerInfo client in PlayerTypeManager.GetClients())
			{
				_modClientChannel.SendScheduledToast(client.ConnectionId, message, duration, fontSize, soundType, soundVolume);
				if (AnnouncementConfig.VanillaCompatMode.Value)
				{
					_vanillaClientChannel.SendToast(client.ConnectionId, message, duration, fontSize, force: true);
				}
			}
		}

		private void BroadcastScheduledBanner(AnnouncementData data, int soundType, float soundVolume)
		{
			if (!NetworkServer.active)
			{
				return;
			}
			foreach (PlayerInfo client in PlayerTypeManager.GetClients())
			{
				_modClientChannel.SendScheduledBanner(client.ConnectionId, data, soundType, soundVolume);
				if (AnnouncementConfig.VanillaCompatMode.Value)
				{
					_vanillaClientChannel.SendBanner(client.ConnectionId, data, force: true);
				}
			}
		}

		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()
		{
			float num = AnnouncementConfig.BroadcastCooldown?.Value ?? 3f;
			return Time.time - _lastBroadcastTime >= num;
		}

		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, AnnouncementConfig.BannerHighlight?.Value ?? "", AnnouncementConfig.BannerHighlightFontSize?.Value ?? 18);
		}
	}
	public static class CodeTalkerIntegration
	{
		private static bool _initialized;

		private static bool _available;

		private static bool _uiInitialized;

		public static bool IsAvailable => _available;

		public static event Action<string, bool, string> OnModResponseReceived;

		public static event Action OnModeratorContentSynced;

		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.LogDebug((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;
				}
				if (announcementBinaryPacket.Type == "ClientResponse")
				{
					if (AnnouncementManager.IsHost())
					{
						ulong senderID = header.SenderID;
						if (senderID != 0)
						{
							PlayerTypeManager.HandleHandshakeResponseBySteamId(senderID);
							PermissionHelper.BroadcastModeratorSync();
							ModeratorContentManager.BroadcastContentSync();
						}
					}
				}
				else if (announcementBinaryPacket.Type == "ModRequest")
				{
					if (AnnouncementManager.IsHost())
					{
						ulong senderID2 = header.SenderID;
						string senderSteamId = senderID2.ToString();
						ProcessModeratorRequest(announcementBinaryPacket, senderSteamId);
					}
				}
				else if (announcementBinaryPacket.Type == "ModContentUpdate")
				{
					if (AnnouncementManager.IsHost())
					{
						ulong senderID2 = header.SenderID;
						string text = senderID2.ToString();
						if (PermissionHelper.IsModerator(text))
						{
							ProcessModeratorContentUpdate(announcementBinaryPacket, text);
							return;
						}
						ServerAnnouncementsPlugin.Log.LogWarning((object)("[ModContentUpdate] Permission denied for " + text));
						SendModResponse(announcementBinaryPacket.RequestId ?? "content-update", success: false, "Permission denied");
					}
				}
				else if (announcementBinaryPacket.Type == "ModResponse")
				{
					if (header.SenderIsLobbyOwner)
					{
						ProcessReceivedPacket(announcementBinaryPacket);
					}
				}
				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 ProcessModeratorRequest(AnnouncementBinaryPacket packet, string senderSteamId)
		{
			if (!PermissionHelper.IsModerator(senderSteamId))
			{
				ServerAnnouncementsPlugin.Log.LogWarning((object)("[ModRequest] Permission denied for " + senderSteamId));
				SendModResponse(packet.RequestId, success: false, "Permission denied");
				return;
			}
			try
			{
				string title = packet.Title;
				if (title == "Toast")
				{
					AnnouncementRouter.Instance.BroadcastToast(packet.Content, packet.Duration, packet.ToastFontSize, packet.ForceShow);
					SendModResponse(packet.RequestId, success: true, "");
				}
				else if (title == "Banner")
				{
					AnnouncementRouter.Instance.BroadcastSharedBanner();
					SendModResponse(packet.RequestId, success: true, "");
				}
				else
				{
					ServerAnnouncementsPlugin.Log.LogWarning((object)("[ModRequest] Unknown request type: " + title));
					SendModResponse(packet.RequestId, success: false, "Unknown request type: " + title);
				}
			}
			catch (Exception ex)
			{
				ServerAnnouncementsPlugin.Log.LogError((object)("[ModRequest] Error: " + ex.Message));
				SendModResponse(packet.RequestId, success: false, ex.Message);
			}
		}

		private static void SendModResponse(string requestId, bool success, string errorMessage)
		{
			if (_available)
			{
				AnnouncementBinaryPacket packet = new AnnouncementBinaryPacket
				{
					Type = "ModResponse",
					RequestId = requestId,
					Success = success,
					ErrorMessage = (errorMessage ?? "")
				};
				SendPacket(packet);
			}
		}

		private static void ProcessReceivedPacket(AnnouncementBinaryPacket packet)
		{
			EnsureUIInitialized();
			switch (packet.Type)
			{
			case "Toast":
				AnnouncementManager.Instance?.HandleToastReceived(packet.Content, packet.Duration, packet.ToastFontSize, packet.ForceShow, null, packet.SoundType, packet.SoundVolume);
				break;
			case "Banner":
			{
				AnnouncementData data = new AnnouncementData(packet.Title, packet.Content, packet.Version, "", packet.TitleFontSize, packet.ContentFontSize, packet.ExtraContent, packet.ExtraFontSize, packet.Highlight, packet.HighlightFontSize);
				AnnouncementManager.Instance?.HandleBannerReceived(data, packet.ForceShow, null, packet.SoundType, packet.SoundVolume);
				break;
			}
			case "Handshake":
				AnnouncementNetworkHandler.HandleHandshake("ServerAnnouncements_v2_CT");
				SendClientResponse();
				break;
			case "ModResponse":
				HandleModResponse(packet);
				break;
			case "PermSync":
				HandlePermissionSync(packet);
				break;
			case "ModContentSync":
				HandleModeratorContentSync(packet);
				break;
			default:
				ServerAnnouncementsPlugin.Log.LogWarning((object)("Unknown announcement type: " + packet.Type));
				break;
			}
		}

		private static void HandlePermissionSync(AnnouncementBinaryPacket packet)
		{
			string text = packet.Content ?? "";
			string[] moderatorIds = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			PermissionHelper.UpdateSyncedModeratorList(moderatorIds);
		}

		private static void HandleModeratorContentSync(AnnouncementBinaryPacket packet)
		{
			string serializedData = packet.ExtraContent ?? "";
			ModeratorContentManager.UpdateFromSync(serializedData);
			CodeTalkerIntegration.OnModeratorContentSynced?.Invoke();
		}

		private static void ProcessModeratorContentUpdate(AnnouncementBinaryPacket packet, string senderSteamId)
		{
			string data = packet.ExtraContent ?? "";
			ModeratorSharedContent content = ModeratorSharedContent.Deserialize(data);
			ModeratorContentManager.UpdateContent(content, senderSteamId);
		}

		private static void HandleModResponse(AnnouncementBinaryPacket packet)
		{
			if (!packet.Success && packet.ErrorMessage != null && packet.ErrorMessage.Contains("Permission denied"))
			{
				PermissionHelper.ClearSyncedModeratorList();
			}
			CodeTalkerIntegration.OnModResponseReceived?.Invoke(packet.RequestId, packet.Success, packet.ErrorMessage);
		}

		private static bool SendPacket(AnnouncementBinaryPacket packet)
		{
			if (!_available)
			{
				return false;
			}
			try
			{
				CodeTalkerNetwork.SendNetworkPacket((BinaryPacketBase)(object)packet);
				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()
			};
			return SendPacket(packet);
		}

		public static string SendModRequest(string requestType, string message, float duration, int fontSize, bool forceShow)
		{
			if (!_available)
			{
				return null;
			}
			if (!PermissionHelper.CanSendAnnouncements())
			{
				return null;
			}
			string text = Guid.NewGuid().ToString("N").Substring(0, 8);
			AnnouncementBinaryPacket packet = new AnnouncementBinaryPacket
			{
				Type = "ModRequest",
				Title = requestType,
				Content = (message ?? ""),
				Duration = duration,
				ToastFontSize = fontSize,
				ForceShow = forceShow,
				RequestId = text
			};
			if (SendPacket(packet))
			{
				return text;
			}
			return null;
		}

		public static bool BroadcastPermissionSync(string moderatorList)
		{
			if (!_available)
			{
				return false;
			}
			if (!AnnouncementManager.IsHost())
			{
				return false;
			}
			AnnouncementBinaryPacket packet = new AnnouncementBinaryPacket
			{
				Type = "PermSync",
				Content = (moderatorList ?? "")
			};
			return SendPacket(packet);
		}

		public static bool BroadcastModeratorContentSync(string serializedContent)
		{
			if (!_available)
			{
				return false;
			}
			if (!AnnouncementManager.IsHost())
			{
				return false;
			}
			AnnouncementBinaryPacket packet = new AnnouncementBinaryPacket
			{
				Type = "ModContentSync",
				ExtraContent = (serializedContent ?? "")
			};
			return SendPacket(packet);
		}

		public static bool SendModeratorContentUpdate(string serializedContent)
		{
			if (!_available)
			{
				return false;
			}
			if (!PermissionHelper.CanSendAnnouncements())
			{
				return false;
			}
			AnnouncementBinaryPacket packet = new AnnouncementBinaryPacket
			{
				Type = "ModContentUpdate",
				ExtraContent = (serializedContent ?? "")
			};
			return SendPacket(packet);
		}
	}
	public class AnnouncementBinaryPacket : BinaryPacketBase
	{
		public override string PacketSignature => "SA_ANN_v2";

		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 string RequestId { get; set; } = "";


		public bool Success { get; set; }

		public string ErrorMessage { get; set; } = "";


		public int SoundType { get; set; }

		public float SoundVolume { get; set; } = 0.5f;


		public string Highlight { get; set; } = "";


		public int HighlightFontSize { 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");
			stringBuilder.Append("|");
			stringBuilder.Append(EscapePipe(RequestId));
			stringBuilder.Append("|");
			stringBuilder.Append(Success ? "1" : "0");
			stringBuilder.Append("|");
			stringBuilder.Append(EscapePipe(ErrorMessage));
			stringBuilder.Append("|");
			stringBuilder.Append(SoundType);
			stringBuilder.Append("|");
			stringBuilder.Append(SoundVolume.ToString("F2"));
			stringBuilder.Append("|");
			stringBuilder.Append(EscapePipe(Highlight));
			stringBuilder.Append("|");
			stringBuilder.Append(HighlightFontSize);
			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";
				}
				if (array.Length >= 18)
				{
					RequestId = UnescapePipe(array[11]);
					Success = array[12] == "1";
					ErrorMessage = UnescapePipe(array[13]);
					int.TryParse(array[14], out var result7);
					SoundType = result7;
					float.TryParse(array[15], out var result8);
					SoundVolume = result8;
					Highlight = UnescapePipe(array[16]);
					int.TryParse(array[17], out var result9);
					HighlightFontSize = result9;
				}
			}
			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, out int soundType, out float soundVolume, out bool isScheduled)
		{
			text = null;
			duration = 0f;
			fontSize = 20;
			forceShow = false;
			soundType = 0;
			soundVolume = 0.5f;
			isScheduled = 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("|SCHED"))
				{
					isScheduled = true;
					forceShow = true;
					text2 = text2.Substring(0, text2.Length - 6);
				}
				else if (text2.EndsWith("|FORCE"))
				{
					forceShow = true;
					text2 = text2.Substring(0, text2.Length - 6);
				}
				string[] array = text2.Split(new char[1] { '|' }, 5);
				if (array.Length >= 5)
				{
					if (!float.TryParse(array[0], out duration))
					{
						return false;
					}
					if (!int.TryParse(array[1], out fontSize))
					{
						fontSize = 20;
					}
					if (!int.TryParse(array[2], out soundType))
					{
						soundType = 0;
					}
					if (!float.TryParse(array[3], out soundVolume))
					{
						soundVolume = 0.5f;
					}
					text = array[4];
					return true;
				}
				if (array.Length >= 3)
				{
					if (!float.TryParse(array[0], out duration))
					{
						return false;
					}
					if (!int.TryParse(array[1], out fontSize))
					{
						fontSize = 20;
					}
					text = array[2];
					return true;
				}
				if (array.Length >= 2)
				{
					if (!float.TryParse(array[0], out duration))
					{
						return false;
					}
					text = array[1];
					return true;
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		public static bool TryParseBanner(string message, out AnnouncementData data, out bool forceShow, out int soundType, out float soundVolume, out bool isScheduled)
		{
			data = default(AnnouncementData);
			soundType = 0;
			soundVolume = 0.5f;
			forceShow = false;
			isScheduled = 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("|SCHED"))
				{
					isScheduled = true;
					forceShow = true;
					text = text.Substring(0, text.Length - 6);
				}
				else if (text.EndsWith("|FORCE"))
				{
					forceShow = true;
					text = text.Substring(0, text.Length - 6);
				}
				string[] array = text.Split(new char[1] { '|' }, 11);
				if (array.Length >= 11)
				{
					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;
					}
					if (!int.TryParse(array[4], out var result5))
					{
						result5 = 18;
					}
					if (!int.TryParse(array[5], out soundType))
					{
						soundType = 0;
					}
					if (!float.TryParse(array[6], out soundVolume))
					{
						soundVolume = 0.5f;
					}
					data = new AnnouncementData(array[7], array[9].Replace("\\|", "|"), result, "", result2, result3, array[10].Replace("\\|", "|"), result4, array[8].Replace("\\|", "|"), result5);
					return true;
				}
				if (array.Length >= 9)
				{
					if (!int.TryParse(array[0], out var result6))
					{
						return false;
					}
					if (!int.TryParse(array[1], out var result7))
					{
						result7 = 24;
					}
					if (!int.TryParse(array[2], out var result8))
					{
						result8 = 16;
					}
					if (!int.TryParse(array[3], out var result9))
					{
						result9 = 14;
					}
					if (!int.TryParse(array[4], out var result10))
					{
						result10 = 18;
					}
					data = new AnnouncementData(array[5], array[7].Replace("\\|", "|"), result6, "", result7, result8, array[8].Replace("\\|", "|"), result9, array[6].Replace("\\|", "|"), result10);
					return true;
				}
				if (array.Length >= 7)
				{
					if (!int.TryParse(array[0], out var result11))
					{
						return false;
					}
					if (!int.TryParse(array[1], out var result12))
					{
						result12 = 24;
					}
					if (!int.TryParse(array[2], out var result13))
					{
						result13 = 16;
					}
					if (!int.TryParse(array[3], out var result14))
					{
						result14 = 14;
					}
					data = new AnnouncementData(array[4], array[5].Replace("\\|", "|"), result11, "", result12, result13, array[6].Replace("\\|", "|"), result14);
					return true;
				}
				if (array.Length >= 5)
				{
					if (!int.TryParse(array[0], out var result15))
					{
						return false;
					}
					if (!int.TryParse(array[1], out var result16))
					{
						result16 = 24;
					}
					if (!int.TryParse(array[2], out var result17))
					{
						result17 = 16;
					}
					data = new AnnouncementData(array[3], array[4].Replace("\\|", "|"), result15, "", result16, result17);
					return true;
				}
				if (array.Length >= 3)
				{
					if (!int.TryParse(array[0], out var result18))
					{
						return false;
					}
					data = new AnnouncementData(array[1], array[2].Replace("\\|", "|"), result18);
					return true;
				}
				return false;
			}
			catch
			{
				return false;
			}
		}
	}
	[HarmonyPatch]
	public static class NoticeNetworkPatches
	{
		private static readonly string[] VANILLA_PREFIXES = new string[4] { "[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, out var soundType, out var soundVolume, out var isScheduled))
				{
					AnnouncementManager.Instance?.HandleToastReceived(text, duration, fontSize, forceShow, null, soundType, soundVolume);
					AnnouncementManager.Instance?.SetLastIsScheduled(isScheduled);
					return false;
				}
				if (AnnouncementNetworkHandler.TryParseBanner(_message, out var data, out var forceShow2, out var soundType2, out var soundVolume2, out var isScheduled2))
				{
					AnnouncementManager.Instance?.HandleBannerReceived(data, forceShow2, null, soundType2, soundVolume2);
					AnnouncementManager.Instance?.SetLastIsScheduled(isScheduled2);
					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, out var soundType, out var soundVolume, out var isScheduled))
				{
					AnnouncementManager.Instance?.HandleToastReceived(text2, duration, fontSize, forceShow, null, soundType, soundVolume);
					AnnouncementManager.Instance?.SetLastIsScheduled(isScheduled);
					return false;
				}
				if (AnnouncementNetworkHandler.TryParseBanner(text, out var data, out var forceShow2, out var soundType2, out var soundVolume2, out var isScheduled2))
				{
					AnnouncementManager.Instance?.HandleBannerReceived(data, forceShow2, null, soundType2, soundVolume2);
					AnnouncementManager.Instance?.SetLastIsScheduled(isScheduled2);
					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 string CharacterName { get; set; }

		public string GlobalNickname { 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;
			CharacterName = "";
			GlobalNickname = "";
		}

		public string GetDisplayString()
		{
			if (!string.IsNullOrEmpty(CharacterName) || !string.IsNullOrEmpty(GlobalNickname))
			{
				List<string> list = new List<string>();
				if (!string.IsNullOrEmpty(CharacterName))
				{
					list.Add(CharacterName);
				}
				if (!string.IsNullOrEmpty(GlobalNickname) && GlobalNickname != CharacterName)
				{
					list.Add("(" + GlobalNickname + ")");
				}
				if (SteamId != 0)
				{
					list.Add($"[{SteamId}]");
				}
				return (list.Count > 0) ? string.Join(" ", list) : $"[{SteamId}]";
			}
			if (SteamId != 0)
			{
				return PlayerTypeManager.GetDisplayStringBySteamId(SteamId.ToString());
			}
			return $"[{SteamId}]";
		}
	}
	public static class PlayerTypeManager
	{