```diff
+ `Refactored Code`: Improved code structure for better maintainability.
+ `Player Penalties Command`: Introduced the `css_penalties` command to display player penalties.
+ `Admin Penalties Information`: Added functionality to provide information for admins regarding penalties of connecting players.
+ `Disconnected Players Command`: Added the `css_disconnected` command to show a list of disconnected players.
+ `Colorful Messages`: Implemented the `css_cssay` command to send colorful messages, e.g., `css_cssay {lightgreen}Test`.
+ `Respawn Functionality`: Updated the `css_respawn` command to respawn players at their death location.
+ `Menu Type Management`: Introduced the `css_menus` command to change the menu type via `MenuManagerCS2`.
+ `Dynamic Menu Control`: Enhanced menu interaction with dynamic controls using WASD + ER keys.
+ `Language File Updates`: Updated language files for better localization.
+ `API Integration`: Added a simple API for external interaction.
+ `Configurable Timezone`: Introduced timezone settings in the configuration.
+ `Admin Activity Display Options`: Added configurable settings for displaying admin activity:
  + `0`: Do not show
  + `1`: Hide admin name
  + `2`: Show admin name
+ `Discord Notification Customization`: Made Discord duration notifications customizable with `{relative}` and `{normal}` placeholders.
+ Improved command logging
+

`Configuration Options:`
+ `Timezone`
+ `Other Settings`
+ `Disconnected Players History Count`
+ `Show Activity Type`
```
This commit is contained in:
Dawid Bepierszcz 2024-09-28 18:03:03 +02:00
parent a3b8d8cfa7
commit 05893a90d3
69 changed files with 10879 additions and 0 deletions

View file

@ -0,0 +1,421 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.ValveConstants.Protobuf;
using CS2_SimpleAdmin.Managers;
using CS2_SimpleAdminApi;
namespace CS2_SimpleAdmin;
public partial class CS2_SimpleAdmin
{
[ConsoleCommand("css_ban")]
[RequiresPermissions("@css/ban")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnBanCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
if (command.ArgCount < 2)
return;
var reason = _localizer?["sa_unknown"] ?? "Unknown";
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, IsHLTV: false }).ToList();
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
{
return;
}
Database.Database database = new(DbConnectionString);
BanManager banManager = new(database, Config);
int.TryParse(command.GetArg(2), out var time);
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
reason = command.GetArg(3);
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Ban(caller, player, time, reason, callerName, banManager, command);
}
});
}
internal void Ban(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, BanManager? banManager = null, CommandInfo? command = null, bool silent = false)
{
if (Database == null || !player.IsValid || !player.UserId.HasValue) return;
if (!caller.CanTarget(player)) return;
if (!CheckValidBan(caller, time)) return;
// Set default caller name if not provided
callerName ??= "Console";
// Freeze player pawn if alive
if (player.PawnIsAlive)
{
player.Pawn.Value?.Freeze();
}
// Get player and admin information
var playerInfo = PlayersInfo[player.UserId.Value];
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
// Asynchronously handle banning logic
Task.Run(async () =>
{
await (banManager ??= new BanManager(Database, Config)).BanPlayer(playerInfo, adminInfo, reason, time);
});
// Update banned players list
if (playerInfo.IpAddress != null && !BannedPlayers.Contains(playerInfo.IpAddress))
BannedPlayers.Add(playerInfo.IpAddress);
if (!BannedPlayers.Contains(player.SteamID.ToString()))
BannedPlayers.Add(player.SteamID.ToString());
// Determine message keys and arguments based on ban time
var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0
? ("sa_player_ban_message_perm", "sa_admin_ban_message_perm",
[reason, "CALLER"],
["CALLER", player.PlayerName, reason])
: ("sa_player_ban_message_time", "sa_admin_ban_message_time",
new object[] { reason, time, "CALLER" },
new object[] { "CALLER", player.PlayerName, reason, time });
// Display center message to the player
Helper.DisplayCenterMessage(player, messageKey, callerName, centerArgs);
// Display admin activity message if necessary
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Schedule a kick timer
if (player.UserId.HasValue)
{
AddTimer(Config.OtherSettings.KickTime, () =>
{
if (player is { IsValid: true, UserId: not null })
{
Helper.KickPlayer(player.UserId.Value, NetworkDisconnectionReason.NETWORK_DISCONNECT_KICKBANADDED);
}
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
// Execute ban command if necessary
if (UnlockedCommands)
{
Server.ExecuteCommand($"banid 1 {new SteamID(player.SteamID).SteamId3}");
}
if (!silent)
{
if (command == null)
Helper.LogCommand(caller, $"css_ban {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
else
Helper.LogCommand(caller, command);
}
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Ban, _localizer);
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Ban, reason, time);
}
[ConsoleCommand("css_addban")]
[RequiresPermissions("@css/ban")]
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnAddBanCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerName = caller?.PlayerName ?? "Console";
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
{
command.ReplyToCommand("Invalid SteamID64.");
return;
}
var steamid = steamId.SteamId64.ToString();
var reason = command.ArgCount >= 3 && !string.IsNullOrEmpty(command.GetArg(3))
? command.GetArg(3)
: _localizer?["sa_unknown"] ?? "Unknown";
int.TryParse(command.GetArg(2), out var time);
if (!CheckValidBan(caller, time)) return;
var adminInfo = caller != null && caller.UserId.HasValue
? PlayersInfo[caller.UserId.Value]
: null;
var matches = Helper.GetPlayerFromSteamid64(steamid);
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
if (player != null && player.IsValid)
{
if (!caller.CanTarget(player))
return;
Ban(caller, player, time, reason, callerName, silent: true);
//command.ReplyToCommand($"Banned player {player.PlayerName}.");
}
else
{
// Asynchronous ban operation if player is not online or not found
Task.Run(async () =>
{
var banManager = new BanManager(Database, Config);
await banManager.AddBanBySteamid(steamid, adminInfo, reason, time);
});
command.ReplyToCommand($"Player with steamid {steamid} is not online. Ban has been added offline.");
}
Helper.LogCommand(caller, command);
if (UnlockedCommands)
Server.ExecuteCommand($"banid 1 {steamId.SteamId3}");
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Ban, reason, time);
}
[ConsoleCommand("css_banip")]
[RequiresPermissions("@css/ban")]
[CommandHelper(minArgs: 1, usage: "<ip> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnBanIpCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerName = caller?.PlayerName ?? "Console";
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
var ipAddress = command.GetArg(1);
if (!Helper.IsValidIp(ipAddress))
{
command.ReplyToCommand($"Invalid IP address.");
return;
}
var reason = command.ArgCount >= 3 && !string.IsNullOrEmpty(command.GetArg(3))
? command.GetArg(3)
: _localizer?["sa_unknown"] ?? "Unknown";
int.TryParse(command.GetArg(2), out var time);
if (!CheckValidBan(caller, time)) return;
var adminInfo = caller != null && caller.UserId.HasValue
? PlayersInfo[caller.UserId.Value]
: null;
var matches = Helper.GetPlayerFromIp(ipAddress);
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
if (player != null && player.IsValid)
{
if (!caller.CanTarget(player))
return;
Ban(caller, player, time, reason, callerName, silent: true);
}
else
{
// Asynchronous ban operation if player is not online or not found
Task.Run(async () =>
{
var banManager = new BanManager(Database, Config);
await banManager.AddBanByIp(ipAddress, adminInfo, reason, time);
});
command.ReplyToCommand($"Player with ip {ipAddress} is not online. Ban has been added offline.");
}
Helper.LogCommand(caller, command);
}
private bool CheckValidBan(CCSPlayerController? caller, int duration)
{
if (caller == null) return true;
bool canPermBan = AdminManager.PlayerHasPermissions(caller, "@css/permban");
if (duration <= 0 && canPermBan == false)
{
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_perm_restricted"]}");
return false;
}
if (duration <= Config.OtherSettings.MaxBanDuration || canPermBan) return true;
caller.PrintToChat($"{_localizer!["sa_prefix"]} {_localizer["sa_ban_max_duration_exceeded", Config.OtherSettings.MaxBanDuration]}");
return false;
}
[ConsoleCommand("css_unban")]
[RequiresPermissions("@css/unban")]
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnUnbanCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerSteamId = caller?.SteamID.ToString() ?? "Console";
if (command.GetArg(1).Length <= 1)
{
command.ReplyToCommand($"Too short pattern to search.");
return;
}
var pattern = command.GetArg(1);
var reason = command.GetArg(2);
BanManager banManager = new(Database, Config);
Task.Run(async () => await banManager.UnbanPlayer(pattern, callerSteamId, reason));
Helper.LogCommand(caller, command);
command.ReplyToCommand($"Unbanned player with pattern {pattern}.");
}
[ConsoleCommand("css_warn")]
[RequiresPermissions("@css/kick")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnWarnCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null)
return;
var callerName = caller == null ? "Console" : caller.PlayerName;
if (command.ArgCount < 2)
return;
var reason = _localizer?["sa_unknown"] ?? "Unknown";
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected && !player.IsHLTV).ToList();
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
{
return;
}
WarnManager warnManager = new(Database);
int.TryParse(command.GetArg(2), out var time);
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
reason = command.GetArg(3);
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Warn(caller, player, time, reason, callerName, warnManager, command);
}
});
}
internal void Warn(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, WarnManager? warnManager = null, CommandInfo? command = null)
{
if (Database == null || !player.IsValid || !player.UserId.HasValue) return;
if (!caller.CanTarget(player)) return;
if (!CheckValidBan(caller, time)) return;
// Set default caller name if not provided
callerName ??= "Console";
// Freeze player pawn if alive
if (player.PawnIsAlive)
{
player.Pawn.Value?.Freeze();
}
// Get player and admin information
var playerInfo = PlayersInfo[player.UserId.Value];
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
// Asynchronously handle warning logic
Task.Run(async () =>
{
warnManager ??= new WarnManager(Database);
await warnManager.WarnPlayer(playerInfo, adminInfo, reason, time);
// Check for warn thresholds and execute punish command if applicable
var totalWarns = await warnManager.GetPlayerWarnsCount(player.SteamID.ToString());
if (Config.WarnThreshold.Count > 0)
{
string? punishCommand = null;
var lastKey = Config.WarnThreshold.Keys.Max();
if (totalWarns >= lastKey)
punishCommand = Config.WarnThreshold[lastKey];
else if (Config.WarnThreshold.TryGetValue(totalWarns, out var value))
punishCommand = value;
if (!string.IsNullOrEmpty(punishCommand))
{
await Server.NextFrameAsync(() =>
{
Server.ExecuteCommand(punishCommand.Replace("USERID", playerInfo.UserId.ToString()).Replace("STEAMID64", playerInfo.SteamId?.ToString()));
});
}
}
});
// Determine message keys and arguments based on warning time
var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) = time == 0
? ("sa_player_warn_message_perm", "sa_admin_warn_message_perm",
new object[] { reason, "CALLER" },
new object[] { "CALLER", player.PlayerName, reason })
: ("sa_player_warn_message_time", "sa_admin_warn_message_time",
[reason, time, "CALLER"],
["CALLER", player.PlayerName, reason, time]);
// Display center message to the playser
Helper.DisplayCenterMessage(player, messageKey, callerName, centerArgs);
// Display admin activity message if necessary
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Log the warning command
if (command == null)
Helper.LogCommand(caller, $"css_warn {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
else
Helper.LogCommand(caller, command);
// Send Discord notification for the warning
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Warn, _localizer);
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Warn, reason, time);
}
[ConsoleCommand("css_unwarn")]
[RequiresPermissions("@css/kick")]
[CommandHelper(minArgs: 1, usage: "<steamid or name or ip>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnUnwarnCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerSteamId = caller?.SteamID.ToString() ?? "Console";
if (command.GetArg(1).Length <= 1)
{
command.ReplyToCommand($"Too short pattern to search.");
return;
}
var pattern = command.GetArg(1);
WarnManager warnManager = new(Database);
Task.Run(async () => await warnManager.UnwarnPlayer(pattern));
Helper.LogCommand(caller, command);
command.ReplyToCommand($"Unwarned player with pattern {pattern}.");
}
}

View file

@ -0,0 +1,127 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Core.Translations;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
using System.Text;
namespace CS2_SimpleAdmin;
public partial class CS2_SimpleAdmin
{
[ConsoleCommand("css_asay", "Say to all admins.")]
[CommandHelper(1, "<message>")]
[RequiresPermissions("@css/chat")]
public void OnAdminToAdminSayCommand(CCSPlayerController? caller, CommandInfo command)
{
Helper.LogCommand(caller, command);
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
foreach (var player in Helper.GetValidPlayers()
.Where(p => AdminManager.PlayerHasPermissions(p, "@css/chat")))
{
if (_localizer != null)
player.PrintToChat(_localizer["sa_adminchat_template_admin",
caller == null ? "Console" : caller.PlayerName,
utf8String]);
}
}
[ConsoleCommand("css_cssay", "Say custom text to all players - u can use color tags.")]
[CommandHelper(1, "<message>")]
[RequiresPermissions("@css/chat")]
public void OnAdminCustomSayCommand(CCSPlayerController? caller, CommandInfo command)
{
if (command.GetCommandString[command.GetCommandString.IndexOf(' ')..].Length == 0) return;
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
Helper.LogCommand(caller, command);
foreach (var player in Helper.GetValidPlayers())
{
player.PrintToChat(utf8String.ReplaceColorTags());
}
}
[ConsoleCommand("css_say", "Say to all players.")]
[CommandHelper(1, "<message>")]
[RequiresPermissions("@css/chat")]
public void OnAdminSayCommand(CCSPlayerController? caller, CommandInfo command)
{
if (command.GetCommandString[command.GetCommandString.IndexOf(' ')..].Length == 0) return;
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
Helper.LogCommand(caller, command);
foreach (var player in Helper.GetValidPlayers())
{
player.SendLocalizedMessage(_localizer,
"sa_adminsay_prefix",
utf8String.ReplaceColorTags());
}
}
[ConsoleCommand("css_psay", "Private message a player.")]
[CommandHelper(2, "<#userid or name> <message>")]
[RequiresPermissions("@css/chat")]
public void OnAdminPrivateSayCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
//Helper.LogCommand(caller, command);
var range = command.GetArg(0).Length + command.GetArg(1).Length + 2;
var message = command.GetCommandString[range..];
var utf8BytesString = Encoding.UTF8.GetBytes(message);
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
playersToTarget.ForEach(player =>
{
player.PrintToChat($"({callerName}) {utf8String}".ReplaceColorTags());
});
command.ReplyToCommand($" Private message sent!");
}
[ConsoleCommand("css_csay", "Say to all players (in center).")]
[CommandHelper(1, "<message>")]
[RequiresPermissions("@css/chat")]
public void OnAdminCenterSayCommand(CCSPlayerController? caller, CommandInfo command)
{
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
Helper.LogCommand(caller, command);
Helper.PrintToCenterAll(utf8String.ReplaceColorTags());
}
[ConsoleCommand("css_hsay", "Say to all players (in hud).")]
[CommandHelper(1, "<message>")]
[RequiresPermissions("@css/chat")]
public void OnAdminHudSayCommand(CCSPlayerController? caller, CommandInfo command)
{
var utf8BytesString = Encoding.UTF8.GetBytes(command.GetCommandString[command.GetCommandString.IndexOf(' ')..]);
var utf8String = Encoding.UTF8.GetString(utf8BytesString);
Helper.LogCommand(caller, command);
VirtualFunctions.ClientPrintAll(
HudDestination.Alert,
utf8String.ReplaceColorTags(),
0, 0, 0, 0);
}
}

View file

@ -0,0 +1,908 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Core.Translations;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Menu;
using CounterStrikeSharp.API.Modules.Utils;
using CS2_SimpleAdmin.Managers;
using CS2_SimpleAdmin.Menus;
using CS2_SimpleAdminApi;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Globalization;
namespace CS2_SimpleAdmin;
public partial class CS2_SimpleAdmin
{
[ConsoleCommand("css_penalties")]
[ConsoleCommand("css_mypenalties")]
[ConsoleCommand("css_comms")]
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)]
public void OnPenaltiesCommand(CCSPlayerController? caller, CommandInfo command)
{
if (caller == null || caller.IsValid == false || !caller.UserId.HasValue || Database == null)
return;
var userId = caller.UserId.Value;
Task.Run(async () =>
{
// Initialize managers
MuteManager muteManager = new(Database);
WarnManager warnManager = new(Database);
try
{
var warns = await warnManager.GetPlayerWarns(PlayersInfo[userId], false);
// Check if the player is muted
var activeMutes = await muteManager.IsPlayerMuted(PlayersInfo[userId].SteamId.SteamId64.ToString());
Dictionary<PenaltyType, List<string>> mutesList = new()
{
{ PenaltyType.Gag, [] },
{ PenaltyType.Mute, [] },
{ PenaltyType.Silence, [] }
};
List<string> warnsList = [];
bool found = false;
foreach (var warn in warns.TakeWhile(warn => (string)warn.status == "ACTIVE"))
{
DateTime ends = warn.ends;
if (_localizer == null) continue;
using (new WithTemporaryCulture(caller.GetLanguage()))
warnsList.Add(_localizer["sa_player_penalty_info_active_warn", ends.ToLocalTime().ToString(CultureInfo.CurrentCulture), (string)warn.reason]);
found = true;
}
if (!found)
{
if (_localizer != null)
warnsList.Add(_localizer["sa_player_penalty_info_no_active_warn"]);
}
if (activeMutes.Count > 0)
{
foreach (var mute in activeMutes)
{
string muteType = mute.type;
DateTime ends = mute.ends;
using (new WithTemporaryCulture(caller.GetLanguage()))
{
switch (muteType)
{
// Apply mute penalty based on mute type
case "GAG":
if (_localizer != null)
mutesList[PenaltyType.Gag].Add(_localizer["sa_player_penalty_info_active_gag", ends.ToLocalTime().ToString(CultureInfo.CurrentCulture)]);
break;
case "MUTE":
if (_localizer != null)
mutesList[PenaltyType.Mute].Add(_localizer["sa_player_penalty_info_active_mute", ends.ToLocalTime().ToString(CultureInfo.CurrentCulture)]);
break;
default:
if (_localizer != null)
mutesList[PenaltyType.Silence].Add(_localizer["sa_player_penalty_info_active_silence", ends.ToLocalTime().ToString(CultureInfo.CurrentCulture)]);
break;
}
}
}
}
if (_localizer != null)
{
if (mutesList[PenaltyType.Gag].Count == 0)
mutesList[PenaltyType.Gag].Add(_localizer["sa_player_penalty_info_no_active_gag"]);
if (mutesList[PenaltyType.Mute].Count == 0)
mutesList[PenaltyType.Mute].Add(_localizer["sa_player_penalty_info_no_active_mute"]);
if (mutesList[PenaltyType.Silence].Count == 0)
mutesList[PenaltyType.Silence].Add(_localizer["sa_player_penalty_info_no_active_silence"]);
}
await Server.NextFrameAsync(() =>
{
caller.SendLocalizedMessage(_localizer, "sa_player_penalty_info",
[
caller.PlayerName,
PlayersInfo[userId].TotalBans,
PlayersInfo[userId].TotalGags,
PlayersInfo[userId].TotalMutes,
PlayersInfo[userId].TotalSilences,
PlayersInfo[userId].TotalWarns,
string.Join("\n", mutesList.SelectMany(kvp => kvp.Value)),
string.Join("\n", warnsList)
]);
});
}
catch (Exception ex)
{
_logger?.LogError($"Error processing player information: {ex}");
}
});
}
[ConsoleCommand("css_admin")]
[RequiresPermissions("@css/generic")]
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)]
public void OnAdminCommand(CCSPlayerController? caller, CommandInfo command)
{
if (caller == null || caller.IsValid == false)
return;
AdminMenu.OpenMenu(caller);
}
[ConsoleCommand("css_adminhelp")]
[RequiresPermissions("@css/generic")]
public void OnAdminHelpCommand(CCSPlayerController? caller, CommandInfo command)
{
var lines = File.ReadAllLines(ModuleDirectory + "/admin_help.txt");
foreach (var line in lines)
{
command.ReplyToCommand(string.IsNullOrWhiteSpace(line) ? " " : line.ReplaceColorTags());
}
}
[ConsoleCommand("css_addadmin")]
[CommandHelper(minArgs: 4, usage: "<steamid> <name> <flags/groups> <immunity> <duration>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
[RequiresPermissions("@css/root")]
public void OnAddAdminCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
{
command.ReplyToCommand($"Invalid SteamID64.");
return;
}
var steamid = steamId.SteamId64.ToString();
if (command.GetArg(2).Length <= 0)
{
command.ReplyToCommand($"Invalid player name.");
return;
}
if (!command.GetArg(3).Contains('@') && !command.GetArg(3).Contains('#'))
{
command.ReplyToCommand($"Invalid flag or group.");
return;
}
var name = command.GetArg(2);
var flags = command.GetArg(3);
var globalAdmin = command.GetArg(4).ToLower().Equals("-g") || command.GetArg(5).ToLower().Equals("-g") ||
command.GetArg(6).ToLower().Equals("-g");
int.TryParse(command.GetArg(4), out var immunity);
int.TryParse(command.GetArg(5), out var time);
AddAdmin(caller, steamid, name, flags, immunity, time, globalAdmin, command);
}
public static void AddAdmin(CCSPlayerController? caller, string steamid, string name, string flags, int immunity, int time = 0, bool globalAdmin = false, CommandInfo? command = null)
{
if (Database == null) return;
PermissionManager adminManager = new(Database);
var flagsList = flags.Split(',').Select(flag => flag.Trim()).ToList();
_ = adminManager.AddAdminBySteamId(steamid, name, flagsList, immunity, time, globalAdmin);
Helper.LogCommand(caller, $"css_addadmin {steamid} {name} {flags} {immunity} {time}");
var msg = $"Added '{flags}' flags to '{name}' ({steamid})";
if (command != null)
command.ReplyToCommand(msg);
else if (caller != null && caller.IsValid)
caller.PrintToChat(msg);
else
Server.PrintToConsole(msg);
}
[ConsoleCommand("css_deladmin")]
[CommandHelper(minArgs: 1, usage: "<steamid>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
[RequiresPermissions("@css/root")]
public void OnDelAdminCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
{
command.ReplyToCommand($"Invalid SteamID64.");
return;
}
var globalDelete = command.GetArg(2).ToLower().Equals("-g");
RemoveAdmin(caller, steamId.SteamId64.ToString(), globalDelete, command);
}
public void RemoveAdmin(CCSPlayerController? caller, string steamid, bool globalDelete = false, CommandInfo? command = null)
{
if (Database == null) return;
PermissionManager adminManager = new(Database);
_ = adminManager.DeleteAdminBySteamId(steamid, globalDelete);
AddTimer(2, () =>
{
if (string.IsNullOrEmpty(steamid) || !SteamID.TryParse(steamid, out var steamId) ||
steamId == null) return;
if (PermissionManager.AdminCache.ContainsKey(steamId))
{
PermissionManager.AdminCache.TryRemove(steamId, out _);
}
AdminManager.ClearPlayerPermissions(steamId);
AdminManager.RemovePlayerAdminData(steamId);
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
Helper.LogCommand(caller, $"css_deladmin {steamid}");
var msg = $"Removed flags from '{steamid}'";
if (command != null)
command.ReplyToCommand(msg);
else if (caller != null && caller.IsValid)
caller.PrintToChat(msg);
else
Server.PrintToConsole(msg);
}
[ConsoleCommand("css_addgroup")]
[CommandHelper(minArgs: 3, usage: "<group_name> <flags> <immunity>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
[RequiresPermissions("@css/root")]
public void OnAddGroup(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
if (!command.GetArg(1).StartsWith("#"))
{
command.ReplyToCommand($"Group name must start with #.");
return;
}
if (!command.GetArg(2).StartsWith($"@") && !command.GetArg(2).StartsWith($"#"))
{
command.ReplyToCommand($"Invalid flag or group.");
return;
}
var groupName = command.GetArg(1);
var flags = command.GetArg(2);
int.TryParse(command.GetArg(3), out var immunity);
var globalGroup = command.GetArg(4).ToLower().Equals("-g");
AddGroup(caller, groupName, flags, immunity, globalGroup, command);
}
private static void AddGroup(CCSPlayerController? caller, string name, string flags, int immunity, bool globalGroup, CommandInfo? command = null)
{
if (Database == null) return;
PermissionManager adminManager = new(Database);
var flagsList = flags.Split(',').Select(flag => flag.Trim()).ToList();
_ = adminManager.AddGroup(name, flagsList, immunity, globalGroup);
Helper.LogCommand(caller, $"css_addgroup {name} {flags} {immunity}");
var msg = $"Created group '{name}' with flags '{flags}'";
if (command != null)
command.ReplyToCommand(msg);
else if (caller != null && caller.IsValid)
caller.PrintToChat(msg);
else
Server.PrintToConsole(msg);
}
[ConsoleCommand("css_delgroup")]
[CommandHelper(minArgs: 1, usage: "<group_name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
[RequiresPermissions("@css/root")]
public void OnDelGroupCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
if (!command.GetArg(1).StartsWith($"#"))
{
command.ReplyToCommand($"Group name must start with #.");
return;
}
var groupName = command.GetArg(1);
RemoveGroup(caller, groupName, command);
}
private void RemoveGroup(CCSPlayerController? caller, string name, CommandInfo? command = null)
{
if (Database == null) return;
PermissionManager adminManager = new(Database);
_ = adminManager.DeleteGroup(name);
AddTimer(2, () =>
{
ReloadAdmins(caller);
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
Helper.LogCommand(caller, $"css_delgroup {name}");
var msg = $"Removed group '{name}'";
if (command != null)
command.ReplyToCommand(msg);
else if (caller != null && caller.IsValid)
caller.PrintToChat(msg);
else
Server.PrintToConsole(msg);
}
[ConsoleCommand("css_reloadadmins")]
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
[RequiresPermissions("@css/root")]
public void OnRelAdminCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
ReloadAdmins(caller);
command.ReplyToCommand("Reloaded sql admins and groups");
}
public void ReloadAdmins(CCSPlayerController? caller)
{
if (Database == null) return;
for (var index = 0; index < PermissionManager.AdminCache.Keys.ToList().Count; index++)
{
var steamId = PermissionManager.AdminCache.Keys.ToList()[index];
if (!PermissionManager.AdminCache.TryRemove(steamId, out _)) continue;
AdminManager.ClearPlayerPermissions(steamId);
AdminManager.RemovePlayerAdminData(steamId);
}
PermissionManager adminManager = new(Database);
Task.Run(async () =>
{
await adminManager.CrateGroupsJsonFile();
await adminManager.CreateAdminsJsonFile();
var adminsFile = await File.ReadAllTextAsync(Instance.ModuleDirectory + "/data/admins.json");
var groupsFile = await File.ReadAllTextAsync(Instance.ModuleDirectory + "/data/groups.json");
await Server.NextFrameAsync(() =>
{
if (!string.IsNullOrEmpty(adminsFile))
AddTimer(0.5f, () => AdminManager.LoadAdminData(ModuleDirectory + "/data/admins.json"));
if (!string.IsNullOrEmpty(groupsFile))
AddTimer(1.0f, () => AdminManager.LoadAdminGroups(ModuleDirectory + "/data/groups.json"));
if (!string.IsNullOrEmpty(adminsFile))
AddTimer(1.5f, () => AdminManager.LoadAdminData(ModuleDirectory + "/data/admins.json"));
});
});
//_ = _adminManager.GiveAllGroupsFlags();
//_ = _adminManager.GiveAllFlags();
}
[ConsoleCommand("css_stealth")]
[ConsoleCommand("css_hide")]
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)]
[RequiresPermissions("@css/kick")]
public void OnHideCommand(CCSPlayerController? caller, CommandInfo command)
{
if (caller == null) return;
Helper.LogCommand(caller, command);
if (!SilentPlayers.Add(caller.Slot))
{
SilentPlayers.Remove(caller.Slot);
caller.PrintToChat($"You aren't hidden now!");
caller.ChangeTeam(CsTeam.Spectator);
}
else
{
Server.ExecuteCommand("sv_disable_teamselect_menu 1");
if (caller.PlayerPawn.Value != null && caller.PawnIsAlive)
caller.PlayerPawn.Value.CommitSuicide(true, false);
AddTimer(1.0f, () => { Server.NextFrame(() => caller.ChangeTeam(CsTeam.Spectator)); }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
AddTimer(1.4f, () => { Server.NextFrame(() => caller.ChangeTeam(CsTeam.None)); }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
caller.PrintToChat($"You are hidden now!");
AddTimer(2.0f, () => { Server.NextFrame(() => Server.ExecuteCommand("sv_disable_teamselect_menu 0")); }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
}
[ConsoleCommand("css_who")]
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
[RequiresPermissions("@css/generic")]
public void OnWhoCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var targets = GetTarget(command);
if (targets == null) return;
Helper.LogCommand(caller, command);
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
if (!player.UserId.HasValue) return;
if (!caller!.CanTarget(player)) return;
var playerInfo = PlayersInfo[player.UserId.Value];
Task.Run(async () =>
{
await Server.NextFrameAsync(() =>
{
Action<string> printMethod = caller == null ? Server.PrintToConsole : caller.PrintToConsole;
printMethod($"--------- INFO ABOUT \"{playerInfo.Name}\" ---------");
printMethod($"• Clan: \"{player.Clan}\" Name: \"{playerInfo.Name}\"");
printMethod($"• UserID: \"{playerInfo.UserId}\"");
printMethod($"• SteamID64: \"{playerInfo.SteamId.SteamId64}\"");
if (player.Connected == PlayerConnectedState.PlayerConnected)
{
printMethod($"• SteamID2: \"{playerInfo.SteamId.SteamId2}\"");
printMethod($"• Community link: \"{playerInfo.SteamId.ToCommunityUrl()}\"");
}
if (playerInfo.IpAddress != null && AdminManager.PlayerHasPermissions(caller, "@css/showip"))
printMethod($"• IP Address: \"{playerInfo.IpAddress}\"");
printMethod($"• Ping: \"{player.Ping}\"");
if (player.Connected == PlayerConnectedState.PlayerConnected)
{
printMethod($"• Total Bans: \"{playerInfo.TotalBans}\"");
printMethod($"• Total Gags: \"{playerInfo.TotalGags}\"");
printMethod($"• Total Mutes: \"{playerInfo.TotalMutes}\"");
printMethod($"• Total Silences: \"{playerInfo.TotalSilences}\"");
printMethod($"• Total Warns: \"{playerInfo.TotalWarns}\"");
}
printMethod($"--------- END INFO ABOUT \"{player.PlayerName}\" ---------");
});
});
});
}
[ConsoleCommand("css_disconnected")]
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)]
[RequiresPermissions("@css/kick")]
public void OnDisconnectedCommand(CCSPlayerController? caller, CommandInfo command)
{
if (_localizer == null || caller == null) return;
var disconnectedMenu = MenuApi?.NewMenu(_localizer["sa_menu_disconnected_title"]);
DisconnectedPlayers.ForEach(player =>
{
disconnectedMenu?.AddMenuOption(player.Name, (_, _) =>
{
var disconnectedMenuAction = MenuApi?.NewMenu(_localizer["sa_menu_disconnected_action_title"]);
disconnectedMenuAction?.AddMenuOption(_localizer["sa_ban"], (_, _) =>
{
DurationMenu.OpenMenu(caller, _localizer["sa_ban"], player, (_, _, duration) =>
ReasonMenu.OpenMenu(caller, PenaltyType.Ban, "Powód", player, (_, _, reason) =>
{
caller.ExecuteClientCommandFromServer($"css_addban {player.SteamId.SteamId64} {duration} \"{reason}\"");
}));
});
disconnectedMenuAction?.AddMenuOption(_localizer["sa_mute"], (_, _) =>
{
DurationMenu.OpenMenu(caller, _localizer["sa_mute"], player, (_, _, duration) =>
ReasonMenu.OpenMenu(caller, PenaltyType.Mute, "Powód", player, (_, _, reason) =>
{
caller.ExecuteClientCommandFromServer($"css_addmute {player.SteamId.SteamId64} {duration} \"{reason}\"");
}));
});
disconnectedMenuAction?.AddMenuOption(_localizer["sa_gag"], (_, _) =>
{
DurationMenu.OpenMenu(caller, _localizer["sa_gag"], player, (_, _, duration) =>
ReasonMenu.OpenMenu(caller, PenaltyType.Mute, "Powód", player, (_, _, reason) =>
{
caller.ExecuteClientCommandFromServer($"css_addgag {player.SteamId.SteamId64} {duration} \"{reason}\"");
}));
});
disconnectedMenuAction?.AddMenuOption(_localizer["sa_silence"], (_, _) =>
{
DurationMenu.OpenMenu(caller, _localizer["sa_silence"], player, (_, _, duration) =>
ReasonMenu.OpenMenu(caller, PenaltyType.Mute, "Powód", player, (_, _, reason) =>
{
caller.ExecuteClientCommandFromServer($"css_addsilence {player.SteamId.SteamId64} {duration} \"{reason}\"");
}));
});
disconnectedMenuAction?.Open(caller);
});
});
disconnectedMenu?.Open(caller);
}
[ConsoleCommand("css_warns")]
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_ONLY)]
[RequiresPermissions("@css/kick")]
public void OnWarnsCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null || _localizer == null || caller == null) return;
var targets = GetTarget(command);
if (targets == null) return;
Helper.LogCommand(caller, command);
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsBot: false }).ToList();
if (playersToTarget.Count > 1)
return;
Database.Database database = new(DbConnectionString);
WarnManager warnManager = new(database);
playersToTarget.ForEach(player =>
{
if (!player.UserId.HasValue) return;
if (!caller!.CanTarget(player)) return;
var userId = player.UserId.Value;
IMenu? warnsMenu = MenuApi?.NewMenu(_localizer["sa_admin_warns_menu_title", player.PlayerName]);
Task.Run(async () =>
{
var warnsList = await warnManager.GetPlayerWarns(PlayersInfo[userId], false);
var sortedWarns = warnsList
.OrderBy(warn => (string)warn.status == "ACTIVE" ? 0 : 1)
.ThenByDescending(warn => (int)warn.id)
.ToList();
sortedWarns.ForEach(w =>
{
warnsMenu?.AddMenuOption($"[{((string)w.status == "ACTIVE" ? $"{ChatColors.LightRed}X" : $"{ChatColors.Lime}✔️")}{ChatColors.Default}] {(string)w.reason}",
(controller, option) =>
{
_ = warnManager.UnwarnPlayer(PlayersInfo[userId], (int)w.id);
player.PrintToChat(_localizer["sa_admin_warns_unwarn", player.PlayerName, (string)w.reason]);
});
});
await Server.NextFrameAsync(() =>
{
warnsMenu?.Open(caller);
});
});
});
}
[ConsoleCommand("css_players")]
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
[RequiresPermissions("@css/generic")]
public void OnPlayersCommand(CCSPlayerController? caller, CommandInfo command)
{
var isJson = command.GetArg(1).ToLower().Equals("-json");
var isDuplicate = command.GetArg(1).ToLower().Equals("-duplicate") || command.GetArg(2).ToLower().Equals("-duplicate");
var playersToTarget = isDuplicate
? Helper.GetValidPlayers().GroupBy(player => player.IpAddress?.Split(":")[0] ?? "Unknown")
.Where(group => group.Count() > 1)
.SelectMany(group => group)
.ToList()
: Helper.GetValidPlayers();
if (!isJson)
{
if (caller != null)
{
caller.PrintToConsole($"--------- PLAYER LIST ---------");
playersToTarget.ForEach(player =>
{
caller.PrintToConsole(
$"• [#{player.UserId}] \"{player.PlayerName}\" (IP Address: \"{(AdminManager.PlayerHasPermissions(caller, "@css/showip") ? player.IpAddress?.Split(":")[0] : "Unknown")}\" SteamID64: \"{player.SteamID}\")");
});
caller.PrintToConsole($"--------- END PLAYER LIST ---------");
}
else
{
Server.PrintToConsole($"--------- PLAYER LIST ---------");
playersToTarget.ForEach(player =>
{
Server.PrintToConsole($"• [#{player.UserId}] \"{player.PlayerName}\" (IP Address: \"{player.IpAddress?.Split(":")[0]}\" SteamID64: \"{player.SteamID}\")");
});
Server.PrintToConsole($"--------- END PLAYER LIST ---------");
}
}
else
{
var playersJson = JsonConvert.SerializeObject(playersToTarget.Select((CCSPlayerController player) =>
{
var matchStats = player.ActionTrackingServices?.MatchStats;
return new
{
player.UserId,
Name = player.PlayerName,
SteamId = player.SteamID.ToString(),
IpAddress = AdminManager.PlayerHasPermissions(caller, "@css/showip") ? player.IpAddress?.Split(":")[0] ?? "Unknown" : "Unknown",
player.Ping,
IsAdmin = AdminManager.PlayerHasPermissions(player, "@css/ban") || AdminManager.PlayerHasPermissions(player, "@css/generic"),
Stats = new
{
player.Score,
Kills = matchStats?.Kills ?? 0,
Deaths = matchStats?.Deaths ?? 0,
player.MVPs
}
};
}));
if (caller != null)
caller.PrintToConsole(playersJson);
else
Server.PrintToConsole(playersJson);
}
}
[ConsoleCommand("css_kick")]
[RequiresPermissions("@css/kick")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnKickCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var reason = _localizer?["sa_unknown"] ?? "Unknown";
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players
.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
{
return;
}
if (command.ArgCount >= 2 && command.GetArg(2).Length > 0)
reason = command.GetArg(2);
playersToTarget.ForEach(player =>
{
if (!player.IsValid)
return;
if (caller!.CanTarget(player))
{
Kick(caller, player, reason, callerName, command);
}
});
}
public void Kick(CCSPlayerController? caller, CCSPlayerController player, string? reason = "Unknown", string? callerName = null, CommandInfo? command = null)
{
if (!player.IsValid) return;
if (!caller.CanTarget(player)) return;
if (!player.UserId.HasValue) return;
// Set default caller name if not provided
callerName ??= caller != null ? caller.PlayerName : "Console";
reason ??= _localizer?["sa_unknown"] ?? "Unknown";
var playerInfo = PlayersInfo[player.UserId.Value];
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
// Freeze player pawn if alive
if (player.PawnIsAlive)
{
player.Pawn.Value?.Freeze();
}
// Determine message keys and arguments for the kick notification
var (messageKey, activityMessageKey, centerArgs, adminActivityArgs) =
("sa_player_kick_message", "sa_admin_kick_message",
new object[] { reason, "CALLER" },
new object[] { "CALLER", player.PlayerName, reason });
// Display center message to the kicked player
Helper.DisplayCenterMessage(player, messageKey, callerName, centerArgs);
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Schedule the kick for the player
if (player.UserId.HasValue)
{
AddTimer(Config.OtherSettings.KickTime, () =>
{
if (player.IsValid)
{
Helper.KickPlayer(player.UserId.Value);
}
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
// Log the command and send Discord notification
if (command == null)
Helper.LogCommand(caller, $"css_kick {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {reason}");
else
Helper.LogCommand(caller, command);
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Kick, reason);
}
[ConsoleCommand("css_changemap")]
[ConsoleCommand("css_map")]
[RequiresPermissions("@css/changemap")]
[CommandHelper(minArgs: 1, usage: "<mapname>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnMapCommand(CCSPlayerController? caller, CommandInfo command)
{
var map = command.GetCommandString.Split(" ")[1];
ChangeMap(caller, map, command);
}
public void ChangeMap(CCSPlayerController? caller, string map, CommandInfo? command = null)
{
var callerName = caller != null ? caller.PlayerName : "Console";
map = map.ToLower();
if (map.StartsWith("ws:"))
{
var issuedCommand = long.TryParse(map.Replace("ws:", ""), out var mapId)
? $"host_workshop_map {mapId}"
: $"ds_workshop_changelevel {map.Replace("ws:", "")}";
AddTimer(3.0f, () =>
{
Server.ExecuteCommand(issuedCommand);
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
else
{
if (!Server.IsMapValid(map))
{
var msg = $"Map {map} not found.";
if (command != null)
command.ReplyToCommand(msg);
else if (caller != null && caller.IsValid)
caller.PrintToChat(msg);
else
Server.PrintToConsole(msg);
return;
}
AddTimer(3.0f, () =>
{
Server.ExecuteCommand($"changelevel {map}");
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
var (activityMessageKey, adminActivityArgs) =
("sa_admin_changemap_message",
new object[] { "CALLER", map });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
Helper.LogCommand(caller, command?.GetCommandString ?? $"css_map {map}");
}
[ConsoleCommand("css_changewsmap", "Change workshop map.")]
[ConsoleCommand("css_wsmap", "Change workshop map.")]
[ConsoleCommand("css_workshop", "Change workshop map.")]
[CommandHelper(1, "<name or id>")]
[RequiresPermissions("@css/changemap")]
public void OnWorkshopMapCommand(CCSPlayerController? caller, CommandInfo command)
{
var map = command.GetArg(1);
ChangeWorkshopMap(caller, map, command);
}
public void ChangeWorkshopMap(CCSPlayerController? caller, string map, CommandInfo? command = null)
{
map = map.ToLower();
var callerName = caller != null ? caller.PlayerName : "Console";
// Determine the workshop command
var issuedCommand = long.TryParse(map, out var mapId)
? $"host_workshop_map {mapId}"
: $"ds_workshop_changelevel {map}";
// Define the admin activity message and arguments
var activityMessageKey = "sa_admin_changemap_message";
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, ["CALLER", map]);
}
// Add timer to execute the map change command after a delay
AddTimer(3.0f, () =>
{
Server.ExecuteCommand(issuedCommand);
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
// Log the command for the change
Helper.LogCommand(caller, command?.GetCommandString ?? $"css_wsmap {map}");
}
[ConsoleCommand("css_cvar", "Change a cvar.")]
[CommandHelper(2, "<cvar> <value>")]
[RequiresPermissions("@css/cvar")]
public void OnCvarCommand(CCSPlayerController? caller, CommandInfo command)
{
var cvar = ConVar.Find(command.GetArg(1));
var callerName = caller == null ? "Console" : caller.PlayerName;
if (cvar == null)
{
command.ReplyToCommand($"Cvar \"{command.GetArg(1)}\" not found.");
return;
}
if (cvar.Name.Equals("sv_cheats") && !AdminManager.PlayerHasPermissions(caller, "@css/cheats"))
{
command.ReplyToCommand($"You don't have permissions to change \"{command.GetArg(1)}\".");
return;
}
Helper.LogCommand(caller, command);
var value = command.GetArg(2);
Server.ExecuteCommand($"{cvar.Name} {value}");
command.ReplyToCommand($"{callerName} changed cvar {cvar.Name} to {value}.");
Logger.LogInformation($"{callerName} changed cvar {cvar.Name} to {value}.");
}
[ConsoleCommand("css_rcon", "Run a server console command.")]
[CommandHelper(1, "<command>")]
[RequiresPermissions("@css/rcon")]
public void OnRconCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
Helper.LogCommand(caller, command);
Server.ExecuteCommand(command.ArgString);
command.ReplyToCommand($"{callerName} executed command {command.ArgString}.");
Logger.LogInformation($"{callerName} executed command ({command.ArgString}).");
}
[ConsoleCommand("css_rr")]
[ConsoleCommand("css_rg")]
[ConsoleCommand("css_restart")]
[ConsoleCommand("css_restartgame")]
[RequiresPermissions("@css/generic")]
[CommandHelper(minArgs: 0, usage: "", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnRestartCommand(CCSPlayerController? caller, CommandInfo command)
{
RestartGame(caller);
}
public static void RestartGame(CCSPlayerController? admin)
{
Helper.LogCommand(admin, "css_restartgame");
// TODO: Localize
var name = admin == null ? "Console" : admin.PlayerName;
Server.PrintToChatAll($"[SA] {name}: Restarting game...");
Server.ExecuteCommand("mp_restartgame 2");
}
}

View file

@ -0,0 +1,680 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CS2_SimpleAdmin.Managers;
using CS2_SimpleAdminApi;
namespace CS2_SimpleAdmin;
public partial class CS2_SimpleAdmin
{
[ConsoleCommand("css_gag")]
[RequiresPermissions("@css/chat")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnGagCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerName = caller == null ? "Console" : caller.PlayerName;
var reason = _localizer?["sa_unknown"] ?? "Unknown";
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
{
return;
}
int.TryParse(command.GetArg(2), out var time);
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
reason = command.GetArg(3);
MuteManager muteManager = new(Database);
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Gag(caller, player, time, reason, callerName, muteManager, command);
}
});
}
internal void Gag(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, MuteManager? muteManager = null, CommandInfo? command = null, bool silent = false)
{
if (Database == null || !player.IsValid || !player.UserId.HasValue) return;
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller == null ? "Console" : caller.PlayerName;
muteManager ??= new MuteManager(Database);
// Get player and admin information
var playerInfo = PlayersInfo[player.UserId.Value];
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
// Asynchronously handle gag logic
Task.Run(async () =>
{
await muteManager.MutePlayer(playerInfo, adminInfo, reason, time);
});
// Add penalty to the player's penalty manager
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Gag, DateTime.Now.AddMinutes(time), time);
// Determine message keys and arguments based on gag time (permanent or timed)
var (messageKey, activityMessageKey, playerArgs, adminActivityArgs) = time == 0
? ("sa_player_gag_message_perm", "sa_admin_gag_message_perm",
[reason, "CALLER"],
["CALLER", player.PlayerName, reason])
: ("sa_player_gag_message_time", "sa_admin_gag_message_time",
new object[] { reason, time, "CALLER" },
new object[] { "CALLER", player.PlayerName, reason, time });
// Display center message to the gagged player
Helper.DisplayCenterMessage(player, messageKey, callerName, playerArgs);
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Increment the player's total gags count
PlayersInfo[player.UserId.Value].TotalGags++;
// Log the gag command and send Discord notification
if (!silent)
{
if (command == null)
Helper.LogCommand(caller, $"css_gag {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
else
Helper.LogCommand(caller, command);
}
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Gag, _localizer);
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Gag, reason, time);
}
[ConsoleCommand("css_addgag")]
[RequiresPermissions("@css/chat")]
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnAddGagCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
// Set caller name
var callerName = caller == null ? "Console" : caller.PlayerName;
// Validate command arguments
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
// Validate and extract SteamID
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
{
command.ReplyToCommand("Invalid SteamID64.");
return;
}
var steamid = steamId.SteamId64.ToString();
var reason = command.ArgCount >= 3 && command.GetArg(3).Length > 0
? command.GetArg(3)
: (_localizer?["sa_unknown"] ?? "Unknown");
MuteManager muteManager = new(Database);
int.TryParse(command.GetArg(2), out var time);
// Get player and admin info
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
// Attempt to match player based on SteamID
var matches = Helper.GetPlayerFromSteamid64(steamid);
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
if (player != null && player.IsValid)
{
// Check if caller can target the player
if (!caller.CanTarget(player)) return;
// Perform the gag for an online player
Gag(caller, player, time, reason, callerName, muteManager, silent: true);
}
else
{
// Asynchronous gag operation for offline players
Task.Run(async () =>
{
await muteManager.AddMuteBySteamid(steamid, adminInfo, reason, time);
});
command.ReplyToCommand($"Player with steamid {steamid} is not online. Gag has been added offline.");
}
// Log the gag command and respond to the command
Helper.LogCommand(caller, command);
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Gag, reason, time);
}
[ConsoleCommand("css_ungag")]
[RequiresPermissions("@css/chat")]
[CommandHelper(minArgs: 1, usage: "<steamid or name> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnUngagCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerSteamId = caller?.SteamID.ToString() ?? "Console";
var pattern = command.GetArg(1);
var reason = command.GetArg(2);
if (pattern.Length <= 1)
{
command.ReplyToCommand($"Too short pattern to search.");
return;
}
Helper.LogCommand(caller, command);
var muteManager = new MuteManager(Database);
// Check if pattern is a valid SteamID64
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
{
var matches = Helper.GetPlayerFromSteamid64(steamId.SteamId64.ToString());
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
if (player != null && player.IsValid)
{
PlayerPenaltyManager.RemovePenaltiesByType(player.Slot, PenaltyType.Gag);
Task.Run(async () =>
{
await muteManager.UnmutePlayer(player.SteamID.ToString(), callerSteamId, reason);
});
command.ReplyToCommand($"Ungaged player {player.PlayerName}.");
return;
}
}
// If not a valid SteamID64, check by player name
var nameMatches = Helper.GetPlayerFromName(pattern);
var namePlayer = nameMatches.Count == 1 ? nameMatches.FirstOrDefault() : null;
if (namePlayer != null && namePlayer.IsValid)
{
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Gag);
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.UserId.Value].TotalGags > 0)
PlayersInfo[namePlayer.UserId.Value].TotalGags--;
Task.Run(async () =>
{
await muteManager.UnmutePlayer(namePlayer.SteamID.ToString(), callerSteamId, reason);
});
command.ReplyToCommand($"Ungaged player {namePlayer.PlayerName}.");
}
else
{
Task.Run(async () =>
{
await muteManager.UnmutePlayer(pattern, callerSteamId, reason);
});
command.ReplyToCommand($"Ungaged offline player with pattern {pattern}.");
}
}
[ConsoleCommand("css_mute")]
[RequiresPermissions("@css/chat")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnMuteCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerName = caller == null ? "Console" : caller.PlayerName;
var reason = _localizer?["sa_unknown"] ?? "Unknown";
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
{
return;
}
int.TryParse(command.GetArg(2), out var time);
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
reason = command.GetArg(3);
MuteManager muteManager = new(Database);
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Mute(caller, player, time, reason, callerName, muteManager, command);
}
});
}
internal void Mute(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, MuteManager? muteManager = null, CommandInfo? command = null, bool silent = false)
{
if (Database == null || !player.IsValid || !player.UserId.HasValue) return;
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller == null ? "Console" : caller.PlayerName;
muteManager ??= new MuteManager(Database);
// Get player and admin information
var playerInfo = PlayersInfo[player.UserId.Value];
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
// Set player's voice flags to muted
player.VoiceFlags = VoiceFlags.Muted;
// Asynchronously handle mute logic
Task.Run(async () =>
{
await muteManager.MutePlayer(playerInfo, adminInfo, reason, time, 1);
});
// Add penalty to the player's penalty manager
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Mute, DateTime.Now.AddMinutes(time), time);
// Determine message keys and arguments based on mute time (permanent or timed)
var (messageKey, activityMessageKey, playerArgs, adminActivityArgs) = time == 0
? ("sa_player_mute_message_perm", "sa_admin_mute_message_perm",
[reason, "CALLER"],
["CALLER", player.PlayerName, reason])
: ("sa_player_mute_message_time", "sa_admin_mute_message_time",
new object[] { reason, time, "CALLER" },
new object[] { "CALLER", player.PlayerName, reason, time });
// Display center message to the muted player
Helper.DisplayCenterMessage(player, messageKey, callerName, playerArgs);
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Increment the player's total mutes count
PlayersInfo[player.UserId.Value].TotalMutes++;
// Log the mute command and send Discord notification
if (!silent)
{
if (command == null)
Helper.LogCommand(caller, $"css_mute {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
else
Helper.LogCommand(caller, command);
}
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Mute, _localizer);
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Mute, reason, time);
}
[ConsoleCommand("css_addmute")]
[RequiresPermissions("@css/chat")]
[CommandHelper(minArgs: 1, usage: "<steamid> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnAddMuteCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
// Set caller name
var callerName = caller == null ? "Console" : caller.PlayerName;
// Validate command arguments
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
// Validate and extract SteamID
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
{
command.ReplyToCommand("Invalid SteamID64.");
return;
}
var steamid = steamId.SteamId64.ToString();
var reason = command.ArgCount >= 3 && command.GetArg(3).Length > 0
? command.GetArg(3)
: (_localizer?["sa_unknown"] ?? "Unknown");
MuteManager muteManager = new(Database);
int.TryParse(command.GetArg(2), out var time);
// Get player and admin info
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
// Attempt to match player based on SteamID
var matches = Helper.GetPlayerFromSteamid64(steamid);
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
if (player != null && player.IsValid)
{
// Check if caller can target the player
if (!caller.CanTarget(player)) return;
// Perform the mute for an online player
Mute(caller, player, time, reason, callerName, muteManager, silent: true);
}
else
{
// Asynchronous mute operation for offline players
Task.Run(async () =>
{
await muteManager.AddMuteBySteamid(steamid, adminInfo, reason, time, 1);
});
command.ReplyToCommand($"Player with steamid {steamid} is not online. Mute has been added offline.");
}
// Log the mute command and respond to the command
Helper.LogCommand(caller, command);
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Mute, reason, time);
}
[ConsoleCommand("css_unmute")]
[RequiresPermissions("@css/chat")]
[CommandHelper(minArgs: 1, usage: "<steamid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnUnmuteCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerSteamId = caller?.SteamID.ToString() ?? "Console";
var pattern = command.GetArg(1);
var reason = command.GetArg(2);
if (pattern.Length <= 1)
{
command.ReplyToCommand("Too short pattern to search.");
return;
}
Helper.LogCommand(caller, command);
var muteManager = new MuteManager(Database);
// Check if pattern is a valid SteamID64
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
{
var matches = Helper.GetPlayerFromSteamid64(steamId.SteamId64.ToString());
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
if (player != null && player.IsValid)
{
PlayerPenaltyManager.RemovePenaltiesByType(player.Slot, PenaltyType.Mute);
player.VoiceFlags = VoiceFlags.Normal;
Task.Run(async () =>
{
await muteManager.UnmutePlayer(player.SteamID.ToString(), callerSteamId, reason, 1);
});
command.ReplyToCommand($"Unmuted player {player.PlayerName}.");
return;
}
}
// If not a valid SteamID64, check by player name
var nameMatches = Helper.GetPlayerFromName(pattern);
var namePlayer = nameMatches.Count == 1 ? nameMatches.FirstOrDefault() : null;
if (namePlayer != null && namePlayer.IsValid)
{
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Mute);
namePlayer.VoiceFlags = VoiceFlags.Normal;
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.UserId.Value].TotalMutes > 0)
PlayersInfo[namePlayer.UserId.Value].TotalMutes--;
Task.Run(async () =>
{
await muteManager.UnmutePlayer(namePlayer.SteamID.ToString(), callerSteamId, reason, 1);
});
command.ReplyToCommand($"Unmuted player {namePlayer.PlayerName}.");
}
else
{
Task.Run(async () =>
{
await muteManager.UnmutePlayer(pattern, callerSteamId, reason, 1);
});
command.ReplyToCommand($"Unmuted offline player with pattern {pattern}.");
}
}
[ConsoleCommand("css_silence")]
[RequiresPermissions("@css/chat")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnSilenceCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerName = caller == null ? "Console" : caller.PlayerName;
var reason = _localizer?["sa_unknown"] ?? "Unknown";
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
{
return;
}
int.TryParse(command.GetArg(2), out var time);
if (command.ArgCount >= 3 && command.GetArg(3).Length > 0)
reason = command.GetArg(3);
MuteManager muteManager = new(Database);
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Silence(caller, player, time, reason, callerName, muteManager, command);
}
});
}
internal void Silence(CCSPlayerController? caller, CCSPlayerController player, int time, string reason, string? callerName = null, MuteManager? muteManager = null, CommandInfo? command = null, bool silent = false)
{
if (Database == null || !player.IsValid || !player.UserId.HasValue) return;
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller == null ? "Console" : caller.PlayerName;
muteManager ??= new MuteManager(Database);
// Get player and admin information
var playerInfo = PlayersInfo[player.UserId.Value];
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
// Asynchronously handle silence logic
Task.Run(async () =>
{
await muteManager.MutePlayer(playerInfo, adminInfo, reason, time, 2); // Assuming 2 is the type for silence
});
// Add penalty to the player's penalty manager
PlayerPenaltyManager.AddPenalty(player.Slot, PenaltyType.Silence, DateTime.Now.AddMinutes(time), time);
// Determine message keys and arguments based on silence time (permanent or timed)
var (messageKey, activityMessageKey, playerArgs, adminActivityArgs) = time == 0
? ("sa_player_silence_message_perm", "sa_admin_silence_message_perm",
[reason, "CALLER"],
["CALLER", player.PlayerName, reason])
: ("sa_player_silence_message_time", "sa_admin_silence_message_time",
new object[] { reason, time, "CALLER" },
new object[] { "CALLER", player.PlayerName, reason, time });
// Display center message to the silenced player
Helper.DisplayCenterMessage(player, messageKey, callerName, playerArgs);
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Increment the player's total silences count
PlayersInfo[player.UserId.Value].TotalSilences++;
// Log the silence command and send Discord notification
if (!silent)
{
if (command == null)
Helper.LogCommand(caller, $"css_silence {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time} {reason}");
else
Helper.LogCommand(caller, command);
}
Helper.SendDiscordPenaltyMessage(caller, player, reason, time, PenaltyType.Silence, _localizer);
SimpleAdminApi?.OnPlayerPenaltiedEvent(playerInfo, adminInfo, PenaltyType.Silence, reason, time);
}
[ConsoleCommand("css_addsilence")]
[RequiresPermissions("@css/chat")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> [time in minutes/0 perm] [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnAddSilenceCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
// Set caller name
var callerName = caller == null ? "Console" : caller.PlayerName;
// Validate command arguments
if (command.ArgCount < 2 || string.IsNullOrEmpty(command.GetArg(1))) return;
// Validate and extract SteamID
if (!Helper.ValidateSteamId(command.GetArg(1), out var steamId) || steamId == null)
{
command.ReplyToCommand("Invalid SteamID64.");
return;
}
var steamid = steamId.SteamId64.ToString();
var reason = command.ArgCount >= 3 && command.GetArg(3).Length > 0
? command.GetArg(3)
: (_localizer?["sa_unknown"] ?? "Unknown");
int.TryParse(command.GetArg(2), out var time);
MuteManager muteManager = new(Database);
// Get player and admin info
var adminInfo = caller != null && caller.UserId.HasValue ? PlayersInfo[caller.UserId.Value] : null;
// Attempt to match player based on SteamID
var matches = Helper.GetPlayerFromSteamid64(steamid);
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
if (player != null && player.IsValid)
{
// Check if caller can target the player
if (!caller.CanTarget(player)) return;
// Perform the silence for an online player
Silence(caller, player, time, reason, callerName, muteManager, silent: true);
}
else
{
// Asynchronous silence operation for offline players
Task.Run(async () =>
{
await muteManager.AddMuteBySteamid(steamid, adminInfo, reason, time, 2);
});
command.ReplyToCommand($"Player with steamid {steamid} is not online. Silence has been added offline.");
}
// Log the silence command and respond to the command
Helper.LogCommand(caller, command);
SimpleAdminApi?.OnPlayerPenaltiedAddedEvent(steamId, adminInfo, PenaltyType.Silence, reason, time);
}
[ConsoleCommand("css_unsilence")]
[RequiresPermissions("@css/chat")]
[CommandHelper(minArgs: 1, usage: "<steamid or name> [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnUnsilenceCommand(CCSPlayerController? caller, CommandInfo command)
{
if (Database == null) return;
var callerSteamId = caller?.SteamID.ToString() ?? "Console";
var pattern = command.GetArg(1);
var reason = command.GetArg(2);
if (pattern.Length <= 1)
{
command.ReplyToCommand("Too short pattern to search.");
return;
}
Helper.LogCommand(caller, command);
var muteManager = new MuteManager(Database);
// Check if pattern is a valid SteamID64
if (Helper.ValidateSteamId(pattern, out var steamId) && steamId != null)
{
var matches = Helper.GetPlayerFromSteamid64(steamId.SteamId64.ToString());
var player = matches.Count == 1 ? matches.FirstOrDefault() : null;
if (player != null && player.IsValid)
{
PlayerPenaltyManager.RemovePenaltiesByType(player.Slot, PenaltyType.Silence);
// Reset voice flags to normal
player.VoiceFlags = VoiceFlags.Normal;
Task.Run(async () =>
{
await muteManager.UnmutePlayer(player.SteamID.ToString(), callerSteamId, reason, 2); // Unmute by type 2 (silence)
});
command.ReplyToCommand($"Unsilenced player {player.PlayerName}.");
return;
}
}
// If not a valid SteamID64, check by player name
var nameMatches = Helper.GetPlayerFromName(pattern);
var namePlayer = nameMatches.Count == 1 ? nameMatches.FirstOrDefault() : null;
if (namePlayer != null && namePlayer.IsValid)
{
PlayerPenaltyManager.RemovePenaltiesByType(namePlayer.Slot, PenaltyType.Silence);
// Reset voice flags to normal
namePlayer.VoiceFlags = VoiceFlags.Normal;
if (namePlayer.UserId.HasValue && PlayersInfo[namePlayer.UserId.Value].TotalSilences > 0)
PlayersInfo[namePlayer.UserId.Value].TotalSilences--;
Task.Run(async () =>
{
await muteManager.UnmutePlayer(namePlayer.SteamID.ToString(), callerSteamId, reason, 2); // Unmute by type 2 (silence)
});
command.ReplyToCommand($"Unsilenced player {namePlayer.PlayerName}.");
}
else
{
Task.Run(async () =>
{
await muteManager.UnmutePlayer(pattern, callerSteamId, reason, 2); // Unmute by type 2 (silence)
});
command.ReplyToCommand($"Unsilenced offline player with pattern {pattern}.");
}
}
}

View file

@ -0,0 +1,94 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Core.Translations;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Menu;
namespace CS2_SimpleAdmin;
public partial class CS2_SimpleAdmin
{
[ConsoleCommand("css_vote")]
[RequiresPermissions("@css/generic")]
[CommandHelper(minArgs: 2, usage: "<question> [... options ...]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnVoteCommand(CCSPlayerController? caller, CommandInfo command)
{
if (command.ArgCount < 2 || _localizer == null)
return;
Helper.LogCommand(caller, command);
VoteAnswers.Clear();
var question = command.GetArg(1);
var answersCount = command.ArgCount;
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
for (var i = 2; i <= answersCount - 1; i++)
{
VoteAnswers.Add(command.GetArg(i), 0);
}
foreach (var player in Helper.GetValidPlayers())
{
using (new WithTemporaryCulture(player.GetLanguage()))
{
IMenu? voteMenu = MenuApi?.NewMenu(_localizer["sa_admin_vote_menu_title", question]);
if (voteMenu == null)
return;
//ChatMenu voteMenu = new(_localizer!["sa_admin_vote_menu_title", question]);
for (var i = 2; i <= answersCount - 1; i++)
{
voteMenu.AddMenuOption(command.GetArg(i), Helper.HandleVotes);
}
voteMenu.PostSelectAction = PostSelectAction.Close;
Helper.PrintToCenterAll(_localizer["sa_admin_vote_message", caller == null ? "Console" : caller.PlayerName, question]);
player.SendLocalizedMessage(_localizer,
"sa_admin_vote_message",
caller == null ? "Console" : caller.PlayerName,
question);
voteMenu.Open(player);
//MenuManager.OpenChatMenu(player, voteMenu);
}
}
VoteInProgress = true;
}
if (VoteInProgress)
{
AddTimer(30, () =>
{
foreach (var player in Helper.GetValidPlayers())
{
if (_localizer != null)
player.SendLocalizedMessage(_localizer,
"sa_admin_vote_message_results",
question);
}
foreach (var (key, value) in VoteAnswers)
{
foreach (var player in Helper.GetValidPlayers())
{
if (_localizer != null)
player.SendLocalizedMessage(_localizer,
"sa_admin_vote_message_results_answer",
key,
value);
}
}
VoteAnswers.Clear();
VoteInProgress = false;
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
}
}

View file

@ -0,0 +1,220 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
namespace CS2_SimpleAdmin;
public partial class CS2_SimpleAdmin
{
[ConsoleCommand("css_noclip", "Noclip a player.")]
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/cheats")]
public void OnNoclipCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player.IsValid &&
player is { PawnIsAlive: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
NoClip(caller, player, callerName);
}
});
}
internal static void NoClip(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
{
if (!player.IsValid) return;
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller != null ? caller.PlayerName : "Console";
// Toggle no-clip mode for the player
player.Pawn.Value?.ToggleNoclip();
// Determine message keys and arguments for the no-clip notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_noclip_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Log the command
if (command == null)
{
Helper.LogCommand(caller, $"css_noclip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
}
else
{
Helper.LogCommand(caller, command);
}
}
[ConsoleCommand("css_god")]
[RequiresPermissions("@css/cheats")]
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
if (player.Connected != PlayerConnectedState.PlayerConnected)
return;
if (caller!.CanTarget(player))
{
God(caller, player, command);
}
});
}
internal static void God(CCSPlayerController? caller, CCSPlayerController player, CommandInfo? command = null)
{
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
var callerName = caller != null ? caller.PlayerName : "Console";
// Toggle god mode for the player
if (!GodPlayers.Add(player.Slot))
{
GodPlayers.Remove(player.Slot);
}
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_god {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
else
Helper.LogCommand(caller, command);
// Determine message key and arguments for the god mode notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_god_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
}
[ConsoleCommand("css_freeze", "Freeze a player.")]
[CommandHelper(1, "<#userid or name> [duration]")]
[RequiresPermissions("@css/slay")]
public void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
int.TryParse(command.GetArg(2), out var time);
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, PawnIsAlive: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Freeze(caller, player, time, callerName, command);
}
});
}
internal static void Freeze(CCSPlayerController? caller, CCSPlayerController player, int time, string? callerName = null, CommandInfo? command = null)
{
if (!player.IsValid) return;
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller != null ? caller.PlayerName : "Console";
// Freeze player pawn
player.Pawn.Value?.Freeze();
// Determine message keys and arguments for the freeze notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_freeze_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Schedule unfreeze for the player if time is specified
if (time > 0)
{
Instance.AddTimer(time, () => player.Pawn.Value?.Unfreeze(), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
// Log the command and send Discord notification
if (command == null)
Helper.LogCommand(caller, $"css_freeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time}");
else
Helper.LogCommand(caller, command);
}
[ConsoleCommand("css_unfreeze", "Unfreeze a player.")]
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/slay")]
public void OnUnfreezeCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, PawnIsAlive: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
Unfreeze(caller, player, callerName, command);
});
}
internal static void Unfreeze(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
{
if (!player.IsValid) return;
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller != null ? caller.PlayerName : "Console";
// Unfreeze player pawn
player.Pawn.Value?.Unfreeze();
// Determine message keys and arguments for the unfreeze notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_unfreeze_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Log the command and send Discord notification
if (command == null)
Helper.LogCommand(caller, $"css_unfreeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
else
Helper.LogCommand(caller, command);
}
}

View file

@ -0,0 +1,821 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
namespace CS2_SimpleAdmin;
public partial class CS2_SimpleAdmin
{
[ConsoleCommand("css_slay")]
[RequiresPermissions("@css/slay")]
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnSlayCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
Slay(caller, player, callerName, command);
});
}
internal static void Slay(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
{
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected) return;
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller != null ? caller.PlayerName : "Console";
// Make the player commit suicide
player.CommitSuicide(false, true);
// Determine message keys and arguments for the slay notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_slay_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
// Log the command and send Discord notification
if (command == null)
Helper.LogCommand(caller, $"css_slay {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
else
Helper.LogCommand(caller, command);
}
[ConsoleCommand("css_give")]
[RequiresPermissions("@css/cheats")]
[CommandHelper(minArgs: 2, usage: "<#userid or name> <weapon>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnGiveCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
var weaponName = command.GetArg(2);
// check if item is typed
if (weaponName.Length < 5)
{
command.ReplyToCommand($"No weapon typed.");
return;
}
// check if item is valid
if (!weaponName.Contains("weapon_") && !weaponName.Contains("item_"))
{
command.ReplyToCommand($"{weaponName} is not a valid item.");
return;
}
// check if weapon is knife
if (weaponName.Contains("_knife") || weaponName.Contains("bayonet"))
{
if (CoreConfig.FollowCS2ServerGuidelines)
{
command.ReplyToCommand($"Cannot Give {weaponName} because it's illegal to be given.");
return;
}
}
playersToTarget.ForEach(player =>
{
if (player.Connected != PlayerConnectedState.PlayerConnected)
return;
GiveWeapon(caller, player, weaponName, callerName, command);
});
}
private static void GiveWeapon(CCSPlayerController? caller, CCSPlayerController player, string weaponName, string? callerName = null, CommandInfo? command = null)
{
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller != null ? caller.PlayerName : "Console";
// Give weapon to the player
player.GiveNamedItem(weaponName);
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_giveweapon {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {weaponName}");
else
Helper.LogCommand(caller, command);
// Determine message keys and arguments for the weapon give notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_give_message",
new object[] { "CALLER", player.PlayerName, weaponName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
}
internal static void GiveWeapon(CCSPlayerController? caller, CCSPlayerController player, CsItem weapon, string? callerName = null, CommandInfo? command = null)
{
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller != null ? caller.PlayerName : "Console";
// Give weapon to the player
player.GiveNamedItem(weapon);
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_giveweapon {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {weapon.ToString()}");
else
Helper.LogCommand(caller, command);
// Determine message keys and arguments for the weapon give notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_give_message",
new object[] { "CALLER", player.PlayerName, weapon.ToString() });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
}
[ConsoleCommand("css_strip")]
[RequiresPermissions("@css/slay")]
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnStripCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
StripWeapons(caller, player, callerName, command);
}
});
}
internal static void StripWeapons(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
{
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller != null ? caller.PlayerName : "Console";
// Check if player is valid, alive, and connected
if (!player.IsValid || !player.PawnIsAlive || player.Connected != PlayerConnectedState.PlayerConnected)
return;
// Strip weapons from the player
player.RemoveWeapons();
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_strip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
else
Helper.LogCommand(caller, command);
// Determine message keys and arguments for the weapon strip notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_strip_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
}
[ConsoleCommand("css_hp")]
[RequiresPermissions("@css/slay")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> <health>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnHpCommand(CCSPlayerController? caller, CommandInfo command)
{
int.TryParse(command.GetArg(2), out var health);
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
SetHp(caller, player, health, command);
}
});
}
internal static void SetHp(CCSPlayerController? caller, CCSPlayerController player, int health, CommandInfo? command = null)
{
if (!player.IsValid || player.IsHLTV) return;
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
var callerName = caller != null ? caller.PlayerName : "Console";
// Set player's health
player.SetHp(health);
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_hp {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {health}");
else
Helper.LogCommand(caller, command);
// Determine message keys and arguments for the HP set notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_hp_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
}
[ConsoleCommand("css_speed")]
[RequiresPermissions("@css/slay")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> <speed>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnSpeedCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
float.TryParse(command.GetArg(2), out var speed);
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
if (player.Connected != PlayerConnectedState.PlayerConnected)
return;
if (caller!.CanTarget(player))
{
SetSpeed(caller, player, speed, command);
}
});
}
internal static void SetSpeed(CCSPlayerController? caller, CCSPlayerController player, float speed, CommandInfo? command = null)
{
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
var callerName = caller != null ? caller.PlayerName : "Console";
// Set player's speed
player.SetSpeed(speed);
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_speed {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {speed}");
else
Helper.LogCommand(caller, command);
// Determine message keys and arguments for the speed set notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_speed_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
}
[ConsoleCommand("css_gravity")]
[RequiresPermissions("@css/slay")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> <gravity>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnGravityCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
float.TryParse(command.GetArg(2), out var gravity);
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
if (player.Connected != PlayerConnectedState.PlayerConnected)
return;
if (caller!.CanTarget(player))
{
SetGravity(caller, player, gravity, command);
}
});
}
internal static void SetGravity(CCSPlayerController? caller, CCSPlayerController player, float gravity, CommandInfo? command = null)
{
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
var callerName = caller != null ? caller.PlayerName : "Console";
// Set player's gravity
player.SetGravity(gravity);
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_gravity {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {gravity}");
else
Helper.LogCommand(caller, command);
// Determine message keys and arguments for the gravity set notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_gravity_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
}
[ConsoleCommand("css_money")]
[RequiresPermissions("@css/slay")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> <money>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnMoneyCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
int.TryParse(command.GetArg(2), out var money);
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
if (player.Connected != PlayerConnectedState.PlayerConnected)
return;
if (caller!.CanTarget(player))
{
SetMoney(caller, player, money, command);
}
});
}
internal static void SetMoney(CCSPlayerController? caller, CCSPlayerController player, int money, CommandInfo? command = null)
{
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
var callerName = caller != null ? caller.PlayerName : "Console";
// Set player's money
player.SetMoney(money);
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_money {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {money}");
else
Helper.LogCommand(caller, command);
// Determine message keys and arguments for the money set notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_money_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller == null || !SilentPlayers.Contains(caller.Slot))
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
}
[ConsoleCommand("css_slap")]
[RequiresPermissions("@css/slay")]
[CommandHelper(minArgs: 1, usage: "<#userid or name> [damage]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnSlapCommand(CCSPlayerController? caller, CommandInfo command)
{
var damage = 0;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player.IsValid && player is { PawnIsAlive: true, IsHLTV: false }).ToList();
if (command.ArgCount >= 2)
{
int.TryParse(command.GetArg(2), out damage);
}
playersToTarget.ForEach(player =>
{
if (player.Connected != PlayerConnectedState.PlayerConnected)
return;
if (caller!.CanTarget(player))
{
Slap(caller, player, damage, command);
}
});
}
internal static void Slap(CCSPlayerController? caller, CCSPlayerController player, int damage, CommandInfo? command = null)
{
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
var callerName = caller != null ? caller.PlayerName : "Console";
// Apply slap damage to the player
player.Pawn.Value?.Slap(damage);
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_slap {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {damage}");
else
Helper.LogCommand(caller, command);
// Determine message key and arguments for the slap notification
var (activityMessageKey, adminActivityArgs) =
("sa_admin_slap_message",
new object[] { "CALLER", player.PlayerName });
// Display admin activity message to other players
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
if (_localizer != null)
{
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
}
[ConsoleCommand("css_team")]
[RequiresPermissions("@css/kick")]
[CommandHelper(minArgs: 2, usage: "<#userid or name> [<ct/tt/spec>] [-k]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
public void OnTeamCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var teamName = command.GetArg(2).ToLower();
var _teamName = "SPEC";
var teamNum = CsTeam.Spectator;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
switch (teamName)
{
case "ct":
case "counterterrorist":
teamNum = CsTeam.CounterTerrorist;
_teamName = "CT";
break;
case "t":
case "tt":
case "terrorist":
teamNum = CsTeam.Terrorist;
_teamName = "TT";
break;
case "swap":
_teamName = "SWAP";
break;
default:
teamNum = CsTeam.Spectator;
_teamName = "SPEC";
break;
}
var kill = command.GetArg(3).ToLower().Equals("-k");
playersToTarget.ForEach(player =>
{
ChangeTeam(caller, player, _teamName, teamNum, kill, command);
});
}
internal static void ChangeTeam(CCSPlayerController? caller, CCSPlayerController player, string teamName, CsTeam teamNum, bool kill, CommandInfo? command = null)
{
// Check if the player is valid and connected
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected)
return;
// Ensure the caller can target the player
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
var callerName = caller != null ? caller.PlayerName : "Console";
// Change team based on the provided teamName and conditions
if (!teamName.Equals("swap", StringComparison.OrdinalIgnoreCase))
{
if (player.PawnIsAlive && teamNum != CsTeam.Spectator && !kill && Instance.Config.OtherSettings.TeamSwitchType == 1)
player.SwitchTeam(teamNum);
else
player.ChangeTeam(teamNum);
}
else
{
if (player.TeamNum != (byte)CsTeam.Spectator)
{
var _teamNum = (CsTeam)player.TeamNum == CsTeam.Terrorist ? CsTeam.CounterTerrorist : CsTeam.Terrorist;
teamName = _teamNum == CsTeam.Terrorist ? "TT" : "CT";
if (player.PawnIsAlive && !kill && Instance.Config.OtherSettings.TeamSwitchType == 1)
player.SwitchTeam(_teamNum);
else
player.ChangeTeam(_teamNum);
}
}
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_team {player.PlayerName} {teamName}");
else
Helper.LogCommand(caller, command);
// Determine message key and arguments for the team change notification
var activityMessageKey = "sa_admin_team_message";
var adminActivityArgs = new object[] { "CALLER", player.PlayerName, teamName };
// Display admin activity message to other players
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
[ConsoleCommand("css_rename", "Rename a player.")]
[CommandHelper(1, "<#userid or name> <new name>")]
[RequiresPermissions("@css/kick")]
public void OnRenameCommand(CCSPlayerController? caller, CommandInfo command)
{
// Set default caller name if not provided
var callerName = caller == null ? "Console" : caller.PlayerName;
// Get the new name from the command arguments
var newName = command.GetArg(2);
// Check if the new name is valid
if (string.IsNullOrEmpty(newName))
return;
// Retrieve the targets based on the command
var targets = GetTarget(command);
if (targets == null) return;
// Filter out valid players from the targets
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
// Log the command
Helper.LogCommand(caller, command);
// Process each player to rename
playersToTarget.ForEach(player =>
{
// Check if the player is connected and can be targeted
if (player.Connected != PlayerConnectedState.PlayerConnected || !caller!.CanTarget(player))
return;
// Rename the player
player.Rename(newName);
// Determine message key and arguments for the rename notification
var activityMessageKey = "sa_admin_rename_message";
var adminActivityArgs = new object[] { "CALLER", player.PlayerName, newName };
// Display admin activity message to other players
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
});
}
[ConsoleCommand("css_prename", "Permanent rename a player.")]
[CommandHelper(1, "<#userid or name> <new name>")]
[RequiresPermissions("@css/ban")]
public void OnPRenameCommand(CCSPlayerController? caller, CommandInfo command)
{
// Set default caller name if not provided
var callerName = caller == null ? "Console" : caller.PlayerName;
// Get the new name from the command arguments
var newName = command.GetArg(2);
// Retrieve the targets based on the command
var targets = GetTarget(command);
if (targets == null) return;
// Filter out valid players from the targets
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
// Log the command
Helper.LogCommand(caller, command);
// Process each player to rename
playersToTarget.ForEach(player =>
{
// Check if the player is connected and can be targeted
if (player.Connected != PlayerConnectedState.PlayerConnected || !caller!.CanTarget(player))
return;
// Determine if the new name is valid and update the renamed players list
if (!string.IsNullOrEmpty(newName))
{
RenamedPlayers[player.SteamID] = newName;
player.Rename(newName);
}
else
{
RenamedPlayers.Remove(player.SteamID);
}
// Determine message key and arguments for the rename notification
var activityMessageKey = "sa_admin_rename_message";
var adminActivityArgs = new object[] { "CALLER", player.PlayerName, newName };
// Display admin activity message to other players
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
});
}
[ConsoleCommand("css_respawn", "Respawn a dead player.")]
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/cheats")]
public void OnRespawnCommand(CCSPlayerController? caller, CommandInfo command)
{
var callerName = caller == null ? "Console" : caller.PlayerName;
var targets = GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, IsHLTV: false }).ToList();
playersToTarget.ForEach(player =>
{
if (player.Connected != PlayerConnectedState.PlayerConnected)
return;
if (caller!.CanTarget(player))
{
Respawn(caller, player, callerName, command);
}
});
}
internal static void Respawn(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
{
// Check if the caller can target the player
if (!caller.CanTarget(player)) return;
// Set default caller name if not provided
callerName ??= caller == null ? "Console" : caller.PlayerName;
// Ensure the player's pawn is valid before attempting to respawn
if (_cBasePlayerControllerSetPawnFunc == null || player.PlayerPawn.Value == null || !player.PlayerPawn.IsValid) return;
// Perform the respawn operation
var playerPawn = player.PlayerPawn.Value;
_cBasePlayerControllerSetPawnFunc.Invoke(player, playerPawn, true, false);
VirtualFunction.CreateVoid<CCSPlayerController>(player.Handle, GameData.GetOffset("CCSPlayerController_Respawn"))(player);
if (player.UserId.HasValue && PlayersInfo.TryGetValue(player.UserId.Value, out var value) && value.DiePosition != null)
playerPawn.Teleport(value.DiePosition?.Position, value.DiePosition?.Angle);
// Log the command
if (command == null)
Helper.LogCommand(caller, $"css_respawn {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
else
Helper.LogCommand(caller, command);
// Determine message key and arguments for the respawn notification
var activityMessageKey = "sa_admin_respawn_message";
var adminActivityArgs = new object[] { "CALLER", player.PlayerName };
// Display admin activity message to other players
if (caller != null && SilentPlayers.Contains(caller.Slot)) return;
Helper.ShowAdminActivity(activityMessageKey, callerName, adminActivityArgs);
}
[ConsoleCommand("css_tp", "Teleport to a player.")]
[ConsoleCommand("css_tpto", "Teleport to a player.")]
[ConsoleCommand("css_goto", "Teleport to a player.")]
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/kick")]
public void OnGotoCommand(CCSPlayerController? caller, CommandInfo command)
{
// Check if the caller is valid and has a live pawn
if (caller == null || !caller.PawnIsAlive) return;
// Get the target players
var targets = GetTarget(command);
if (targets == null || targets.Count() > 1) return;
var playersToTarget = targets.Players
.Where(player => player is { IsValid: true, IsHLTV: false })
.ToList();
// Log the command
Helper.LogCommand(caller, command);
// Process each player to teleport
foreach (var player in playersToTarget.Where(player => player is { Connected: PlayerConnectedState.PlayerConnected, PawnIsAlive: true }).Where(caller.CanTarget))
{
if (caller.PlayerPawn.Value == null)
continue;
// Teleport the caller to the player and toggle noclip
caller.TeleportPlayer(player);
caller.PlayerPawn.Value.ToggleNoclip();
// Set a timer to toggle noclip back after 3 seconds
AddTimer(3, () => caller.PlayerPawn.Value.ToggleNoclip());
// Prepare message key and arguments for the teleport notification
var activityMessageKey = "sa_admin_tp_message";
var adminActivityArgs = new object[] { player.PlayerName };
// Show admin activity
if (!SilentPlayers.Contains(caller.Slot) && _localizer != null)
{
Helper.ShowAdminActivity(activityMessageKey, caller.PlayerName, adminActivityArgs);
}
}
}
[ConsoleCommand("css_bring", "Teleport a player to you.")]
[ConsoleCommand("css_tphere", "Teleport a player to you.")]
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/kick")]
public void OnBringCommand(CCSPlayerController? caller, CommandInfo command)
{
// Check if the caller is valid and has a live pawn
if (caller == null || !caller.PawnIsAlive) return;
// Get the target players
var targets = GetTarget(command);
if (targets == null || targets.Count() > 1) return;
var playersToTarget = targets.Players
.Where(player => player is { IsValid: true, IsHLTV: false })
.ToList();
// Log the command
Helper.LogCommand(caller, command);
// Process each player to teleport
foreach (var player in playersToTarget.Where(player => player is { Connected: PlayerConnectedState.PlayerConnected, PawnIsAlive: true }).Where(caller.CanTarget))
{
if (caller.PlayerPawn.Value == null)
continue;
// Teleport the player to the caller and toggle noclip
player.TeleportPlayer(caller);
caller.PlayerPawn.Value.ToggleNoclip();
// Set a timer to toggle noclip back after 3 seconds
AddTimer(3, () => caller.PlayerPawn.Value.ToggleNoclip());
// Prepare message key and arguments for the bring notification
var activityMessageKey = "sa_admin_bring_message";
var adminActivityArgs = new object[] { player.PlayerName };
// Show admin activity
if (!SilentPlayers.Contains(caller.Slot) && _localizer != null)
{
Helper.ShowAdminActivity(activityMessageKey, caller.PlayerName, adminActivityArgs);
}
}
}
}