diff --git a/CS2-SimpleAdmin/Api/CS2_SimpleAdminApi.cs b/CS2-SimpleAdmin/Api/CS2_SimpleAdminApi.cs index 2a960a6..1d6e0e7 100644 --- a/CS2-SimpleAdmin/Api/CS2_SimpleAdminApi.cs +++ b/CS2-SimpleAdmin/Api/CS2_SimpleAdminApi.cs @@ -82,4 +82,9 @@ public void LogCommand(CCSPlayerController? caller, CommandInfo command) { Helper.LogCommand(caller, command); } + + public bool IsAdminSilent(CCSPlayerController player) + { + return CS2_SimpleAdmin.SilentPlayers.Contains(player.Slot); + } } \ No newline at end of file diff --git a/CS2-SimpleAdmin/CS2-SimpleAdmin.cs b/CS2-SimpleAdmin/CS2-SimpleAdmin.cs index 386384b..5a6454c 100644 --- a/CS2-SimpleAdmin/CS2-SimpleAdmin.cs +++ b/CS2-SimpleAdmin/CS2-SimpleAdmin.cs @@ -11,7 +11,7 @@ namespace CS2_SimpleAdmin; -[MinimumApiVersion(284)] +[MinimumApiVersion(286)] public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig { internal static CS2_SimpleAdmin Instance { get; private set; } = new(); @@ -19,7 +19,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)"); public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)"; public override string ModuleAuthor => "daffyy & Dliix66"; - public override string ModuleVersion => "1.6.8a"; + public override string ModuleVersion => "1.6.9a"; public override void Load(bool hotReload) { @@ -36,10 +36,11 @@ public override void Load(bool hotReload) AddTimer(2.0f, () => { if (Database == null) return; + + var playerManager = new PlayerManager(); Helper.GetValidPlayers().ForEach(player => { - var playerManager = new PlayerManager(); playerManager.LoadPlayerData(player); }); }); @@ -56,9 +57,14 @@ public override void OnAllPluginsLoaded(bool hotReload) { AddTimer(3.0f, () => ReloadAdmins(null)); - MenuApi = MenuCapability.Get(); - if (MenuApi == null) - Logger.LogError("MenuManager Core not found..."); + try + { + MenuApi = MenuCapability.Get(); + } + catch (Exception ex) + { + Logger.LogError("Unable to load required plugins ... \n{exception}", ex.Message); + } RegisterCommands.InitializeCommands(); } @@ -88,9 +94,11 @@ public void OnConfigParsed(CS2_SimpleAdminConfig config) DbConnectionString = builder.ConnectionString; Database = new Database.Database(DbConnectionString); - if (!Database.CheckDatabaseConnection()) + if (!Database.CheckDatabaseConnection(out var exception)) { - Logger.LogError("Unable connect to database!"); + if (exception != null) + Logger.LogError("Problem with database connection! \n{exception}", exception); + Unload(false); return; } diff --git a/CS2-SimpleAdmin/CS2-SimpleAdmin.csproj b/CS2-SimpleAdmin/CS2-SimpleAdmin.csproj index f89f803..58512e8 100644 --- a/CS2-SimpleAdmin/CS2-SimpleAdmin.csproj +++ b/CS2-SimpleAdmin/CS2-SimpleAdmin.csproj @@ -10,9 +10,9 @@ - + - + diff --git a/CS2-SimpleAdmin/Commands/basebans.cs b/CS2-SimpleAdmin/Commands/basebans.cs index 7262eb8..31382f4 100644 --- a/CS2-SimpleAdmin/Commands/basebans.cs +++ b/CS2-SimpleAdmin/Commands/basebans.cs @@ -171,6 +171,8 @@ public void OnAddBanCommand(CCSPlayerController? caller, CommandInfo command) { await BanManager.AddBanBySteamid(steamid, adminInfo, reason, time); }); + + Helper.SendDiscordPenaltyMessage(caller, steamid, reason, time, PenaltyType.Ban, _localizer); command.ReplyToCommand($"Player with steamid {steamid} is not online. Ban has been added offline."); } diff --git a/CS2-SimpleAdmin/Commands/basecommands.cs b/CS2-SimpleAdmin/Commands/basecommands.cs index d307195..deb77db 100644 --- a/CS2-SimpleAdmin/Commands/basecommands.cs +++ b/CS2-SimpleAdmin/Commands/basecommands.cs @@ -375,14 +375,14 @@ public void ReloadAdmins(CCSPlayerController? caller) var adminsFile = await File.ReadAllTextAsync(Instance.ModuleDirectory + "/data/admins.json"); var groupsFile = await File.ReadAllTextAsync(Instance.ModuleDirectory + "/data/groups.json"); - await Server.NextFrameAsync(() => + await Server.NextWorldUpdateAsync(() => { if (!string.IsNullOrEmpty(adminsFile)) - AddTimer(0.5f, () => AdminManager.LoadAdminData(ModuleDirectory + "/data/admins.json")); + AddTimer(1.0f, () => AdminManager.LoadAdminData(ModuleDirectory + "/data/admins.json")); if (!string.IsNullOrEmpty(groupsFile)) - AddTimer(1.0f, () => AdminManager.LoadAdminGroups(ModuleDirectory + "/data/groups.json")); + AddTimer(1.5f, () => AdminManager.LoadAdminGroups(ModuleDirectory + "/data/groups.json")); if (!string.IsNullOrEmpty(adminsFile)) - AddTimer(1.5f, () => AdminManager.LoadAdminData(ModuleDirectory + "/data/admins.json")); + AddTimer(2.0f, () => AdminManager.LoadAdminData(ModuleDirectory + "/data/admins.json")); }); }); diff --git a/CS2-SimpleAdmin/Commands/basecomms.cs b/CS2-SimpleAdmin/Commands/basecomms.cs index bdd365c..16c73de 100644 --- a/CS2-SimpleAdmin/Commands/basecomms.cs +++ b/CS2-SimpleAdmin/Commands/basecomms.cs @@ -151,6 +151,8 @@ public void OnAddGagCommand(CCSPlayerController? caller, CommandInfo command) await MuteManager.AddMuteBySteamid(steamid, adminInfo, reason, time); }); + Helper.SendDiscordPenaltyMessage(caller, steamid, reason, time, PenaltyType.Gag, _localizer); + command.ReplyToCommand($"Player with steamid {steamid} is not online. Gag has been added offline."); } @@ -370,6 +372,8 @@ public void OnAddMuteCommand(CCSPlayerController? caller, CommandInfo command) await MuteManager.AddMuteBySteamid(steamid, adminInfo, reason, time, 1); }); + Helper.SendDiscordPenaltyMessage(caller, steamid, reason, time, PenaltyType.Mute, _localizer); + command.ReplyToCommand($"Player with steamid {steamid} is not online. Mute has been added offline."); } @@ -589,6 +593,8 @@ public void OnAddSilenceCommand(CCSPlayerController? caller, CommandInfo command await MuteManager.AddMuteBySteamid(steamid, adminInfo, reason, time, 2); }); + Helper.SendDiscordPenaltyMessage(caller, steamid, reason, time, PenaltyType.Silence, _localizer); + command.ReplyToCommand($"Player with steamid {steamid} is not online. Silence has been added offline."); } diff --git a/CS2-SimpleAdmin/Database/Database.cs b/CS2-SimpleAdmin/Database/Database.cs index 4b40545..bb7926f 100644 --- a/CS2-SimpleAdmin/Database/Database.cs +++ b/CS2-SimpleAdmin/Database/Database.cs @@ -41,16 +41,18 @@ public void DatabaseMigration() migrator.ExecuteMigrations(); } - public bool CheckDatabaseConnection() + public bool CheckDatabaseConnection(out string? exception) { using var connection = GetConnection(); - + exception = null; + try { return connection.Ping(); } - catch + catch (Exception ex) { + exception = ex.Message; return false; } } diff --git a/CS2-SimpleAdmin/Events.cs b/CS2-SimpleAdmin/Events.cs index 3e767dc..3418bb9 100644 --- a/CS2-SimpleAdmin/Events.cs +++ b/CS2-SimpleAdmin/Events.cs @@ -96,7 +96,7 @@ public HookResult OnClientDisconnect(EventPlayerDisconnect @event, GameEventInfo GravityPlayers.Remove(player); if (player.UserId.HasValue) - PlayersInfo.Remove(player.UserId.Value); + PlayersInfo.TryRemove(player.UserId.Value, out _); var authorizedSteamId = player.AuthorizedSteamID; if (authorizedSteamId == null || !PermissionManager.AdminCache.TryGetValue(authorizedSteamId, @@ -132,7 +132,7 @@ public HookResult OnPlayerFullConnect(EventPlayerConnectFull @event, GameEventIn public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) { #if DEBUG - Logger.LogCritical("[OnRoundEnd]"); + Logger.LogCritical("[OnRoundStart]"); #endif GodPlayers.Clear(); @@ -209,7 +209,9 @@ private HookResult ComamndListenerHandler(CCSPlayerController? player, CommandIn if (target == null || !target.IsValid || target.Connected != PlayerConnectedState.PlayerConnected) return HookResult.Continue; - return !AdminManager.CanPlayerTarget(player, target) ? HookResult.Stop : HookResult.Continue; + Logger.LogInformation($"{player.PlayerName} {AdminManager.GetPlayerImmunity(player).ToString()} probuje wywalic {target.PlayerName} {AdminManager.GetPlayerImmunity(target).ToString()}"); + + return !player.CanTarget(target) ? HookResult.Stop : HookResult.Continue; } } @@ -366,12 +368,15 @@ public HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info) { var player = @event.Userid; - if (player?.UserId == null || player.IsBot || player.Connected != PlayerConnectedState.PlayerConnected) + if (player?.UserId == null || !player.IsValid || player.IsHLTV || player.Connected != PlayerConnectedState.PlayerConnected) return HookResult.Continue; SpeedPlayers.Remove(player.Slot); GravityPlayers.Remove(player); + if (!PlayersInfo.ContainsKey(player.UserId.Value)) + return HookResult.Continue; + PlayersInfo[player.UserId.Value].DiePosition = new DiePosition( new Vector( player.PlayerPawn.Value?.AbsOrigin?.X ?? 0, diff --git a/CS2-SimpleAdmin/Helper.cs b/CS2-SimpleAdmin/Helper.cs index 82e58ec..57fa127 100644 --- a/CS2-SimpleAdmin/Helper.cs +++ b/CS2-SimpleAdmin/Helper.cs @@ -393,6 +393,102 @@ public static void SendDiscordPenaltyMessage(CCSPlayerController? caller, CCSPla var hostname = ConVar.Find("hostname")?.StringValue ?? localizer["sa_unknown"]; var colorHex = penaltySetting.FirstOrDefault(s => s.Name.Equals("Color"))?.Value ?? "#FFFFFF"; + if (string.IsNullOrEmpty(colorHex)) + colorHex = "#FFFFFF"; + + var embed = new Embed + { + Color = DiscordManager.ColorFromHex(colorHex), + Title = penalty switch + { + PenaltyType.Ban => localizer["sa_discord_penalty_ban"], + PenaltyType.Mute => localizer["sa_discord_penalty_mute"], + PenaltyType.Gag => localizer["sa_discord_penalty_gag"], + PenaltyType.Silence => localizer["sa_discord_penalty_silence"], + PenaltyType.Warn => localizer["sa_discord_penalty_warn"], + _ => throw new ArgumentOutOfRangeException(nameof(penalty), penalty, null) + }, + Description = $"{hostname}", + ThumbnailUrl = penaltySetting.FirstOrDefault(s => s.Name.Equals("ThumbnailUrl"))?.Value, + ImageUrl = penaltySetting.FirstOrDefault(s => s.Name.Equals("ImageUrl"))?.Value, + Footer = new Footer + { + Text = penaltySetting.FirstOrDefault(s => s.Name.Equals("Footer"))?.Value + }, + + Timestamp = Time.ActualDateTime().ToUniversalTime().ToString("o"), + }; + + for (var i = 0; i < fieldNames.Length; i++) + { + embed.AddField(fieldNames[i], fieldValues[i], inlineFlags[i]); + } + + Task.Run(async () => + { + try + { + await new DiscordManager(webhookUrl).SendEmbedAsync(embed); + } + catch (Exception ex) + { + // Log or handle the exception + CS2_SimpleAdmin._logger?.LogError("Unable to send discord webhook: {exception}", ex.Message); + } + }); + } + + public static void SendDiscordPenaltyMessage(CCSPlayerController? caller, string steamId, string reason, int duration, PenaltyType penalty, IStringLocalizer? localizer) + { + if (localizer == null) return; + + var penaltySetting = penalty switch + { + PenaltyType.Ban => CS2_SimpleAdmin.Instance.Config.Discord.DiscordPenaltyBanSettings, + PenaltyType.Mute => CS2_SimpleAdmin.Instance.Config.Discord.DiscordPenaltyMuteSettings, + PenaltyType.Gag => CS2_SimpleAdmin.Instance.Config.Discord.DiscordPenaltyGagSettings, + PenaltyType.Silence => CS2_SimpleAdmin.Instance.Config.Discord.DiscordPenaltySilenceSettings, + PenaltyType.Warn => CS2_SimpleAdmin.Instance.Config.Discord.DiscordPenaltyWarnSettings, + _ => throw new ArgumentOutOfRangeException(nameof(penalty), penalty, null) + }; + + var webhookUrl = penaltySetting.FirstOrDefault(s => s.Name.Equals("Webhook"))?.Value; + + if (string.IsNullOrEmpty(webhookUrl)) return; + const string defaultCommunityUrl = ""; + var callerCommunityUrl = caller != null ? $"<{new SteamID(caller.SteamID).ToCommunityUrl()}>" : defaultCommunityUrl; + var targetCommunityUrl = $"<{new SteamID(ulong.Parse(steamId)).ToCommunityUrl()}>"; + + var callerName = caller?.PlayerName ?? CS2_SimpleAdmin._localizer?["sa_console"] ?? "Console"; + var targetName = steamId; + var targetSteamId = steamId; + + var futureTime = Time.ActualDateTime().AddMinutes(duration); + var futureUnixTimestamp = new DateTimeOffset(futureTime).ToUnixTimeSeconds(); + + string time; + + if (penaltySetting.FirstOrDefault(s => s.Name.Equals("Time"))?.Value == "{relative}") + time = duration != 0 ? $"" : localizer["sa_permanent"]; + else + time = duration != 0 ? ConvertMinutesToTime(duration) : localizer["sa_permanent"]; + + string[] fieldNames = [ + localizer["sa_player"], + localizer["sa_steamid"], + localizer["sa_duration"], + localizer["sa_reason"], + localizer["sa_admin"]]; + string[] fieldValues = + [ + $"[{targetName}]({targetCommunityUrl})", $"||{targetSteamId}||", time, reason, + $"[{callerName}]({callerCommunityUrl})" + ]; + + bool[] inlineFlags = [true, true, true, false, false]; + var hostname = ConVar.Find("hostname")?.StringValue ?? localizer["sa_unknown"]; + var colorHex = penaltySetting.FirstOrDefault(s => s.Name.Equals("Color"))?.Value ?? "#FFFFFF"; + var embed = new Embed { Color = DiscordManager.ColorFromHex(colorHex), diff --git a/CS2-SimpleAdmin/Managers/PermissionManager.cs b/CS2-SimpleAdmin/Managers/PermissionManager.cs index 4ac21b0..0643c35 100644 --- a/CS2-SimpleAdmin/Managers/PermissionManager.cs +++ b/CS2-SimpleAdmin/Managers/PermissionManager.cs @@ -106,7 +106,7 @@ ORDER BY sa_admins.player_steamid } catch (Exception ex) { - CS2_SimpleAdmin._logger?.LogError(ex.ToString()); + CS2_SimpleAdmin._logger?.LogError("Unable to load admins from database! {exception}", ex.Message); return []; } } @@ -224,7 +224,7 @@ FROM sa_groups_flags f } catch (Exception ex) { - CS2_SimpleAdmin._logger?.LogError(ex.ToString()); + CS2_SimpleAdmin._logger?.LogError("Unable to load groups from database! {exception}", ex.Message); } return []; @@ -371,7 +371,6 @@ public async Task CreateAdminsJsonFile() public async Task DeleteAdminBySteamId(string playerSteamId, bool globalDelete = false) { if (database == null) return; - if (string.IsNullOrEmpty(playerSteamId)) return; //_adminCache.TryRemove(playerSteamId, out _); @@ -453,7 +452,7 @@ public async Task AddAdminBySteamId(string playerSteamId, string playerName, Lis }); } - await Server.NextFrameAsync(() => + await Server.NextWorldUpdateAsync(() => { CS2_SimpleAdmin.Instance.ReloadAdmins(null); }); @@ -500,7 +499,7 @@ public async Task AddGroup(string groupName, List flagsList, int immunit await connection.ExecuteAsync(insertGroupServer, new { groupId, server_id = globalGroup ? null : CS2_SimpleAdmin.ServerId }); - await Server.NextFrameAsync(() => + await Server.NextWorldUpdateAsync(() => { CS2_SimpleAdmin.Instance.ReloadAdmins(null); }); @@ -508,7 +507,7 @@ await Server.NextFrameAsync(() => } catch (Exception ex) { - CS2_SimpleAdmin._logger?.LogError(ex.ToString()); + CS2_SimpleAdmin._logger?.LogError("Problem with loading admins: {exception}", ex.Message); } } diff --git a/CS2-SimpleAdmin/Managers/PlayerManager.cs b/CS2-SimpleAdmin/Managers/PlayerManager.cs index ab38e8a..41ca9d4 100644 --- a/CS2-SimpleAdmin/Managers/PlayerManager.cs +++ b/CS2-SimpleAdmin/Managers/PlayerManager.cs @@ -86,6 +86,11 @@ public void LoadPlayerData(CCSPlayerController player) try { + if (!CS2_SimpleAdmin.PlayersInfo.ContainsKey(userId)) + { + Helper.KickPlayer(userId, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_INVALIDCONNECTION); + } + // Check if the player is banned var isBanned = await CS2_SimpleAdmin.Instance.BanManager.IsPlayerBanned(CS2_SimpleAdmin.PlayersInfo[userId]); @@ -194,7 +199,7 @@ await Server.NextFrameAsync(() => } catch (Exception ex) { - CS2_SimpleAdmin._logger?.LogError($"Error processing player connection: {ex}"); + CS2_SimpleAdmin._logger?.LogError("Error processing player connection: {exception}", ex.Message); } }); @@ -263,7 +268,7 @@ public void CheckPlayersTimer() } catch (Exception ex) { - CS2_SimpleAdmin._logger?.LogError($"Unexpected error: {ex.Message}"); + CS2_SimpleAdmin._logger?.LogError("Unexpected error: {exception}", ex.Message); } CS2_SimpleAdmin.BannedPlayers.Clear(); diff --git a/CS2-SimpleAdmin/Managers/ServerManager.cs b/CS2-SimpleAdmin/Managers/ServerManager.cs index 38f0293..012329e 100644 --- a/CS2-SimpleAdmin/Managers/ServerManager.cs +++ b/CS2-SimpleAdmin/Managers/ServerManager.cs @@ -14,19 +14,23 @@ public void LoadServerData() CS2_SimpleAdmin.Instance.AddTimer(2.0f, () => { if (CS2_SimpleAdmin.ServerLoaded || CS2_SimpleAdmin.ServerId != null || CS2_SimpleAdmin.Database == null) return; + + if (_getIpTryCount > 16) + { + CS2_SimpleAdmin._logger?.LogError("Unable to load server data - can't fetch ip address!"); + return; + } var ipAddress = ConVar.Find("ip")?.StringValue; if (string.IsNullOrEmpty(ipAddress) || ipAddress.StartsWith("0.0.0")) { ipAddress = Helper.GetServerIp(); - } - if (string.IsNullOrEmpty(ipAddress) || ipAddress.StartsWith("0.0.0")) - { - if (_getIpTryCount < 12) + if (_getIpTryCount <= 16) { _getIpTryCount++; + LoadServerData(); return; } @@ -54,7 +58,7 @@ await connection.ExecuteAsync( else { await connection.ExecuteAsync( - "UPDATE `sa_servers` SET `hostname` = @hostname, `id` = `id` WHERE `address` = @address", + "UPDATE `sa_servers` SET `hostname` = @hostname WHERE `address` = @address", new { address, hostname }); } @@ -66,7 +70,7 @@ await connection.ExecuteAsync( if (CS2_SimpleAdmin.ServerId != null) { - await Server.NextFrameAsync(() => CS2_SimpleAdmin.Instance.ReloadAdmins(null)); + await Server.NextWorldUpdateAsync(() => CS2_SimpleAdmin.Instance.ReloadAdmins(null)); } CS2_SimpleAdmin.ServerLoaded = true; diff --git a/CS2-SimpleAdmin/Menus/FunActionsMenu.cs b/CS2-SimpleAdmin/Menus/FunActionsMenu.cs index a7b1bfe..06373d2 100644 --- a/CS2-SimpleAdmin/Menus/FunActionsMenu.cs +++ b/CS2-SimpleAdmin/Menus/FunActionsMenu.cs @@ -143,10 +143,10 @@ private static void StripWeapons(CCSPlayerController admin, CCSPlayerController private static void Freeze(CCSPlayerController admin, CCSPlayerController player) { - if (!(player?.PlayerPawn.Value?.IsValid ?? false)) + if (!(player.PlayerPawn.Value?.IsValid ?? false)) return; - if (player.PlayerPawn.Value.MoveType != MoveType_t.MOVETYPE_OBSOLETE) + if (player.PlayerPawn.Value.MoveType != MoveType_t.MOVETYPE_INVALID) CS2_SimpleAdmin.Freeze(admin, player, -1); else CS2_SimpleAdmin.Unfreeze(admin, player); diff --git a/CS2-SimpleAdmin/VERSION b/CS2-SimpleAdmin/VERSION index c24cd72..f7fba94 100644 --- a/CS2-SimpleAdmin/VERSION +++ b/CS2-SimpleAdmin/VERSION @@ -1 +1 @@ -1.6.8a \ No newline at end of file +1.6.9a \ No newline at end of file diff --git a/CS2-SimpleAdmin/Variables.cs b/CS2-SimpleAdmin/Variables.cs index db1d6d9..c0e65b0 100644 --- a/CS2-SimpleAdmin/Variables.cs +++ b/CS2-SimpleAdmin/Variables.cs @@ -38,10 +38,10 @@ public partial class CS2_SimpleAdmin // Player Management private static readonly HashSet GodPlayers = []; - private static readonly HashSet SilentPlayers = []; + internal static readonly HashSet SilentPlayers = []; internal static readonly ConcurrentBag BannedPlayers = []; internal static readonly Dictionary RenamedPlayers = []; - internal static readonly Dictionary PlayersInfo = []; + internal static readonly ConcurrentDictionary PlayersInfo = []; private static readonly List DisconnectedPlayers = []; // Discord Integration diff --git a/CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj b/CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj index 8cf7c0b..b63c31f 100644 --- a/CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj +++ b/CS2-SimpleAdminApi/CS2-SimpleAdminApi.csproj @@ -8,7 +8,7 @@ - + diff --git a/CS2-SimpleAdminApi/ICS2-SimpleAdminApi.cs b/CS2-SimpleAdminApi/ICS2-SimpleAdminApi.cs index d30d249..a1e627d 100644 --- a/CS2-SimpleAdminApi/ICS2-SimpleAdminApi.cs +++ b/CS2-SimpleAdminApi/ICS2-SimpleAdminApi.cs @@ -23,4 +23,6 @@ public interface ICS2_SimpleAdminApi public void IssuePenalty(CCSPlayerController player, CCSPlayerController? admin, PenaltyType penaltyType, string reason, int duration = -1); public void LogCommand(CCSPlayerController? caller, string command); public void LogCommand(CCSPlayerController? caller, CommandInfo command); + + public bool IsAdminSilent(CCSPlayerController player); } \ No newline at end of file