Decompiled source of ScarletHooks v1.1.0

ScarletHooks.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Microsoft.CodeAnalysis;
using ProjectM;
using ProjectM.Network;
using ScarletCore.Data;
using ScarletCore.Events;
using ScarletCore.Services;
using ScarletCore.Systems;
using ScarletCore.Utils;
using ScarletHooks.Data;
using ScarletHooks.Systems;
using ScarletRCON.Shared;
using Stunlock.Core;
using Unity.Collections;
using Unity.Entities;
using VampireCommandFramework;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ScarletHooks")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("ScarletHooks")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+8089890c32adf5b83e2f9a20c8ecb1b3e23af955")]
[assembly: AssemblyProduct("ScarletHooks")]
[assembly: AssemblyTitle("ScarletHooks")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ScarletHooks
{
	public static class ECSExtensions
	{
		public static EntityManager EntityManager => GameSystems.EntityManager;

		public unsafe static void Write<T>(this Entity entity, T componentData) where T : struct
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			ComponentType val = default(ComponentType);
			((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0);
			byte[] array = StructureToByteArray(componentData);
			int num = Marshal.SizeOf<T>();
			fixed (byte* ptr = array)
			{
				EntityManager entityManager = EntityManager;
				((EntityManager)(ref entityManager)).SetComponentDataRaw(entity, val.TypeIndex, (void*)ptr, num);
			}
		}

		public static byte[] StructureToByteArray<T>(T structure) where T : struct
		{
			int num = Marshal.SizeOf(structure);
			byte[] array = new byte[num];
			IntPtr intPtr = Marshal.AllocHGlobal(num);
			Marshal.StructureToPtr(structure, intPtr, fDeleteOld: true);
			Marshal.Copy(intPtr, array, 0, num);
			Marshal.FreeHGlobal(intPtr);
			return array;
		}

		public unsafe static T Read<T>(this Entity entity) where T : struct
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			ComponentType val = default(ComponentType);
			((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0);
			EntityManager entityManager = EntityManager;
			void* componentDataRawRO = ((EntityManager)(ref entityManager)).GetComponentDataRawRO(entity, val.TypeIndex);
			return Marshal.PtrToStructure<T>(new IntPtr(componentDataRawRO));
		}

		public static DynamicBuffer<T> ReadBuffer<T>(this Entity entity) where T : struct
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			EntityManager entityManager = EntityManager;
			return ((EntityManager)(ref entityManager)).GetBuffer<T>(entity, false);
		}

		public static bool Has<T>(this Entity entity)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			ComponentType val = default(ComponentType);
			((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0);
			EntityManager entityManager = EntityManager;
			return ((EntityManager)(ref entityManager)).HasComponent(entity, val);
		}

		public static void Add<T>(this Entity entity)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			ComponentType val = default(ComponentType);
			((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0);
			EntityManager entityManager = EntityManager;
			((EntityManager)(ref entityManager)).AddComponent(entity, val);
		}

		public static void Remove<T>(this Entity entity)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			ComponentType val = default(ComponentType);
			((ComponentType)(ref val))..ctor(Il2CppType.Of<T>(), (AccessMode)0);
			EntityManager entityManager = EntityManager;
			((EntityManager)(ref entityManager)).RemoveComponent(entity, val);
		}
	}
	[BepInPlugin("ScarletHooks", "ScarletHooks", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BasePlugin
	{
		private static Harmony _harmony;

		public static Harmony Harmony => _harmony;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource LogInstance { get; private set; }

		public static Settings Settings { get; private set; }

		public static Database Database { get; private set; }

		public override void Load()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			Instance = this;
			ManualLogSource log = ((BasePlugin)this).Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(27, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Plugin ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("ScarletHooks");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" version ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("1.1.0");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" is loaded!");
			}
			log.LogInfo(val);
			_harmony = new Harmony("ScarletHooks");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			Settings = new Settings("ScarletHooks", (BasePlugin)(object)Instance);
			Database = new Database("ScarletHooks");
			LoadSettings();
			MessageDispatchSystem.Initialize();
			CommandRegistry.RegisterAll();
			RconCommandRegistrar.RegisterAll();
		}

		public override bool Unload()
		{
			CommandRegistry.UnregisterAssembly();
			RconCommandRegistrar.UnregisterAssembly();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			return true;
		}

		public static void LoadSettings()
		{
			SettingsSection val = Settings.Section("General");
			val.Add<string>("AdminWebhookURL", "null", "Admin Webhook URL. All messages configured for admin will be sent to this address.");
			val.Add<string>("PublicWebhookURL", "null", "Public Webhook URL. All messages configured for public will be sent to this address.");
			val.Add<string>("LoginWebhookURL", "null", "Login Webhook URL. Only login messages will be sent to this address.");
			val.Add<string>("PvpKillWebhookURL", "null", "PvP Kill Webhook URL. Only PvP kill messages will be sent to this address.");
			val.Add<string>("VBloodWebhookURL", "null", "VBlood Death Webhook URL. Only VBlood death messages will be sent to this address.");
			val.Add<bool>("EnableBatching", true, "Enable or disable batching messages to avoid rate limiting.\nUseful for large servers or when many messages are sent at once.");
			val.Add<float>("MessageInterval", 0.2f, "Interval in seconds between sending messages.\nUseful to prevent rate limiting when sending messages to webhooks.");
			val.Add<float>("OnFailInterval", 2f, "Interval in seconds to wait before retrying after a webhook failure.");
			SettingsSection val2 = Settings.Section("Customization");
			val2.Add<string>("LoginMessageFormat", "{playerName} has joined the game.", "Format for login messages.\nAvailable placeholders: {playerName}, {clanName}");
			val2.Add<string>("LogoutMessageFormat", "{playerName} has left the game.", "Format for logout messages.\nAvailable placeholders: {playerName}, {clanName}");
			val2.Add<string>("GlobalPrefix", "[Global] {playerName}:", "Prefix for global chat messages.\nAvailable placeholders: {playerName}, {clanName}");
			val2.Add<string>("LocalPrefix", "[Local] {playerName}:", "Prefix for local chat messages.\nAvailable placeholders: {playerName}, {clanName}");
			val2.Add<string>("ClanPrefix", "[Clan][{clanName}] {playerName}:", "Prefix for clan chat messages.\nAvailable placeholders: {playerName}, {clanName}");
			val2.Add<string>("WhisperPrefix", "[Whisper to {targetName}] {playerName}:", "Prefix for whisper messages.\nAvailable placeholders: {playerName}, {clanName}, {targetName}");
			val2.Add<string>("VBloodDeathMessageFormat", "{playerName} has killed {VBloodName}.", "Format for VBlood death messages.\nAvailable placeholders: {playerName}, {vBloodName}");
			val2.Add<string>("PvpKillMessageFormat", "[{clanName}] {playerName} has killed [{clanName}] {targetName}.", "Format for PvP kill messages.\nAvailable placeholders: {playerName}, {targetName}, {clanName}");
			SettingsSection val3 = Settings.Section("Admin");
			val3.Add<bool>("AdminGlobalMessages", true, "Enable or disable sending global chat messages to the Admin Webhook.");
			val3.Add<bool>("AdminLocalMessages", true, "Enable or disable sending local chat messages to the Admin Webhook.");
			val3.Add<bool>("AdminClanMessages", true, "Enable or disable sending clan chat messages to the Admin Webhook.");
			val3.Add<bool>("AdminWhisperMessages", true, "Enable or disable sending whisper messages to the Admin Webhook.");
			val3.Add<bool>("AdminLoginMessages", true, "Enable or disable sending login messages to the Admin Webhook.");
			val3.Add<bool>("AdminVBloodMessages", true, "Enable or disable sending VBlood death messages to the Admin Webhook.");
			val3.Add<bool>("AdminPvpMessages", true, "Enable or disable sending PvP kill messages to the Admin Webhook.");
			SettingsSection val4 = Settings.Section("Public");
			val4.Add<bool>("PublicGlobalMessages", true, "Enable or disable sending global chat messages to the Public Webhook.");
			val4.Add<bool>("PublicLocalMessages", false, "Enable or disable sending local chat messages to the Public Webhook.");
			val4.Add<bool>("PublicClanMessages", false, "Enable or disable sending clan chat messages to the Public Webhook.");
			val4.Add<bool>("PublicWhisperMessages", false, "Enable or disable sending whisper messages to the Public Webhook.");
			val4.Add<bool>("PublicLoginMessages", false, "Enable or disable sending login messages to the Public Webhook.");
			val4.Add<bool>("PublicVBloodMessages", true, "Enable or disable sending VBlood death messages to the Public Webhook.");
			val4.Add<bool>("PublicPvpMessages", true, "Enable or disable sending PvP kill messages to the Public Webhook.");
			SettingsSection val5 = Settings.Section("Clans");
			val5.Add<bool>("ClanLoginMessages", true, "Enable or disable sending login messages to clans.");
		}

		public static void ReloadSettings()
		{
			Settings.Dispose();
			LoadSettings();
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "ScarletHooks";

		public const string PLUGIN_NAME = "ScarletHooks";

		public const string PLUGIN_VERSION = "1.1.0";
	}
}
namespace ScarletHooks.Systems
{
	public static class MessageDispatchSystem
	{
		private static readonly HttpClient _httpClient = new HttpClient();

		public static readonly ConcurrentDictionary<string, Queue<string>> MessageQueues = new ConcurrentDictionary<string, Queue<string>>();

		public static readonly ConcurrentDictionary<string, DateTime> LastUsed = new ConcurrentDictionary<string, DateTime>();

		private static readonly ConcurrentDictionary<string, int> ConsecutiveFailures = new ConcurrentDictionary<string, int>();

		private const int MaxFailuresBeforeDiscard = 5;

		private static CancellationTokenSource _cts;

		public static bool ShowRunning = false;

		private static bool _isRunning = false;

		private static Settings Settings => Plugin.Settings;

		private static Database Database => Plugin.Database;

		public static string AdminWebHookUrl => Settings.Get<string>("AdminWebhookURL");

		public static string PublicWebHookUrl => Settings.Get<string>("PublicWebhookURL");

		public static string LoginWebhookURL => Settings.Get<string>("LoginWebhookURL");

		public static string PvpKillWebhookURL => Settings.Get<string>("PvpKillWebhookURL");

		public static string VBloodWebhookURL => Settings.Get<string>("VBloodWebhookURL");

		public static float MessageInterval => Settings.Get<float>("MessageInterval");

		public static float OnFailInterval => Settings.Get<float>("OnFailInterval");

		public static bool EnableBatching => Settings.Get<bool>("EnableBatching");

		public static Dictionary<string, string> ClanWebHookUrls { get; set; } = new Dictionary<string, string>();


		public static void Initialize()
		{
			_isRunning = true;
			LoadFromFile();
			EventManager.OnUserConnected += HandleConnectionEvent;
			EventManager.OnUserDisconnected += HandleDisconnectionEvent;
			EventManager.OnChatMessage += HandleMessageEvent;
			EventManager.OnPlayerDeath += HandlePvpDeathEvent;
			EventManager.OnVBloodDeath += HandleVBloodDeathEvent;
			_cts = new CancellationTokenSource();
			Task.Run(() => ProcessQueueLoop(_cts.Token));
		}

		public static void Shutdown()
		{
			_cts?.Cancel();
			_isRunning = false;
		}

		public static void ForceShutdown()
		{
			_cts?.Cancel();
			_isRunning = false;
			ClearAll();
		}

		public static void ClearAll()
		{
			MessageQueues.Clear();
			LastUsed.Clear();
			ConsecutiveFailures.Clear();
		}

		public static void LoadFromFile()
		{
			if (Database.Has("ClanWebHookUrls"))
			{
				ClanWebHookUrls = Database.Load<Dictionary<string, string>>("ClanWebHookUrls");
			}
		}

		public static void HandlePvpDeathEvent(object _, DeathEventArgs args)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (args == null || !_isRunning)
			{
				return;
			}
			List<DeathInfo> deaths = args.Deaths;
			PlayerData val2 = default(PlayerData);
			PlayerData val3 = default(PlayerData);
			foreach (DeathInfo item in deaths)
			{
				Entity died = item.Died;
				Entity killer = item.Killer;
				if (died.Has<PlayerCharacter>() && killer.Has<PlayerCharacter>())
				{
					PlayerCharacter val = died.Read<PlayerCharacter>();
					if (!PlayerService.TryGetByName(((object)(FixedString64Bytes)(ref val.Name)).ToString(), ref val2))
					{
						break;
					}
					val = killer.Read<PlayerCharacter>();
					if (!PlayerService.TryGetByName(((object)(FixedString64Bytes)(ref val.Name)).ToString(), ref val3))
					{
						break;
					}
					string text = Settings.Get<string>("PvpKillMessageFormat");
					string content = text.Replace("{playerName}", val3.Name).Replace("{targetName}", val2.Name).Replace("[{clanName}]", "[" + val3.ClanName + "]")
						.Replace("{clanName}", (val3.ClanName ?? "") ?? "");
					if (Settings.Get<bool>("AdminPvpMessages"))
					{
						AddToQueue(AdminWebHookUrl, content);
					}
					if (Settings.Get<bool>("PublicPvpMessages"))
					{
						AddToQueue(PublicWebHookUrl, content);
					}
					AddToQueue(PvpKillWebhookURL, content);
				}
			}
		}

		public static void HandleVBloodDeathEvent(object _, DeathEventArgs args)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			if (args == null || !_isRunning)
			{
				return;
			}
			List<DeathInfo> deaths = args.Deaths;
			PlayerData val2 = default(PlayerData);
			foreach (DeathInfo item in deaths)
			{
				Entity died = item.Died;
				Entity killer = item.Killer;
				if (killer.Has<PlayerCharacter>())
				{
					PlayerCharacter val = killer.Read<PlayerCharacter>();
					if (!PlayerService.TryGetByName(((object)(FixedString64Bytes)(ref val.Name)).ToString(), ref val2))
					{
						break;
					}
					Dictionary<int, string> names = VBloods.Names;
					PrefabGUID val3 = died.Read<PrefabGUID>();
					string newValue = names[((PrefabGUID)(ref val3)).GuidHash];
					string text = Settings.Get<string>("VBloodDeathMessageFormat");
					string content = text.Replace("{playerName}", val2.Name).Replace("{VBloodName}", newValue);
					if (Settings.Get<bool>("AdminVBloodMessages"))
					{
						AddToQueue(AdminWebHookUrl, content);
					}
					if (Settings.Get<bool>("PublicVBloodMessages"))
					{
						AddToQueue(PublicWebHookUrl, content);
					}
					AddToQueue(VBloodWebhookURL, content);
				}
			}
		}

		private static void HandleConnectionEvent(object _, UserConnectedEventArgs args)
		{
			if (args != null && _isRunning)
			{
				PlayerData player = args.Player;
				DateTime utcNow = DateTime.UtcNow;
				if (!((utcNow - player.ConnectedSince).TotalSeconds < 10.0))
				{
					HandleLoginMessage(player.Name, player.ClanName);
				}
				player.SetData<DateTime>(utcNow);
			}
		}

		private static void HandleDisconnectionEvent(object _, UserDisconnectedEventArgs args)
		{
			PlayerData player = args.Player;
			DateTime utcNow = DateTime.UtcNow;
			DateTime data = player.GetData<DateTime>();
			if (!(data != default(DateTime)) || !((utcNow - data).TotalSeconds < 10.0))
			{
				HandleLogoutMessage(player.Name, player.ClanName);
			}
		}

		public static void HandleMessageEvent(object _, ChatMessageEventArgs args)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (args != null && _isRunning && !string.IsNullOrEmpty(args.Message) && !string.IsNullOrEmpty(args.Sender.Name))
			{
				string message = args.Message;
				string name = args.Sender.Name;
				ChatMessageType messageType = args.MessageType;
				string clanName = args.Sender.ClanName;
				PlayerData receiver = args.Receiver;
				HandleMessage(message, name, messageType, clanName, (receiver != null) ? receiver.Name : null);
			}
		}

		private static void HandleMessage(string content, string playerName, ChatMessageType messageType, string clanName, string targetName)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected I4, but got Unknown
			if (!string.IsNullOrEmpty(content) && _isRunning)
			{
				switch ((int)messageType)
				{
				case 0:
					GlobalMessage(content, playerName, clanName);
					break;
				case 4:
					LocalMessage(content, playerName, clanName);
					break;
				case 2:
					ClanMessage(content, playerName, clanName);
					break;
				case 3:
					WhisperMessage(content, playerName, targetName, clanName);
					break;
				case 1:
					break;
				}
			}
		}

		public static void HandleLoginMessage(string playerName, string clanName)
		{
			if (string.IsNullOrEmpty(playerName) || !_isRunning)
			{
				return;
			}
			string text = Settings.Get<string>("LoginMessageFormat");
			if (!string.IsNullOrEmpty(text))
			{
				string content = text.Replace("{playerName}", playerName).Replace("{clanName}", clanName);
				if (Settings.Get<bool>("AdminLoginMessages"))
				{
					AddToQueue(AdminWebHookUrl, content);
				}
				if (Settings.Get<bool>("PublicLoginMessages"))
				{
					AddToQueue(PublicWebHookUrl, content);
				}
				if (!string.IsNullOrEmpty(clanName) && Settings.Get<bool>("ClanLoginMessages") && ClanWebHookUrls.TryGetValue(clanName, out var value))
				{
					AddToQueue(value, content);
				}
				AddToQueue(LoginWebhookURL, content);
			}
		}

		public static void HandleLogoutMessage(string playerName, string clanName)
		{
			if (string.IsNullOrEmpty(playerName) || !_isRunning)
			{
				return;
			}
			string text = Settings.Get<string>("LogoutMessageFormat");
			if (!string.IsNullOrEmpty(text))
			{
				string content = text.Replace("{playerName}", playerName).Replace("{clanName}", clanName);
				if (Settings.Get<bool>("AdminLoginMessages"))
				{
					AddToQueue(AdminWebHookUrl, content);
				}
				if (Settings.Get<bool>("PublicLoginMessages"))
				{
					AddToQueue(PublicWebHookUrl, content);
				}
				if (!string.IsNullOrEmpty(clanName) && Settings.Get<bool>("ClanLoginMessages") && ClanWebHookUrls.TryGetValue(clanName, out var value))
				{
					AddToQueue(value, content);
				}
				AddToQueue(LoginWebhookURL, content);
			}
		}

		public static void GlobalMessage(string content, string playerName, string clanName)
		{
			SendMessage(content, playerName, clanName, "GlobalPrefix", "AdminGlobalMessages", "PublicGlobalMessages");
		}

		public static void LocalMessage(string content, string playerName, string clanName)
		{
			SendMessage(content, playerName, clanName, "LocalPrefix", "AdminLocalMessages", "PublicLocalMessages");
		}

		public static void ClanMessage(string content, string playerName, string clanName)
		{
			if (!string.IsNullOrEmpty(clanName))
			{
				string text = BuildPrefix("ClanPrefix", playerName, clanName);
				if (Settings.Get<bool>("AdminClanMessages"))
				{
					AddToQueue(AdminWebHookUrl, text + " " + content);
				}
				if (Settings.Get<bool>("PublicClanMessages"))
				{
					AddToQueue(PublicWebHookUrl, text + " " + content);
				}
				if (ClanWebHookUrls.TryGetValue(clanName, out var value))
				{
					AddToQueue(value, text + " " + content);
				}
			}
		}

		public static void WhisperMessage(string content, string playerName, string targetName, string clanName)
		{
			string text = Settings.Get<string>("WhisperPrefix").Replace("{playerName}", playerName).Replace("{targetName}", targetName ?? "")
				.Replace("{clanName}", clanName ?? "");
			if (Settings.Get<bool>("AdminWhisperMessages"))
			{
				AddToQueue(AdminWebHookUrl, text + " " + content);
			}
			if (Settings.Get<bool>("PublicWhisperMessages"))
			{
				AddToQueue(PublicWebHookUrl, text + " " + content);
			}
		}

		private static void SendMessage(string content, string playerName, string clanName, string prefixKey, string adminSettingKey, string publicSettingKey)
		{
			string text = BuildPrefix(prefixKey, playerName, clanName);
			if (Settings.Get<bool>(adminSettingKey))
			{
				AddToQueue(AdminWebHookUrl, text + " " + content);
			}
			if (Settings.Get<bool>(publicSettingKey))
			{
				AddToQueue(PublicWebHookUrl, text + " " + content);
			}
		}

		private static string BuildPrefix(string prefixKey, string playerName, string clanName)
		{
			return Settings.Get<string>(prefixKey).Replace("{playerName}", playerName).Replace("{clanName}", clanName ?? "");
		}

		public static void AddToQueue(string webhookUrl, string content)
		{
			if (!string.IsNullOrEmpty(webhookUrl) && IsValidUrl(webhookUrl))
			{
				Queue<string> orAdd = MessageQueues.GetOrAdd(webhookUrl, (string _) => new Queue<string>());
				lock (orAdd)
				{
					orAdd.Enqueue(content);
				}
				LastUsed.TryAdd(webhookUrl, DateTime.MinValue);
			}
		}

		public static void AddClan(string clanName)
		{
			ClanWebHookUrls[clanName] = null;
			Database.Save<Dictionary<string, string>>("ClanWebHookUrls", ClanWebHookUrls);
		}

		public static void RemoveClan(string clanName)
		{
			ClanWebHookUrls.Remove(clanName);
			Database.Save<Dictionary<string, string>>("ClanWebHookUrls", ClanWebHookUrls);
		}

		public static async Task<bool> TrySendMessage(string content, string webhookUrl = null)
		{
			if (string.IsNullOrEmpty(webhookUrl) || !IsValidUrl(webhookUrl))
			{
				Console.WriteLine("Invalid webhook url.");
				return false;
			}
			var payload = new { content };
			string json = JsonSerializer.Serialize(payload);
			StringContent contentData = new StringContent(json, Encoding.UTF8, "application/json");
			try
			{
				HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, webhookUrl)
				{
					Content = contentData
				};
				HttpResponseMessage response = await _httpClient.SendAsync(request);
				if (!response.IsSuccessStatusCode)
				{
					string errorContent = await response.Content.ReadAsStringAsync();
					Console.WriteLine($"Failed to send webhook. Status: {response.StatusCode}, Content: {errorContent}");
					return false;
				}
				return true;
			}
			catch (Exception ex)
			{
				Console.WriteLine("Exception while sending webhook message: " + ex.Message);
				return false;
			}
		}

		private static async Task ProcessQueueLoop(CancellationToken token)
		{
			while (_isRunning && !token.IsCancellationRequested)
			{
				foreach (KeyValuePair<string, Queue<string>> kvp in MessageQueues)
				{
					string webhookUrl = kvp.Key;
					Queue<string> queue = kvp.Value;
					DateTime lastUsed = LastUsed.GetOrAdd(webhookUrl, DateTime.MinValue);
					if (!((DateTime.Now - lastUsed).TotalSeconds >= (double)MessageInterval))
					{
						continue;
					}
					int itemsToDequeue = 1;
					string content;
					lock (queue)
					{
						if (queue.Count == 0)
						{
							continue;
						}
						if (EnableBatching)
						{
							content = string.Join("\n", queue);
							itemsToDequeue = queue.Count;
						}
						else
						{
							content = queue.Peek();
						}
						goto IL_016b;
					}
					IL_016b:
					int value;
					Queue<string> value2;
					if (await TrySendMessage(content, webhookUrl))
					{
						lock (queue)
						{
							int i = 0;
							while (i < itemsToDequeue && queue.Count > 0)
							{
								queue.Dequeue();
								value = i++;
							}
						}
						if (queue.Count == 0)
						{
							MessageQueues.TryRemove(webhookUrl, out value2);
						}
						ConsecutiveFailures[webhookUrl] = 0;
						LastUsed[webhookUrl] = DateTime.Now;
					}
					else
					{
						int failures = ConsecutiveFailures.AddOrUpdate(webhookUrl, 1, (string _, int count) => count + 1);
						LastUsed[webhookUrl] = DateTime.Now.AddSeconds(OnFailInterval);
						if (failures >= 5)
						{
							Console.WriteLine($"[Webhook Error] Discarding queue for {webhookUrl} after {failures} failures.");
							MessageQueues.TryRemove(webhookUrl, out value2);
							ConsecutiveFailures.TryRemove(webhookUrl, out value);
						}
					}
					content = null;
				}
				try
				{
					await Task.Delay(TimeSpan.FromSeconds(MessageInterval), token);
				}
				catch (TaskCanceledException ex)
				{
					Console.WriteLine("[Webhook Error] Task was canceled: " + ex.Message);
					break;
				}
			}
		}

		public static bool IsValidUrl(string url)
		{
			if (string.IsNullOrWhiteSpace(url))
			{
				return false;
			}
			return Regex.IsMatch(url, "^https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&\\/=]*)$");
		}
	}
}
namespace ScarletHooks.Data
{
	[StructLayout(LayoutKind.Sequential, Size = 1)]
	public readonly struct VBloods
	{
		public static readonly Dictionary<int, string> Names = new Dictionary<int, string>
		{
			{ 1106149033, "Grayson the Armourer" },
			{ 2122229952, "Rufus the Foreman" },
			{ -2025101517, "Errol the Stonebreaker" },
			{ 1112948824, "Lord Styx the Night Champion" },
			{ -1936575244, "Gorecrusher the Behemoth" },
			{ -910296704, "Matka the Curse Weaver" },
			{ -203043163, "Albert the Duke of Balaton" },
			{ -1905691330, "Alpha the White Wolf" },
			{ -1065970933, "Terah the Geomancer" },
			{ -2013903325, "Mairwyn the Elementalist" },
			{ 1896428751, "Clive the Firestarter" },
			{ -1659822956, "Quincey the Bandit King" },
			{ -548489519, "Ungora the Spider Queen" },
			{ 685266977, "Morian the Stormwing Matriarch" },
			{ -393555055, "Talzur the Winged Horror" },
			{ 939467639, "Leandra the Shadow Priestess" },
			{ 326378955, "Cyril the Cursed Smith" },
			{ -680831417, "Raziel the Shepherd" },
			{ -29797003, "Vincent the Frostbringer" },
			{ 1688478381, "Octavian the Militia Captain" },
			{ 850622034, "Meredith the Bright Archer" },
			{ 153390636, "Nicholaus the Fallen" },
			{ 577478542, "Goreswine the Ravager" },
			{ 613251918, "Bane the Shadowblade" },
			{ -1968372384, "Jade the Vampire Hunter" },
			{ 24378719, "Frostmaw the Mountain Terror" },
			{ -1347412392, "Terrorclaw the Ogre" },
			{ -1365931036, "Kriig the Undead General" },
			{ -1208888966, "Foulrot the Soultaker" },
			{ -2039908510, "Putrid Rat" },
			{ -1449631170, "Tristan the Vampire Hunter" },
			{ 109969450, "Ben the Old Wanderer" },
			{ -1942352521, "Beatrice the Tailor" },
			{ 1124739990, "Keely the Frost Archer" },
			{ 763273073, "Lidia the Chaos Archer" },
			{ -2122682556, "Finn the Fisherman" },
			{ 114912615, "Azariel the Sunbringer" },
			{ -26105228, "Sir Magnus the Overseer" },
			{ 192051202, "Baron du Bouchon the Sommelier" },
			{ 1233988687, "Adam the Firstborn" },
			{ -740796338, "Solarus the Immaculate" },
			{ -1391546313, "Kodia the Ferocious Bear" },
			{ 172235178, "Ziva the Engineer" },
			{ 814083983, "Henry Blackbrew the Doctor" },
			{ -1101874342, "Domina the Blade Dancer" },
			{ 910988233, "Grethel the Glassblower" },
			{ -99012450, "Christina the Sun Priestess" },
			{ 106480588, "Angram the Purifier" },
			{ 2054432370, "Voltatia the Power Master" },
			{ 1945956671, "Maja the Dark Savant" },
			{ 495971434, "General Valencia the Depraved" },
			{ 591725925, "Megara the Serpent Queen" },
			{ -327335305, "Dracula the Immortal King" },
			{ -496360395, "General Cassius the Betrayer" },
			{ 795262842, "General Elena the Hollow" },
			{ -1505705712, "Willfred the Village Elder" },
			{ -484556888, "Polora the Feywalker" },
			{ 336560131, "Simon Belmont the Vampire Hunter" },
			{ -1383529374, "Jakira the Shadow Huntress" },
			{ 619948378, "Sir Erwin the Gallant Cavalier" },
			{ -753453016, "Gaius the Cursed Champion" },
			{ -1669199769, "Stavros the Carver" },
			{ 173259239, "Dantos the Forgebinder" },
			{ 1295855316, "Lucile the Venom Alchemist" }
		};
	}
}
namespace ScarletHooks.Commands
{
	[CommandGroup("hooks", null)]
	public static class AdminCommands
	{
		private static Settings Settings => Plugin.Settings;

		[Command("add", null, null, "Add a new clan to the list. The clan name is case-sensitive. If you are unsure, use 'hooks afp' to add from the player's name.", null, true)]
		public static void Add(ChatCommandContext ctx, string clanName)
		{
			if (string.IsNullOrEmpty(clanName))
			{
				ctx.Reply(RichTextFormatter.FormatError("You must provide a clan name."));
			}
			else if (MessageDispatchSystem.ClanWebHookUrls.ContainsKey(clanName))
			{
				ctx.Reply(RichTextFormatter.FormatError("This clan already exists, please go to the config file to change the webhook url."));
				ctx.Reply(RichTextFormatter.FormatError("Don't forget to reload the webhooks after changing the webhook url."));
			}
			else
			{
				MessageDispatchSystem.AddClan(clanName);
				ctx.Reply(RichTextFormatter.Format("Clan ~" + clanName + "~ added to the list of clans, please go to the config file to set the webhook url.", (List<string>)null));
				ctx.Reply(RichTextFormatter.Format("Don't forget to reload the webhooks after changing the webhook url.", (List<string>)null));
			}
		}

		[Command("afp", null, null, null, null, true)]
		public static void AddFromPlayer(ChatCommandContext ctx, string playerName)
		{
			PlayerData val = default(PlayerData);
			if (!PlayerService.TryGetByName(playerName, ref val) || string.IsNullOrEmpty(val.ClanName))
			{
				ctx.Reply(RichTextFormatter.FormatError("Player '" + playerName + "' not found or does not belong to a clan."));
				return;
			}
			string clanName = val.ClanName;
			if (MessageDispatchSystem.ClanWebHookUrls.ContainsKey(clanName))
			{
				ctx.Reply(RichTextFormatter.FormatError("This clan already exists, please go to the config file to change the webhook url."));
				ctx.Reply(RichTextFormatter.FormatError("Don't forget to reload the webhooks after changing the webhook url."));
				return;
			}
			MessageDispatchSystem.AddClan(clanName);
			ctx.Reply(RichTextFormatter.Format($"Clan ~{clanName}~ (from player '{playerName}') added to the list of clans, please go to the config file to set the webhook url.", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format("Don't forget to reload the webhooks after changing the webhook url.", (List<string>)null));
		}

		[Command("remove", null, null, null, null, true)]
		public static void Remove(ChatCommandContext ctx, string name)
		{
			string text = null;
			PlayerData val = default(PlayerData);
			if (PlayerService.TryGetByName(name, ref val))
			{
				text = val.ClanName;
			}
			if (string.IsNullOrEmpty(text))
			{
				text = name;
			}
			if (!string.IsNullOrEmpty(text))
			{
				if (!MessageDispatchSystem.ClanWebHookUrls.ContainsKey(text))
				{
					ctx.Reply(RichTextFormatter.FormatError("No clan named ~" + text + "~ is loaded."));
					return;
				}
				MessageDispatchSystem.RemoveClan(text);
				ctx.Reply(RichTextFormatter.Format("Clan ~" + text + "~ removed from the list of clans.", (List<string>)null));
			}
		}

		[Command("reload settings", null, null, "Reload all settings.", null, true)]
		public static void RealodSettings(ChatCommandContext ctx)
		{
			Plugin.ReloadSettings();
			ctx.Reply(RichTextFormatter.Format("~Settings reloaded.~", (List<string>)null));
		}

		[Command("reload webhooks", null, null, "Reload all webhooks.", null, true)]
		public static void RealodWebhooks(ChatCommandContext ctx)
		{
			MessageDispatchSystem.LoadFromFile();
			ctx.Reply(RichTextFormatter.Format("~Webhooks reloaded.~", (List<string>)null));
		}

		[Command("reload", null, null, "Reload all settings and webhooks.", null, true)]
		public static void Realod(ChatCommandContext ctx)
		{
			Plugin.ReloadSettings();
			MessageDispatchSystem.LoadFromFile();
			ctx.Reply(RichTextFormatter.Format("~Settings and webhooks reloaded.~", (List<string>)null));
		}

		[Command("list", null, null, "List all webhooks.", null, true)]
		public static void ListClanWebHookUrls(ChatCommandContext ctx)
		{
			ctx.Reply(RichTextFormatter.Format("~Admin~: " + Settings.Get<string>("AdminWebhookURL") + ".", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format("~Public~: " + Settings.Get<string>("PublicWebhookURL") + ".", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format("List of clans webhooks: ", (List<string>)null));
			foreach (var (value, value2) in MessageDispatchSystem.ClanWebHookUrls)
			{
				ctx.Reply(RichTextFormatter.Format($"~{value}~: {value2}.", (List<string>)null));
			}
			ctx.Reply(RichTextFormatter.Format($"Total webhook urls: ~{MessageDispatchSystem.ClanWebHookUrls.Count + 2}~.", (List<string>)null));
		}

		[Command("settings", null, null, null, null, true)]
		public static void ChangeSettings(ChatCommandContext ctx, string settings, bool value)
		{
			if (!Settings.Has(settings))
			{
				ctx.Reply(RichTextFormatter.Format("~" + settings + "~ does not exist.", (List<string>)null));
				return;
			}
			List<string> list = new List<string>(4) { "AdminWebhookURL", "PublicWebhookURL", "MessageInterval", "OnFailInterval" };
			if (list.Contains(settings))
			{
				ctx.Reply(RichTextFormatter.Format("~" + settings + "~ cannot be changed via command.", (List<string>)null));
				return;
			}
			Settings.Set<bool>(settings, value);
			ctx.Reply(RichTextFormatter.Format($"~{settings}~ changed to ~{value}~.", (List<string>)null));
		}

		[Command("settings", null, null, null, null, true)]
		public static void ShowSettings(ChatCommandContext ctx)
		{
			ctx.Reply(RichTextFormatter.Format("~Current settings:~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format("AdminWebhookURL: ~" + Settings.Get<string>("AdminWebhookURL") + "~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format("PublicWebhookURL: ~" + Settings.Get<string>("PublicWebhookURL") + "~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"MessageInterval: ~{Settings.Get<float>("MessageInterval")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"OnFailInterval: ~{Settings.Get<float>("OnFailInterval")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"EnableBatching: ~{Settings.Get<bool>("EnableBatching")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"AdminGlobalMessages: ~{Settings.Get<bool>("AdminGlobalMessages")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"AdminClanMessages: ~{Settings.Get<bool>("AdminClanMessages")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"AdminLocalMessages: ~{Settings.Get<bool>("AdminLocalMessages")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"AdminWhisperMessages: ~{Settings.Get<bool>("AdminWhisperMessages")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"PublicGlobalMessages: ~{Settings.Get<bool>("PublicGlobalMessages")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"PublicClanMessages: ~{Settings.Get<bool>("PublicClanMessages")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"PublicLocalMessages: ~{Settings.Get<bool>("PublicLocalMessages")}~", (List<string>)null));
			ctx.Reply(RichTextFormatter.Format($"PublicWhisperMessages: ~{Settings.Get<bool>("PublicWhisperMessages")}~", (List<string>)null));
		}

		[Command("start", null, null, null, null, true)]
		public static void Start(ChatCommandContext ctx)
		{
			MessageDispatchSystem.Initialize();
			ctx.Reply(RichTextFormatter.Format("~Message dispatch system started.~", (List<string>)null));
		}

		[Command("stop", null, null, null, null, true)]
		public static void Stop(ChatCommandContext ctx)
		{
			MessageDispatchSystem.Shutdown();
			ctx.Reply(RichTextFormatter.Format("~Message dispatch system stopped.~", (List<string>)null));
		}

		[Command("forcestop", null, null, null, null, true)]
		public static void ForceStop(ChatCommandContext ctx)
		{
			MessageDispatchSystem.ForceShutdown();
			ctx.Reply(RichTextFormatter.Format("~Message dispatch system stopped and cleared all cache.~", (List<string>)null));
		}
	}
	[RconCommandCategory("ScarletHooks")]
	public class AdminRconCommands
	{
		private static Settings Settings => Plugin.Settings;

		[RconCommand("add", "Add a new clan to the list. The clan name is case-sensitive. If you are unsure, use 'scarlethooks.afp' to add from the player's name.", null)]
		public static string Add(string clanName)
		{
			if (string.IsNullOrEmpty(clanName))
			{
				return "You must provide a clan name.";
			}
			if (MessageDispatchSystem.ClanWebHookUrls.ContainsKey(clanName))
			{
				return "This clan already exists, please use the 'scarlethooks.setclanwebhook' command to change the webhook url.";
			}
			MessageDispatchSystem.AddClan(clanName);
			return "Clan " + clanName + " added to the list of clans, please use the 'scarlethooks.setclanwebhook' command to set the webhook url.";
		}

		[RconCommand("afp", "Add a clan to the list using a player's name.", null)]
		public static string AddFromPlayer(string playerName)
		{
			PlayerData val = default(PlayerData);
			if (!PlayerService.TryGetByName(playerName, ref val) || string.IsNullOrEmpty(val.ClanName))
			{
				return "Player '" + playerName + "' not found or does not belong to a clan.";
			}
			string clanName = val.ClanName;
			if (MessageDispatchSystem.ClanWebHookUrls.ContainsKey(clanName))
			{
				return "This clan already exists, please use the 'scarlethooks.setclanwebhook' command to change the webhook url.";
			}
			MessageDispatchSystem.AddClan(clanName);
			return $"Clan {clanName} (from player '{playerName}') added to the list of clans, please use the 'scarlethooks.setclanwebhook' command to set the webhook url.";
		}

		[RconCommand("remove", "Remove a clan from the list by clan or player name.", null)]
		public static string Remove(string name)
		{
			string text = null;
			PlayerData val = default(PlayerData);
			if (PlayerService.TryGetByName(name, ref val))
			{
				text = val.ClanName;
			}
			if (string.IsNullOrEmpty(text))
			{
				text = name;
			}
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			if (!MessageDispatchSystem.ClanWebHookUrls.ContainsKey(text))
			{
				return "No clan named " + text + " is loaded.";
			}
			MessageDispatchSystem.RemoveClan(text);
			return "Clan " + text + " removed from the list of clans.";
		}

		[RconCommand("reload settings", "Reload the settings from the configuration file.", null)]
		public static string ReloadSettings()
		{
			Plugin.ReloadSettings();
			return "Settings reloaded.";
		}

		[RconCommand("reload webhooks", "Reload the webhooks from the configuration file.", null)]
		public static string ReloadWebhooks()
		{
			MessageDispatchSystem.LoadFromFile();
			return "Webhooks reloaded.";
		}

		[RconCommand("reload", "Reload both settings and webhooks from the configuration file.", null)]
		public static string Reload()
		{
			Plugin.ReloadSettings();
			MessageDispatchSystem.LoadFromFile();
			return "Settings and webhooks reloaded.";
		}

		[RconCommand("list", "List all configured webhook URLs.", null)]
		public static string ListClanWebHookUrls()
		{
			StringBuilder stringBuilder = new StringBuilder();
			StringBuilder stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder3 = stringBuilder2;
			StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2);
			handler.AppendLiteral("Admin: ");
			handler.AppendFormatted(Settings.Get<string>("AdminWebhookURL"));
			handler.AppendLiteral(".");
			stringBuilder3.AppendLine(ref handler);
			stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder4 = stringBuilder2;
			handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
			handler.AppendLiteral("Public: ");
			handler.AppendFormatted(Settings.Get<string>("PublicWebhookURL"));
			handler.AppendLiteral(".");
			stringBuilder4.AppendLine(ref handler);
			stringBuilder.AppendLine("List of clans webhooks:");
			foreach (KeyValuePair<string, string> clanWebHookUrl in MessageDispatchSystem.ClanWebHookUrls)
			{
				clanWebHookUrl.Deconstruct(out var key, out var value);
				string value2 = key;
				string value3 = value;
				stringBuilder2 = stringBuilder;
				StringBuilder stringBuilder5 = stringBuilder2;
				handler = new StringBuilder.AppendInterpolatedStringHandler(3, 2, stringBuilder2);
				handler.AppendFormatted(value2);
				handler.AppendLiteral(": ");
				handler.AppendFormatted(value3);
				handler.AppendLiteral(".");
				stringBuilder5.AppendLine(ref handler);
			}
			stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder6 = stringBuilder2;
			handler = new StringBuilder.AppendInterpolatedStringHandler(21, 1, stringBuilder2);
			handler.AppendLiteral("Total webhook urls: ");
			handler.AppendFormatted(MessageDispatchSystem.ClanWebHookUrls.Count + 2);
			handler.AppendLiteral(".");
			stringBuilder6.AppendLine(ref handler);
			return stringBuilder.ToString();
		}

		[RconCommand("settings", "Change a boolean setting.", null)]
		public static string ChangeSettings(string settings, bool value)
		{
			if (!Settings.Has(settings))
			{
				return settings + " does not exist.";
			}
			List<string> list = new List<string>(4) { "AdminWebhookURL", "PublicWebhookURL", "MessageInterval", "OnFailInterval" };
			if (list.Contains(settings))
			{
				return settings + " cannot be changed via command.";
			}
			Settings.Set<bool>(settings, value);
			return $"{settings} changed to {value}.";
		}

		[RconCommand("settings", "Show all current settings.", null)]
		public static string ShowSettings()
		{
			string text = "Current settings:";
			text = text + "AdminWebhookURL: " + Settings.Get<string>("AdminWebhookURL") + "\n";
			text = text + "PublicWebhookURL: " + Settings.Get<string>("PublicWebhookURL") + "\n";
			text += $"MessageInterval: {Settings.Get<float>("MessageInterval")}\n";
			text += $"OnFailInterval: {Settings.Get<float>("OnFailInterval")}\n";
			text += $"EnableBatching: {Settings.Get<bool>("EnableBatching")}\n";
			text += $"AdminGlobalMessages: {Settings.Get<bool>("AdminGlobalMessages")}\n";
			text += $"AdminClanMessages: {Settings.Get<bool>("AdminClanMessages")}\n";
			text += $"AdminLocalMessages: {Settings.Get<bool>("AdminLocalMessages")}\n";
			text += $"AdminWhisperMessages: {Settings.Get<bool>("AdminWhisperMessages")}\n";
			text += $"PublicGlobalMessages: {Settings.Get<bool>("PublicGlobalMessages")}\n";
			text += $"PublicClanMessages: {Settings.Get<bool>("PublicClanMessages")}\n";
			text += $"PublicLocalMessages: {Settings.Get<bool>("PublicLocalMessages")}\n";
			return text + $"PublicWhisperMessages: {Settings.Get<bool>("PublicWhisperMessages")}\n";
		}

		[RconCommand("start", "Start the message dispatch system.", null)]
		public static string Start()
		{
			MessageDispatchSystem.Initialize();
			return "Message dispatch system started.";
		}

		[RconCommand("stop", "Stop the message dispatch system.", null)]
		public static string Stop()
		{
			MessageDispatchSystem.Shutdown();
			return "Message dispatch system stopped.";
		}

		[RconCommand("forcestop", "Force stop the message dispatch system and clear all cache.", null)]
		public static string ForceStop()
		{
			MessageDispatchSystem.ForceShutdown();
			return "Message dispatch system stopped and cleared all cache.";
		}

		[RconCommand("setadminwebhook", "Set the admin webhook URL.", null)]
		public static string SetAdminWebhook(string url)
		{
			if (string.IsNullOrEmpty(url))
			{
				return "You must provide a valid URL.";
			}
			Settings.Set<string>("AdminWebhookURL", url);
			return "Admin webhook URL set to: " + url;
		}

		[RconCommand("setpublicwebhook", "Set the public webhook URL.", null)]
		public static string SetPublicWebhook(string url)
		{
			if (string.IsNullOrEmpty(url))
			{
				return "You must provide a valid URL.";
			}
			Settings.Set<string>("PublicWebhookURL", url);
			return "Public webhook URL set to: " + url;
		}

		[RconCommand("setloginwebhook", "Set the login webhook URL.", null)]
		public static string SetLoginWebhook(string url)
		{
			if (string.IsNullOrEmpty(url))
			{
				return "You must provide a valid URL.";
			}
			Settings.Set<string>("LoginWebhookURL", url);
			return "Login webhook URL set to: " + url;
		}

		[RconCommand("setclanwebhook", "Set the webhook URL for a specific clan.", null)]
		public static string SetClanWebhook(string clanName, string url)
		{
			if (string.IsNullOrEmpty(clanName) || string.IsNullOrEmpty(url))
			{
				return "You must provide a clan name and a valid URL.";
			}
			if (!MessageDispatchSystem.ClanWebHookUrls.ContainsKey(clanName))
			{
				return "Clan " + clanName + " does not exist. Use the 'add' command to add it first.";
			}
			MessageDispatchSystem.ClanWebHookUrls[clanName] = url;
			Plugin.Database.Save<Dictionary<string, string>>("ClanWebHookUrls", MessageDispatchSystem.ClanWebHookUrls);
			return "Webhook URL for clan " + clanName + " set to: " + url;
		}
	}
}
namespace ScarletRCON.Shared
{
	[AttributeUsage(AttributeTargets.Class)]
	public class RconCommandCategoryAttribute : Attribute
	{
		public string Name { get; }

		public RconCommandCategoryAttribute(string categoryName)
		{
			Name = categoryName;
			base..ctor();
		}
	}
	[AttributeUsage(AttributeTargets.Method)]
	public class RconCommandAttribute : Attribute
	{
		public string Name { get; set; }

		public string? Description { get; set; }

		public string? Usage { get; set; }

		public RconCommandAttribute(string name, string? description = null, string? usage = null)
		{
			Name = name;
			Description = description;
			Usage = usage;
			base..ctor();
		}
	}
	public static class RconCommandRegistrar
	{
		private static bool? _isScarletRconAvailable;

		public static bool IsScarletRconAvailable()
		{
			bool valueOrDefault = _isScarletRconAvailable.GetValueOrDefault();
			if (!_isScarletRconAvailable.HasValue)
			{
				valueOrDefault = Type.GetType("ScarletRCON.CommandSystem.CommandHandler, ScarletRCON") != null;
				_isScarletRconAvailable = valueOrDefault;
			}
			return _isScarletRconAvailable.Value;
		}

		public static void RegisterAll()
		{
			if (!IsScarletRconAvailable())
			{
				return;
			}
			Assembly callingAssembly = Assembly.GetCallingAssembly();
			string item = callingAssembly.GetName().Name.ToLowerInvariant() + ".";
			List<(string, string, MethodInfo, string, string, string)> list = new List<(string, string, MethodInfo, string, string, string)>();
			Type[] types = callingAssembly.GetTypes();
			foreach (Type type in types)
			{
				string item2 = type.GetCustomAttribute<RconCommandCategoryAttribute>()?.Name ?? "Uncategorized";
				MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					RconCommandAttribute customAttribute = methodInfo.GetCustomAttribute<RconCommandAttribute>();
					if (customAttribute == null)
					{
						continue;
					}
					string text = customAttribute.Usage;
					if (string.IsNullOrWhiteSpace(text))
					{
						ParameterInfo[] parameters = methodInfo.GetParameters();
						text = ((parameters.Length == 0) ? "" : string.Join(" ", parameters.Select((ParameterInfo p) => (p.ParameterType == typeof(List<string>)) ? "<...text>" : ("<" + p.Name + ">"))));
					}
					list.Add((item2, item, methodInfo, customAttribute.Name, customAttribute.Description, text));
				}
			}
			Type type2 = Type.GetType("ScarletRCON.CommandSystem.CommandHandler, ScarletRCON");
			MethodInfo method = type2.GetMethod("RegisterExternalCommandsBatch", BindingFlags.Static | BindingFlags.Public);
			if (type2 == null)
			{
				throw new InvalidOperationException("ScarletRCON.CommandSystem.CommandHandler not found.");
			}
			if (method == null)
			{
				throw new InvalidOperationException("ScarletRCON.CommandSystem.CommandHandler.RegisterExternalCommandsBatch not found.");
			}
			method.Invoke(null, new object[1] { list });
		}

		public static void UnregisterAssembly(Assembly? assembly = null)
		{
			if (IsScarletRconAvailable())
			{
				if ((object)assembly == null)
				{
					assembly = Assembly.GetCallingAssembly();
				}
				Type type = Type.GetType("ScarletRCON.CommandSystem.CommandHandler, ScarletRCON");
				MethodInfo methodInfo = type?.GetMethod("UnregisterAssembly", BindingFlags.Static | BindingFlags.Public);
				if (type == null)
				{
					throw new InvalidOperationException("ScarletRCON.CommandSystem.CommandHandler not found.");
				}
				if (methodInfo == null)
				{
					throw new InvalidOperationException("ScarletRCON.CommandSystem.CommandHandler.UnregisterAssembly not found.");
				}
				methodInfo.Invoke(null, new object[1] { assembly });
			}
		}
	}
}