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
{