Merge branch 'main' into feature/menu

# Conflicts:
#	CS2-SimpleAdmin.cs
This commit is contained in:
Valentin Barat
2024-02-12 11:41:44 +01:00
16 changed files with 1400 additions and 689 deletions

View File

@@ -1,5 +1,6 @@
using CounterStrikeSharp.API.Modules.Entities;
using Dapper;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
namespace CS2_SimpleAdmin
@@ -50,7 +51,7 @@ namespace CS2_SimpleAdmin
{
DateTime now = DateTime.Now;
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
string sql = "SELECT flags, immunity, ends FROM sa_admins WHERE player_steamid = @PlayerSteamID AND (ends IS NULL OR ends > @CurrentTime) AND (server_id IS NULL OR server_id = @serverid)";
List<dynamic>? activeFlags = (await connection.QueryAsync(sql, new { PlayerSteamID = steamId, CurrentTime = now, serverid = CS2_SimpleAdmin.ServerId }))?.ToList();
@@ -148,59 +149,66 @@ namespace CS2_SimpleAdmin
{
DateTime now = DateTime.Now;
await using var connection = _database.GetConnection();
string sql = "SELECT player_steamid, flags, immunity, ends FROM sa_admins WHERE (ends IS NULL OR ends > @CurrentTime) AND (server_id IS NULL OR server_id = @serverid)";
List<dynamic>? activeFlags = (await connection.QueryAsync(sql, new { CurrentTime = now, serverid = CS2_SimpleAdmin.ServerId }))?.ToList();
if (activeFlags == null)
try
{
return new List<(string, List<string>, int, DateTime?)>();
}
await using var connection = await _database.GetConnectionAsync();
List<(string, List<string>, int, DateTime?)> filteredFlagsWithImmunity = new List<(string, List<string>, int, DateTime?)>();
string sql = "SELECT player_steamid, flags, immunity, ends FROM sa_admins WHERE (ends IS NULL OR ends > @CurrentTime) AND (server_id IS NULL OR server_id = @serverid)";
List<dynamic>? activeFlags = (await connection.QueryAsync(sql, new { CurrentTime = now, serverid = CS2_SimpleAdmin.ServerId }))?.ToList();
foreach (dynamic flags in activeFlags)
{
if (flags is not IDictionary<string, object> flagsDict)
if (activeFlags == null)
{
continue;
return new List<(string, List<string>, int, DateTime?)>();
}
if (!flagsDict.TryGetValue("player_steamid", out var steamIdObj) ||
!flagsDict.TryGetValue("flags", out var flagsValueObj) ||
!flagsDict.TryGetValue("immunity", out var immunityValueObj) ||
!flagsDict.TryGetValue("ends", out var endsObj))
{
//Console.WriteLine("One or more required keys are missing.");
continue;
}
List<(string, List<string>, int, DateTime?)> filteredFlagsWithImmunity = new List<(string, List<string>, int, DateTime?)>();
DateTime? ends = null;
if (endsObj != null) // Check if "ends" is not null
foreach (dynamic flags in activeFlags)
{
if (!DateTime.TryParse(endsObj.ToString(), out var parsedEnds))
if (flags is not IDictionary<string, object> flagsDict)
{
//Console.WriteLine("Failed to parse 'ends' value.");
continue;
}
ends = parsedEnds;
if (!flagsDict.TryGetValue("player_steamid", out var steamIdObj) ||
!flagsDict.TryGetValue("flags", out var flagsValueObj) ||
!flagsDict.TryGetValue("immunity", out var immunityValueObj) ||
!flagsDict.TryGetValue("ends", out var endsObj))
{
//Console.WriteLine("One or more required keys are missing.");
continue;
}
DateTime? ends = null;
if (endsObj != null) // Check if "ends" is not null
{
if (!DateTime.TryParse(endsObj.ToString(), out var parsedEnds))
{
//Console.WriteLine("Failed to parse 'ends' value.");
continue;
}
ends = parsedEnds;
}
if (!(steamIdObj is string steamId) ||
!(flagsValueObj is string flagsValue) ||
!int.TryParse(immunityValueObj.ToString(), out var immunityValue))
{
//Console.WriteLine("Failed to parse one or more values.");
continue;
}
filteredFlagsWithImmunity.Add((steamId, flagsValue.Split(',').ToList(), immunityValue, ends));
}
if (!(steamIdObj is string steamId) ||
!(flagsValueObj is string flagsValue) ||
!int.TryParse(immunityValueObj.ToString(), out var immunityValue))
{
//Console.WriteLine("Failed to parse one or more values.");
continue;
}
filteredFlagsWithImmunity.Add((steamId, flagsValue.Split(',').ToList(), immunityValue, ends));
return filteredFlagsWithImmunity;
}
catch (Exception)
{
return new List<(string, List<string>, int, DateTime?)>();
}
return filteredFlagsWithImmunity;
}
public async Task GiveAllFlags()
@@ -236,7 +244,7 @@ namespace CS2_SimpleAdmin
//_adminCache.TryRemove(playerSteamId, out _);
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
string sql = "";
@@ -265,7 +273,7 @@ namespace CS2_SimpleAdmin
else
futureTime = null;
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
var sql = "INSERT INTO `sa_admins` (`player_steamid`, `player_name`, `flags`, `immunity`, `ends`, `created`, `server_id`) " +
"VALUES (@playerSteamid, @playerName, @flags, @immunity, @ends, @created, @serverid)";
@@ -286,10 +294,19 @@ namespace CS2_SimpleAdmin
public async Task DeleteOldAdmins()
{
await using var connection = _database.GetConnection();
try
{
await using var connection = await _database.GetConnectionAsync();
string sql = "DELETE FROM sa_admins WHERE ends IS NOT NULL AND ends <= @CurrentTime";
await connection.ExecuteAsync(sql, new { CurrentTime = DateTime.Now });
}
catch (Exception)
{
if (CS2_SimpleAdmin._logger != null)
CS2_SimpleAdmin._logger.LogCritical("Unable to remove expired admins");
}
string sql = "DELETE FROM sa_admins WHERE ends IS NOT NULL AND ends <= @CurrentTime";
await connection.ExecuteAsync(sql, new { CurrentTime = DateTime.Now });
}
}
}

View File

@@ -1,4 +1,5 @@
using Dapper;
using Microsoft.Extensions.Logging;
namespace CS2_SimpleAdmin
{
@@ -18,7 +19,7 @@ namespace CS2_SimpleAdmin
DateTime now = DateTime.Now;
DateTime futureTime = now.AddMinutes(time);
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
var sql = "INSERT INTO `sa_bans` (`player_steamid`, `player_name`, `player_ip`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`) " +
"VALUES (@playerSteamid, @playerName, @playerIp, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid)";
@@ -45,7 +46,7 @@ namespace CS2_SimpleAdmin
DateTime now = DateTime.Now;
DateTime futureTime = now.AddMinutes(time);
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
var sql = "INSERT INTO `sa_bans` (`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`) " +
"VALUES (@playerSteamid, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid)";
@@ -70,7 +71,7 @@ namespace CS2_SimpleAdmin
DateTime now = DateTime.Now;
DateTime futureTime = now.AddMinutes(time);
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
var sql = "INSERT INTO `sa_bans` (`player_ip`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `server_id`) " +
"VALUES (@playerIp, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @serverid)";
@@ -90,18 +91,23 @@ namespace CS2_SimpleAdmin
public async Task<bool> IsPlayerBanned(PlayerInfo player)
{
if (player == null)
if (player.SteamId == null && player.IpAddress == null)
{
return false;
}
#if DEBUG
if (CS2_SimpleAdmin._logger != null)
CS2_SimpleAdmin._logger.LogCritical($"IsPlayerBanned for {player.Name}");
#endif
DateTime currentTimeUtc = DateTime.UtcNow;
string sql = "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)";
int banCount = 0;
try
{
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
var parameters = new
{
@@ -119,12 +125,13 @@ namespace CS2_SimpleAdmin
return banCount > 0;
}
public async Task<int> GetPlayerBans(PlayerInfo player)
{
string sql = "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)";
int banCount;
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
if (!string.IsNullOrEmpty(player.IpAddress))
{
@@ -145,7 +152,7 @@ namespace CS2_SimpleAdmin
return;
}
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
string sqlUnban = "UPDATE sa_bans SET status = 'UNBANNED' WHERE player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern AND status = 'ACTIVE'";
await connection.ExecuteAsync(sqlUnban, new { pattern = playerPattern });
@@ -153,10 +160,18 @@ namespace CS2_SimpleAdmin
public async Task ExpireOldBans()
{
await using var connection = _database.GetConnection();
try
{
await using var connection = await _database.GetConnectionAsync();
string sql = "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime";
await connection.ExecuteAsync(sql, new { CurrentTime = DateTime.Now });
string sql = "UPDATE sa_bans SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime";
await connection.ExecuteAsync(sql, new { CurrentTime = DateTime.Now });
}
catch (Exception)
{
if (CS2_SimpleAdmin._logger != null)
CS2_SimpleAdmin._logger.LogCritical("Unable to remove expired bans");
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,10 +6,11 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.159" />
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.166" />
<PackageReference Include="Dapper" Version="*" />
<PackageReference Include="MySqlConnector" Version="2.3.5" />
<PackageReference Include="Newtonsoft.Json" Version="*" />
@@ -17,6 +18,10 @@
<ItemGroup>
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<None Update="database_setup.sql" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View File

@@ -1,19 +1,31 @@
using MySqlConnector;
using Microsoft.Extensions.Logging;
using MySqlConnector;
namespace CS2_SimpleAdmin;
public class Database
namespace CS2_SimpleAdmin
{
private readonly string _dbConnectionString;
public Database(string dbConnectionString)
public class Database
{
_dbConnectionString = dbConnectionString;
}
private readonly string _dbConnectionString;
public MySqlConnection GetConnection()
{
var connection = new MySqlConnection(_dbConnectionString);
connection.Open();
return connection;
public Database(string dbConnectionString)
{
_dbConnectionString = dbConnectionString;
}
public async Task<MySqlConnection> GetConnectionAsync()
{
try
{
var connection = new MySqlConnection(_dbConnectionString);
await connection.OpenAsync();
return connection;
}
catch (Exception)
{
if (CS2_SimpleAdmin._logger != null)
CS2_SimpleAdmin._logger.LogCritical("Unable to connect to database");
throw;
}
}
}
}
}

273
Events.cs
View File

@@ -6,10 +6,8 @@ using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Cvars;
using Dapper;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
using System.Data;
using System.Text;
using static CounterStrikeSharp.API.Core.Listeners;
namespace CS2_SimpleAdmin;
@@ -17,44 +15,12 @@ public partial class CS2_SimpleAdmin
{
private void registerEvents()
{
//RegisterListener<OnClientAuthorized>(OnClientAuthorized);
//RegisterListener<OnClientConnect>(OnClientConnect);
//RegisterListener<OnClientPutInServer>(OnClientPutInServer);
//RegisterListener<OnClientDisconnect>(OnClientDisconnect);
//RegisterEventHandler<EventPlayerConnectFull>(OnPlayerFullConnect);
RegisterListener<OnMapStart>(OnMapStart);
//RegisterEventHandler<EventPlayerHurt>(OnPlayerHurt);
//RegisterEventHandler<EventRoundStart>(OnRoundStart);
RegisterListener<Listeners.OnClientPutInServer>(OnClientPutInServer);
RegisterListener<Listeners.OnMapStart>(OnMapStart);
AddCommandListener("say", OnCommandSay);
AddCommandListener("say_team", OnCommandTeamSay);
}
/*private HookResult OnPlayerFullConnect(EventPlayerConnectFull @event, GameEventInfo info)
{
CCSPlayerController? player = @event.Userid;
if (player is null || player.IsBot || player.IsHLTV) return HookResult.Continue;
PlayerInfo playerInfo = new PlayerInfo
{
UserId = player.UserId,
Index = (ushort)player.UserId,
SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(),
Name = player?.PlayerName,
IpAddress = player?.IpAddress?.Split(":")[0]
};
Task.Run(async () =>
{
Server.NextFrame(() =>
{
if (player is null) return;
});
});
return HookResult.Continue;
}
*/
[GameEventHandler]
private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info)
{
@@ -69,10 +35,10 @@ public partial class CS2_SimpleAdmin
{
if (player is null || !player.IsValid || player.IsBot || player.IsHLTV || info.GetArg(1).Length == 0) return HookResult.Continue;
if (player != null && gaggedPlayers.Contains(player.Slot))
{
PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager();
if (playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) || playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence))
return HookResult.Handled;
}
return HookResult.Continue;
}
@@ -81,10 +47,10 @@ public partial class CS2_SimpleAdmin
{
if (player is null || !player.IsValid || player.IsBot || player.IsHLTV || info.GetArg(1).Length == 0) return HookResult.Continue;
if (player != null && gaggedPlayers.Contains(player.Slot))
{
PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager();
if (playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) || playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence))
return HookResult.Handled;
}
if (info.GetArg(1).StartsWith("@"))
{
@@ -114,23 +80,17 @@ public partial class CS2_SimpleAdmin
return HookResult.Continue;
}
[GameEventHandler]
public HookResult OnPlayerConnect(EventPlayerConnectFull @event, GameEventInfo info)
public void OnClientPutInServer(int playerSlot)
{
if (!@event.Userid.IsValid || !@event.Userid.PlayerPawn.IsValid)
return HookResult.Continue;
CCSPlayerController? player = @event.Userid;
CCSPlayerController? player = Utilities.GetPlayerFromSlot(playerSlot);
#if DEBUG
Logger.LogCritical("[OnPlayerConnect] Before check");
#endif
if (_database == null || player is null || !player.IsValid || player.IsBot || player.IsHLTV)
return HookResult.Continue;
#endif
if (player is null || !player.IsValid || player.IsBot || player.IsHLTV) return;
#if DEBUG
Logger.LogCritical("[OnPlayerConnect] After Check");
#endif
string? ipAddress = !string.IsNullOrEmpty(player.IpAddress) ? player.IpAddress.Split(":")[0] : null;
if (
@@ -138,13 +98,13 @@ public partial class CS2_SimpleAdmin
bannedPlayers.Contains(player.SteamID.ToString())
)
{
Server.NextFrame(() =>
{
Helper.KickPlayer((ushort)player.UserId!, "Banned");
});
return HookResult.Continue;
Helper.KickPlayer((ushort)player.UserId!, "Banned");
return;
}
if (_database == null)
return;
PlayerInfo playerInfo = new PlayerInfo
{
UserId = player.UserId,
@@ -155,19 +115,18 @@ public partial class CS2_SimpleAdmin
IpAddress = ipAddress
};
BanManager _banManager = new(_database, Config);
MuteManager _muteManager = new(_database);
PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager();
Task.Run(async () =>
{
BanManager _banManager = new(_database, Config);
MuteManager _muteManager = new(_database);
List<dynamic> activeMutes = await _muteManager.IsPlayerMuted(playerInfo.SteamId);
if (await _banManager.IsPlayerBanned(playerInfo))
{
if (playerInfo.IpAddress != null && !bannedPlayers.Contains(playerInfo.IpAddress))
bannedPlayers.Add(playerInfo.IpAddress);
if (!bannedPlayers.Contains(playerInfo.SteamId))
if (playerInfo.SteamId != null && !bannedPlayers.Contains(playerInfo.SteamId))
bannedPlayers.Add(playerInfo.SteamId);
Server.NextFrame(() =>
@@ -179,113 +138,151 @@ public partial class CS2_SimpleAdmin
return;
}
List<dynamic> activeMutes = await _muteManager.IsPlayerMuted(playerInfo.SteamId);
if (activeMutes.Count > 0)
{
foreach (var mute in activeMutes)
foreach (dynamic mute in activeMutes)
{
string muteType = mute.type;
DateTime ends = mute.ends;
int duration = mute.duration;
if (muteType == "GAG")
{
// Chat mute
if (playerInfo.Slot.HasValue && !gaggedPlayers.Contains(playerInfo.Slot.Value))
gaggedPlayers.Add(playerInfo.Slot.Value);
if (TagsDetected)
playerPenaltyManager.AddPenalty(playerInfo.Slot, PenaltyType.Gag, ends, duration);
Server.NextFrame(() =>
{
Server.NextFrame(() =>
if (TagsDetected)
{
Server.ExecuteCommand($"css_tag_mute {playerInfo.SteamId}");
});
}
}
});
}
else if (muteType == "MUTE")
{
// Voice mute
playerPenaltyManager.AddPenalty(playerInfo.Slot, PenaltyType.Mute, ends, duration);
Server.NextFrame(() =>
{
player.VoiceFlags = VoiceFlags.Muted;
});
}
else
{
playerPenaltyManager.AddPenalty(playerInfo.Slot, PenaltyType.Silence, ends, duration);
Server.NextFrame(() =>
{
player.VoiceFlags = VoiceFlags.Muted;
if (TagsDetected)
{
Server.ExecuteCommand($"css_tag_mute {playerInfo.SteamId}");
}
});
}
}
}
});
return HookResult.Continue;
return;
}
[GameEventHandler]
public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info)
{
if (!@event.Userid.IsValid || @event.Userid.IsBot)
if (@event.Userid is null || !@event.Userid.IsValid)
return HookResult.Continue;
CCSPlayerController? player = @event.Userid;
#if DEBUG
Logger.LogCritical("[OnClientDisconnect] Before");
#endif
if (player is null || !player.IsValid || player.IsBot || player.IsHLTV)
return HookResult.Continue;
if (player.IsBot || player.IsHLTV) return HookResult.Continue;
if (player.Connected == PlayerConnectedState.PlayerConnecting)
return HookResult.Continue;
#if DEBUG
Logger.LogCritical("[OnClientDisconnect] After Check");
#endif
if (gaggedPlayers.Contains(player.Slot))
{
gaggedPlayers = new ConcurrentBag<int>(gaggedPlayers.Where(item => item != player.Slot));
}
PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager();
playerPenaltyManager.RemoveAllPenalties(player.Slot);
//RemoveFromConcurrentBag(gaggedPlayers, player.Slot);
RemoveFromConcurrentBag(silentPlayers, player.Slot);
RemoveFromConcurrentBag(godPlayers, player.Slot);
if (silentPlayers.Contains(player.Slot))
if (player.AuthorizedSteamID != null && AdminSQLManager._adminCache.TryGetValue(player.AuthorizedSteamID, out DateTime? expirationTime)
&& expirationTime <= DateTime.Now)
{
silentPlayers = new ConcurrentBag<int>(silentPlayers.Where(item => item != player.Slot));
}
if (godPlayers.Contains(player.Slot))
{
godPlayers = new ConcurrentBag<int>(godPlayers.Where(item => item != player.Slot));
}
if (player.AuthorizedSteamID != null && AdminSQLManager._adminCache.ContainsKey(player.AuthorizedSteamID))
{
if (AdminSQLManager._adminCache.TryGetValue(player.AuthorizedSteamID, out DateTime? expirationTime) &&
expirationTime <= DateTime.Now)
{
AdminManager.ClearPlayerPermissions(player.AuthorizedSteamID);
AdminManager.RemovePlayerAdminData(player.AuthorizedSteamID);
}
AdminManager.ClearPlayerPermissions(player.AuthorizedSteamID);
AdminManager.RemovePlayerAdminData(player.AuthorizedSteamID);
}
if (TagsDetected)
NativeAPI.IssueServerCommand($"css_tag_unmute {player!.SteamID}");
Server.ExecuteCommand($"css_tag_unmute {player.SteamID}");
return HookResult.Continue;
}
private void OnMapStart(string mapName)
{
gaggedPlayers.Clear();
Random random = new Random();
//gaggedPlayers.Clear();
godPlayers.Clear();
silentPlayers.Clear();
PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager();
playerPenaltyManager.RemoveAllPenalties();
_database = new(dbConnectionString);
if (_database == null) return;
AdminSQLManager _adminManager = new(_database);
AddTimer(60.0f, bannedPlayers.Clear, CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT | CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
AddTimer(120.0f, async () =>
AddTimer(random.Next(60, 80), async () =>
{
#if DEBUG
Logger.LogCritical("[OnMapStart] Expired check");
#endif
AdminSQLManager _adminManager = new(_database);
BanManager _banManager = new(_database, Config);
MuteManager _muteManager = new(_database);
await _banManager.ExpireOldBans();
await _muteManager.ExpireOldMutes();
await _adminManager.DeleteOldAdmins();
#if DEBUG
Logger.LogCritical("[OnMapStart] Expired check");
#endif
bannedPlayers.Clear();
Server.NextFrame(() =>
{
foreach (CCSPlayerController player in Helper.GetValidPlayers())
{
if (playerPenaltyManager.IsSlotInPenalties(player.Slot))
{
if (!playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute) && !playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence))
player.VoiceFlags = VoiceFlags.Normal;
if (!playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) && !playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence))
{
if (TagsDetected)
Server.ExecuteCommand($"css_tag_unmute {player!.SteamID}");
}
if (
!playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence) &&
!playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute) &&
!playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag)
)
{
player.VoiceFlags = VoiceFlags.Normal;
if (TagsDetected)
Server.ExecuteCommand($"css_tag_unmute {player!.SteamID}");
}
}
}
});
playerPenaltyManager.RemoveExpiredPenalties();
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT | CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
@@ -302,23 +299,56 @@ public partial class CS2_SimpleAdmin
await Task.Run(async () =>
{
using (var connection = _database.GetConnection())
AdminSQLManager _adminManager = new(_database);
try
{
await connection.ExecuteAsync(
"INSERT INTO `sa_servers` (address, hostname) VALUES (@address, @hostname) " +
"ON DUPLICATE KEY UPDATE hostname = @hostname",
new { address = $"{address}", hostname });
await using (var connection = await _database.GetConnectionAsync())
{
bool addressExists = await connection.ExecuteScalarAsync<bool>(
"SELECT COUNT(*) FROM sa_servers WHERE address = @address",
new { address });
int? serverId = await connection.ExecuteScalarAsync<int>(
"SELECT `id` FROM `sa_servers` WHERE `address` = @address",
new { address = $"{address}" });
if (!addressExists)
{
await connection.ExecuteAsync(
"INSERT INTO sa_servers (address, hostname) VALUES (@address, @hostname)",
new { address, hostname });
}
else
{
await connection.ExecuteAsync(
"UPDATE `sa_servers` SET hostname = @hostname WHERE address = @address",
new { address, hostname });
}
ServerId = serverId;
int? serverId = await connection.ExecuteScalarAsync<int>(
"SELECT `id` FROM `sa_servers` WHERE `address` = @address",
new { address });
await _adminManager.GiveAllFlags();
ServerId = serverId;
}
}
catch (Exception)
{
if (_logger != null)
_logger.LogCritical("Unable to create or get server_id");
}
await _adminManager.GiveAllFlags();
});
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
AddTimer(2.0f, () =>
{
ConVar? botQuota = ConVar.Find("bot_quota");
if (botQuota != null && botQuota.GetPrimitiveValue<int>() > 0)
{
Logger.LogInformation("Due to bugs with bots (game bug), consider disabling bots by setting `bot_quota 0` in the gamemode config if your server crashes after a map change.");
Logger.LogWarning("Due to bugs with bots (game bug), consider disabling bots by setting `bot_quota 0` in the gamemode config if your server crashes after a map change.");
Logger.LogCritical("Due to bugs with bots (game bug), consider disabling bots by setting `bot_quota 0` in the gamemode config if your server crashes after a map change.");
}
});
}
[GameEventHandler]
@@ -326,7 +356,9 @@ public partial class CS2_SimpleAdmin
{
CCSPlayerController? player = @event.Userid;
if (player is null || !player.IsValid || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || player.IsBot || player.IsHLTV || player.PlayerPawn.IsValid || player.Connected == PlayerConnectedState.PlayerDisconnecting)
if (player is null || @event.Attacker == null || !player.IsValid || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null
|| player.IsBot || player.IsHLTV || player.PlayerPawn.IsValid || player.Connected == PlayerConnectedState.PlayerDisconnecting
|| @event.Attacker.Connected == PlayerConnectedState.PlayerDisconnecting)
return HookResult.Continue;
if (godPlayers.Contains(player.Slot) && player.PawnIsAlive)
@@ -337,4 +369,5 @@ public partial class CS2_SimpleAdmin
return HookResult.Continue;
}
}

View File

@@ -2,9 +2,12 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Menu;
using CounterStrikeSharp.API.Modules.Utils;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
namespace CS2_SimpleAdmin
@@ -20,20 +23,25 @@ namespace CS2_SimpleAdmin
public static List<CCSPlayerController> GetPlayerFromSteamid64(string steamid)
{
return Utilities.GetPlayers().FindAll(x =>
x.AuthorizedSteamID != null &&
x.AuthorizedSteamID.SteamId64.ToString().Equals(steamid, StringComparison.OrdinalIgnoreCase)
return GetValidPlayers().FindAll(x =>
x.SteamID.ToString().Equals(steamid, StringComparison.OrdinalIgnoreCase)
);
}
public static List<CCSPlayerController> GetPlayerFromIp(string ipAddress)
{
return Utilities.GetPlayers().FindAll(x =>
return GetValidPlayers().FindAll(x =>
x.IpAddress != null &&
x.IpAddress.Split(":")[0].Equals(ipAddress)
);
}
public static List<CCSPlayerController> GetValidPlayers()
{
return Utilities.GetPlayers().FindAll(p => p != null && p.IsValid && p.SteamID.ToString().Length == 17 && p.Connected == PlayerConnectedState.PlayerConnected && !p.IsBot && !p.IsHLTV);
}
public static bool IsValidSteamID64(string input)
{
string pattern = @"^\d{17}$";
@@ -60,7 +68,6 @@ namespace CS2_SimpleAdmin
//Console.WriteLine($"Setting immunity for SteamID {steamid} to {immunity}");
if (flags != null)
{
//Console.WriteLine($"Applying flags to SteamID {steamid}:");
@@ -150,10 +157,33 @@ namespace CS2_SimpleAdmin
{
if (CS2_SimpleAdmin.voteInProgress && !CS2_SimpleAdmin.votePlayers.Contains(player.Slot))
{
option.Disabled = true;
CS2_SimpleAdmin.votePlayers.Add(player.Slot);
CS2_SimpleAdmin.voteAnswers[option.Text]++;
option.Disabled = true;
}
}
}
public class SchemaString<SchemaClass> : NativeObject where SchemaClass : NativeObject
{
public SchemaString(SchemaClass instance, string member) : base(Schema.GetSchemaValue<nint>(instance.Handle, typeof(SchemaClass).Name!, member))
{ }
public unsafe void Set(string str)
{
byte[] bytes = this.GetStringBytes(str);
for (int i = 0; i < bytes.Length; i++)
{
Unsafe.Write((void*)(this.Handle.ToInt64() + i), bytes[i]);
}
Unsafe.Write((void*)(this.Handle.ToInt64() + bytes.Length), 0);
}
private byte[] GetStringBytes(string str)
{
return Encoding.ASCII.GetBytes(str);
}
}
}

View File

@@ -1,5 +1,6 @@
using CounterStrikeSharp.API.Core;
using Dapper;
using Microsoft.Extensions.Logging;
namespace CS2_SimpleAdmin
{
@@ -16,7 +17,7 @@ namespace CS2_SimpleAdmin
{
if (player == null || player.SteamId == null) return;
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
DateTime now = DateTime.Now;
DateTime futureTime = now.AddMinutes(time);
@@ -24,6 +25,8 @@ namespace CS2_SimpleAdmin
string muteType = "GAG";
if (type == 1)
muteType = "MUTE";
else if (type == 2)
muteType = "SILENCE";
var sql = "INSERT INTO `sa_mutes` (`player_steamid`, `player_name`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`) " +
"VALUES (@playerSteamid, @playerName, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @type, @serverid)";
@@ -47,7 +50,7 @@ namespace CS2_SimpleAdmin
{
if (string.IsNullOrEmpty(playerSteamId)) return;
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
DateTime now = DateTime.Now;
DateTime futureTime = now.AddMinutes(time);
@@ -55,6 +58,8 @@ namespace CS2_SimpleAdmin
string muteType = "GAG";
if (type == 1)
muteType = "MUTE";
else if (type == 2)
muteType = "SILENCE";
var sql = "INSERT INTO `sa_mutes` (`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`) " +
"VALUES (@playerSteamid, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @type, @serverid)";
@@ -80,12 +85,17 @@ namespace CS2_SimpleAdmin
return new List<dynamic>();
}
await using var connection = _database.GetConnection();
DateTime currentTimeUtc = DateTime.UtcNow;
string sql = "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)";
#if DEBUG
if (CS2_SimpleAdmin._logger != null)
CS2_SimpleAdmin._logger.LogCritical($"IsPlayerMuted for {steamId}");
#endif
try
{
await using var connection = await _database.GetConnectionAsync();
DateTime currentTimeUtc = DateTime.UtcNow;
string sql = "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)";
var parameters = new { PlayerSteamID = steamId, CurrentTime = currentTimeUtc };
var activeMutes = (await connection.QueryAsync(sql, parameters)).ToList();
return activeMutes;
@@ -98,7 +108,7 @@ namespace CS2_SimpleAdmin
public async Task<int> GetPlayerMutes(string steamId)
{
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
int muteCount;
string sql = "SELECT COUNT(*) FROM sa_mutes WHERE player_steamid = @PlayerSteamID";
@@ -115,7 +125,7 @@ namespace CS2_SimpleAdmin
return;
}
await using var connection = _database.GetConnection();
await using var connection = await _database.GetConnectionAsync();
if (type == 2)
{
@@ -130,6 +140,8 @@ namespace CS2_SimpleAdmin
{
muteType = "MUTE";
}
else if (type == 2)
muteType = "SILENCE";
string sqlUnban = "UPDATE sa_mutes SET status = 'UNMUTED' WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE'";
await connection.ExecuteAsync(sqlUnban, new { pattern = playerPattern, muteType });
@@ -137,10 +149,18 @@ namespace CS2_SimpleAdmin
public async Task ExpireOldMutes()
{
await using var connection = _database.GetConnection();
try
{
await using var connection = await _database.GetConnectionAsync();
string sql = "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime";
await connection.ExecuteAsync(sql, new { CurrentTime = DateTime.Now });
string sql = "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime";
await connection.ExecuteAsync(sql, new { CurrentTime = DateTime.Now });
}
catch (Exception)
{
if (CS2_SimpleAdmin._logger != null)
CS2_SimpleAdmin._logger.LogCritical("Unable to remove expired mutes");
}
}
public async Task CheckMute(PlayerInfo player)
@@ -160,11 +180,6 @@ namespace CS2_SimpleAdmin
if (muteType == "GAG")
{
if (player.Slot.HasValue && !CS2_SimpleAdmin.gaggedPlayers.Contains(player.Slot.Value))
{
CS2_SimpleAdmin.gaggedPlayers.Add(player.Slot.Value);
}
if (CS2_SimpleAdmin.TagsDetected)
NativeAPI.IssueServerCommand($"css_tag_mute {player!.SteamId}");

View File

@@ -4,7 +4,7 @@
{
public int? Index { get; set; }
public int? UserId { get; set; }
public int? Slot { get; set; }
public int Slot { get; set; }
public string? SteamId { get; set; }
public string? Name { get; set; }
public string? IpAddress { get; set; }

139
PlayerPenaltyManager.cs Normal file
View File

@@ -0,0 +1,139 @@
using System.Collections.Concurrent;
namespace CS2_SimpleAdmin
{
public enum PenaltyType
{
Mute,
Gag,
Silence
}
public class PlayerPenaltyManager
{
private static ConcurrentDictionary<int, Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration)>>> penalties =
new ConcurrentDictionary<int, Dictionary<PenaltyType, List<(DateTime, int)>>>();
// Add a penalty for a player
public void AddPenalty(int slot, PenaltyType penaltyType, DateTime endDateTime, int durationSeconds)
{
if (!penalties.ContainsKey(slot))
{
penalties[slot] = new Dictionary<PenaltyType, List<(DateTime, int)>>();
}
if (!penalties[slot].ContainsKey(penaltyType))
{
penalties[slot][penaltyType] = new List<(DateTime, int)>();
}
penalties[slot][penaltyType].Add((endDateTime, durationSeconds));
}
public bool IsPenalized(int slot, PenaltyType penaltyType)
{
//Console.WriteLine($"Checking penalties for player with slot {slot} and penalty type {penaltyType}");
if (penalties.TryGetValue(slot, out var penaltyDict) && penaltyDict.TryGetValue(penaltyType, out var penaltiesList))
{
//Console.WriteLine($"Found penalties for player with slot {slot} and penalty type {penaltyType}");
DateTime now = DateTime.Now;
// Check if any active penalties exist
foreach (var penalty in penaltiesList.ToList())
{
// Check if the penalty is still active
if (penalty.Duration > 0 && now >= penalty.EndDateTime.AddSeconds(penalty.Duration))
{
//Console.WriteLine($"Removing expired penalty for player with slot {slot} and penalty type {penaltyType}");
penaltiesList.Remove(penalty); // Remove expired penalty
if (penaltiesList.Count == 0)
{
//Console.WriteLine($"No more penalties of type {penaltyType} for player with slot {slot}. Removing penalty type.");
penaltyDict.Remove(penaltyType); // Remove penalty type if no more penalties exist
}
}
else if (penalty.Duration == 0 || now < penalty.EndDateTime)
{
//Console.WriteLine($"Player with slot {slot} is penalized for type {penaltyType}");
// Return true if there's an active penalty
return true;
}
}
// Return false if no active penalties are found
//Console.WriteLine($"Player with slot {slot} is not penalized for type {penaltyType}");
return false;
}
// Return false if no penalties of the specified type were found for the player
//Console.WriteLine($"No penalties found for player with slot {slot} and penalty type {penaltyType}");
return false;
}
// Get the end datetime and duration of penalties for a player and penalty type
public List<(DateTime EndDateTime, int Duration)> GetPlayerPenalties(int slot, PenaltyType penaltyType)
{
if (penalties.TryGetValue(slot, out Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration)>>? penaltyDict) &&
penaltyDict.TryGetValue(penaltyType, out List<(DateTime EndDateTime, int Duration)>? penaltiesList) && penaltiesList != null)
{
return penaltiesList;
}
return new List<(DateTime EndDateTime, int Duration)>();
}
public bool IsSlotInPenalties(int slot)
{
return penalties.ContainsKey(slot);
}
// Remove all penalties for a player slot
public void RemoveAllPenalties(int slot)
{
if (penalties.ContainsKey(slot))
{
penalties.TryRemove(slot, out _);
}
}
// Remove all penalties
public void RemoveAllPenalties()
{
penalties.Clear();
}
// Remove all penalties of a selected type from a specific player
public void RemovePenaltiesByType(int slot, PenaltyType penaltyType)
{
if (penalties.TryGetValue(slot, out Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration)>>? penaltyDict) &&
penaltyDict.ContainsKey(penaltyType))
{
penaltyDict.Remove(penaltyType);
}
}
// Remove all expired penalties for all players and penalty types
public void RemoveExpiredPenalties()
{
DateTime now = DateTime.Now;
foreach (var kvp in penalties.ToList()) // Use ToList to avoid modification while iterating
{
var playerSlot = kvp.Key;
var penaltyDict = kvp.Value;
// Remove expired penalties for the player
foreach (var penaltiesList in penaltyDict.Values)
{
penaltiesList.RemoveAll(p => p.Duration > 0 && now >= p.EndDateTime.AddSeconds(p.Duration));
}
// Remove player slot if no penalties left
if (penaltyDict.Count == 0)
{
penalties.TryRemove(playerSlot, out _);
}
}
}
}
}

View File

@@ -1,5 +1,8 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
using System.Text;
using CounterStrikeSharp.API;
@@ -23,7 +26,9 @@ public static class PlayerUtils
public static bool CanTarget(this CCSPlayerController controller, CCSPlayerController target)
{
if (target.IsBot) return true;
return AdminManager.CanPlayerTarget(controller, target);
if (controller is null) return true;
return AdminManager.CanPlayerTarget(controller, target) || AdminManager.CanPlayerTarget(new SteamID(controller.SteamID), new SteamID(target.SteamID));
}
public static void SetSpeed(this CCSPlayerController controller, float speed)
@@ -81,19 +86,56 @@ public static class PlayerUtils
public static void Freeze(this CBasePlayerPawn pawn)
{
pawn.MoveType = MoveType_t.MOVETYPE_OBSOLETE;
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 1); // obsolete
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
}
public static void Unfreeze(this CBasePlayerPawn pawn)
{
pawn.MoveType = MoveType_t.MOVETYPE_WALK;
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 2); // walk
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
}
public static void ToggleNoclip(this CBasePlayerPawn pawn)
{
if (pawn.MoveType == MoveType_t.MOVETYPE_NOCLIP)
{
pawn.MoveType = MoveType_t.MOVETYPE_WALK;
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 2); // walk
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
}
else
{
pawn.MoveType = MoveType_t.MOVETYPE_NOCLIP;
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 8); // noclip
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
}
}
public static void Rename(this CCSPlayerController controller, string newName = "Unknown")
{
if (CS2_SimpleAdmin._plugin == null)
return;
SchemaString<CBasePlayerController> playerName = new SchemaString<CBasePlayerController>(controller, "m_iszPlayerName");
playerName.Set(newName + " ");
CS2_SimpleAdmin._plugin.AddTimer(0.25f, () =>
{
Utilities.SetStateChanged(controller, "CCSPlayerController", "m_szClan");
Utilities.SetStateChanged(controller, "CBasePlayerController", "m_iszPlayerName");
});
CS2_SimpleAdmin._plugin.AddTimer(0.3f, () =>
{
playerName.Set(newName);
});
CS2_SimpleAdmin._plugin.AddTimer(0.4f, () =>
{
Utilities.SetStateChanged(controller, "CBasePlayerController", "m_iszPlayerName");
});
}
private static void PerformSlap(CBasePlayerPawn pawn, int damage = 0)

View File

@@ -30,6 +30,9 @@ Manage your Counter-Strike 2 server by simple commands :)
- css_mute <#userid or name> [time in minutes/0 perm] [reason] - Mute player // @css/chat
- css_addmute <steamid> [time in minutes/0 perm] [reason] - Mute player via steamid64 // @css/chat
- css_unmute <steamid or name> - Unmute player // @css/chat
- css_silence <#userid or name> [time in minutes/0 perm] [reason] - Silence player // @css/chat
- css_addsilence <steamid> [time in minutes/0 perm] [reason] - Silence player via steamid64 // @css/chat
- css_unsilence <steamid or name> - Unsilence player // @css/chat
- css_give <#userid or name> <weapon> - Give weapon to player // @css/cheats
- css_strip <#userid or name> - Takes all of the player weapons // @css/slay
- css_hp <#userid or name> [health] - Set player health // @css/slay
@@ -49,6 +52,7 @@ Manage your Counter-Strike 2 server by simple commands :)
- css_noclip <#userid or name> - Toggle noclip for player // @css/cheats
- css_freeze <#userid or name> [duration] - Freeze player // @css/slay
- css_unfreeze <#userid or name> - Unfreeze player // @css/slay
- css_rename <#userid or name> <new name> - Rename player // @css/kick
- css_respawn <#userid or name> - Respawn player // @css/cheats
- css_cvar <cvar> <value> - Change cvar value // @css/cvar
- css_rcon <command> - Run command as server // @css/rcon
@@ -58,7 +62,7 @@ Manage your Counter-Strike 2 server by simple commands :)
```
### Requirments
- [CounterStrikeSharp](https://github.com/roflmuffin/CounterStrikeSharp/) **tested on v159**
- [CounterStrikeSharp](https://github.com/roflmuffin/CounterStrikeSharp/) **tested on v163**
- MySQL **tested on MySQL (MariaDB) Server version: 10.11.4-MariaDB-1~deb12u1 Debian 12**

50
database_setup.sql Normal file
View File

@@ -0,0 +1,50 @@
CREATE TABLE IF NOT EXISTS `sa_bans` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`player_steamid` VARCHAR(64),
`player_name` VARCHAR(128),
`player_ip` VARCHAR(128),
`admin_steamid` VARCHAR(64) NOT NULL,
`admin_name` VARCHAR(128) NOT NULL,
`reason` VARCHAR(255) NOT NULL,
`duration` INT NOT NULL,
`ends` TIMESTAMP NOT NULL,
`created` TIMESTAMP NOT NULL,
`server_id` INT NULL,
`status` ENUM('ACTIVE', 'UNBANNED', 'EXPIRED', '') NOT NULL DEFAULT 'ACTIVE'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `sa_mutes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`player_steamid` varchar(64) NOT NULL,
`player_name` varchar(128) NULL,
`admin_steamid` varchar(64) NOT NULL,
`admin_name` varchar(128) NOT NULL,
`reason` varchar(255) NOT NULL,
`duration` int(11) NOT NULL,
`ends` timestamp NOT NULL,
`created` timestamp NOT NULL,
`type` enum('GAG','MUTE','SILENCE','') NOT NULL DEFAULT 'GAG',
`server_id` INT NULL,
`status` enum('ACTIVE','UNMUTED','EXPIRED','') NOT NULL DEFAULT 'ACTIVE',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `sa_admins` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`player_steamid` varchar(64) NOT NULL,
`player_name` varchar(128) NOT NULL,
`flags` TEXT NOT NULL,
`immunity` varchar(64) NOT NULL DEFAULT '0',
`server_id` INT NULL,
`ends` timestamp NULL,
`created` timestamp NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `sa_servers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`address` varchar(64) NOT NULL,
`hostname` varchar(64) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `address` (`address`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

View File

@@ -8,6 +8,8 @@
"sa_player_gag_message_perm": "You have been gagged permanently for {lightred}{0}{default} by {lightred}{1}{default}!",
"sa_player_mute_message_time": "You have been muted for {lightred}{0}{default} for {lightred}{1}{default} minutes by {lightred}{2}{default}!",
"sa_player_mute_message_perm": "You have been muted permanently for {lightred}{0}{default} by {lightred}{1}{default}!",
"sa_player_silence_message_time": "You have been silenced for {lightred}{0}{default} for {lightred}{1}{default} minutes by {lightred}{2}{default}!",
"sa_player_silence_message_perm": "You have been silenced permanently for {lightred}{0}{default} by {lightred}{1}{default}!",
"sa_admin_ban_message_time": "Admin {lightred}{0}{default} banned {lightred}{1}{default} for {lightred}{2}{default} for {lightred}{3}{default} minutes!",
"sa_admin_ban_message_perm": "Admin {lightred}{0}{default} banned {lightred}{1}{default} permanently for {lightred}{2}{default}!",
"sa_admin_kick_message": "Admin {lightred}{0}{default} kicked {lightred}{1}{default} for {lightred}{2}{default}!",
@@ -15,6 +17,8 @@
"sa_admin_gag_message_perm": "Admin {lightred}{0}{default} gagged {lightred}{1}{default} permanently for {lightred}{2}{default}!",
"sa_admin_mute_message_time": "Admin {lightred}{0}{default} muted {lightred}{1}{default} for {lightred}{2}{default} for {lightred}{3}{default} minutes!",
"sa_admin_mute_message_perm": "Admin {lightred}{0}{default} muted {lightred}{1}{default} permanently for {lightred}{2}{default}!",
"sa_admin_silence_message_time": "Admin {lightred}{0}{default} silenced {lightred}{1}{default} for {lightred}{2}{default} for {lightred}{3}{default} minutes!",
"sa_admin_silence_message_perm": "Admin {lightred}{0}{default} silenced {lightred}{1}{default} permanently for {lightred}{2}{default}!",
"sa_admin_give_message": "Admin {lightred}{0}{default} gave {lightred}{1}{default} a {lightred}{2}{default}!",
"sa_admin_strip_message": "Admin {lightred}{0}{default} took all of player {lightred}{1}{default} weapons!",
"sa_admin_hp_message": "Admin {lightred}{0}{default} changed {lightred}{1}{default} hp amount{default}!",
@@ -26,6 +30,7 @@
"sa_admin_noclip_message": "Admin {lightred}{0}{default} toggled noclip for {lightred}{1}{default}!",
"sa_admin_freeze_message": "Admin {lightred}{0}{default} freezed {lightred}{1}{default}!",
"sa_admin_unfreeze_message": "Admin {lightred}{0}{default} umfreezed {lightred}{1}{default}!",
"sa_admin_rename_message": "Admin {lightred}{0}{default} changed {lightred}{1}{default} nickname to {lightred}{2}{default}!",
"sa_admin_respawn_message": "Admin {lightred}{0}{default} respawned {lightred}{1}{default}!",
"sa_admin_team_message": "Admin {lightred}{0}{default} transfered {lightred}{1}{default} to {lightred}{2}{default}!",
"sa_admin_vote_menu_title": "{lime}VOTING FOR {gold}{0}",

View File

@@ -8,6 +8,8 @@
"sa_player_gag_message_perm": "Has sido silenciado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}.",
"sa_player_mute_message_time": "Has sido silenciado por {lightred}{0}{default} durante {lightred}{1}{default} minutos por {lightred}{2}{default}.",
"sa_player_mute_message_perm": "Has sido silenciado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}.",
"sa_player_silence_message_time": "Has sido silenciado por {lightred}{0}{default} durante {lightred}{1}{default} minutos por {lightred}{2}{default}.",
"sa_player_silence_message_perm": "Has sido silenciado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}.",
"sa_admin_ban_message_time": "El administrador {lightred}{0}{default} ha baneado a {lightred}{1}{default} por {lightred}{2}{default} durante {lightred}{3}{default} minutos.",
"sa_admin_ban_message_perm": "El administrador {lightred}{0}{default} ha baneado a {lightred}{1}{default} permanentemente por {lightred}{2}{default}.",
"sa_admin_kick_message": "El administrador {lightred}{0}{default} ha expulsado a {lightred}{1}{default} por {lightred}{2}{default}.",
@@ -15,6 +17,8 @@
"sa_admin_gag_message_perm": "El administrador {lightred}{0}{default} ha silenciado a {lightred}{1}{default} permanentemente por {lightred}{2}{default}.",
"sa_admin_mute_message_time": "El administrador {lightred}{0}{default} ha silenciado a {lightred}{1}{default} por {lightred}{2}{default} durante {lightred}{3}{default} minutos.",
"sa_admin_mute_message_perm": "El administrador {lightred}{0}{default} ha silenciado a {lightred}{1}{default} permanentemente por {lightred}{2}{default}.",
"sa_admin_silence_message_time": "El administrador {lightred}{0}{default} ha silenciado a {lightred}{1}{default} por {lightred}{2}{default} durante {lightred}{3}{default} minutos.",
"sa_admin_silence_message_perm": "El administrador {lightred}{0}{default} ha silenciado a {lightred}{1}{default} permanentemente por {lightred}{2}{default}.",
"sa_admin_give_message": "El administrador {lightred}{0}{default} le ha dado a {lightred}{1}{default} un {lightred}{2}{default}.",
"sa_admin_strip_message": "El administrador {lightred}{0}{default} le ha quitado todas las armas al jugador {lightred}{1}{default}.",
"sa_admin_hp_message": "El administrador {lightred}{0}{default} ha cambiado la cantidad de salud de {lightred}{1}{default}{default}.",
@@ -26,6 +30,7 @@
"sa_admin_noclip_message": "El administrador {lightred}{0}{default} ha activado noclip para {lightred}{1}{default}.",
"sa_admin_freeze_message": "El administrador {lightred}{0}{default} ha congelado a {lightred}{1}{default}.",
"sa_admin_unfreeze_message": "El administrador {lightred}{0}{default} ha descongelado a {lightred}{1}{default}.",
"sa_admin_rename_message": "El administrador {lightred}{0}{default} ha cambiado el nombre de {lightred}{1}{default} a {lightred}{2}{default}."
"sa_admin_respawn_message": "El administrador {lightred}{0}{default} ha respawnado a {lightred}{1}{default}.",
"sa_admin_team_message": "El administrador {lightred}{0}{default} ha transferido a {lightred}{1}{default} a {lightred}{2}{default}.",
"sa_admin_vote_menu_title": "{lime}VOTACIÓN PARA {gold}{0}",
@@ -35,4 +40,4 @@
"sa_adminsay_prefix": "{RED}ADMIN: {lightred}{0}{default}",
"sa_adminchat_template_admin": "{LIME}(ADMIN) {lightred}{0}{default}: {lightred}{1}{default}",
"sa_adminchat_template_player": "{SILVER}(JUGADOR) {lightred}{0}{default}: {lightred}{1}{default}"
}
}

View File

@@ -8,6 +8,8 @@
"sa_player_gag_message_perm": "Zostałeś zakneblowany na zawsze za {lightred}{0}{default} przez {lightred}{1}{default}!",
"sa_player_mute_message_time": "Zostałeś uciszony za {lightred}{0}{default} na {lightred}{1}{default} minute przez {lightred}{2}{default}!",
"sa_player_mute_message_perm": "Zostałeś uciszony na zawsze za {lightred}{0}{default} przez {lightred}{1}{default}!",
"sa_player_silence_message_time": "Zostałeś wyciszony za {lightred}{0}{default} na {lightred}{1}{default} minut przez {lightred}{2}{default}!",
"sa_player_silence_message_perm": "Zostałeś wyciszony na zawsze za {lightred}{0}{default} przez {lightred}{1}{default}!",
"sa_admin_ban_message_time": "Admin {lightred}{0}{default} zbanował {lightred}{1}{default} za {lightred}{2}{default} na {lightred}{3}{default} minut!",
"sa_admin_ban_message_perm": "Admin {lightred}{0}{default} zbanował {lightred}{1}{default} na zawsze za {lightred}{2}{default}!",
"sa_admin_kick_message": "Admin {lightred}{0}{default} wyrzucił {lightred}{1}{default} za {lightred}{2}{default}!",
@@ -15,6 +17,8 @@
"sa_admin_gag_message_perm": "Admin {lightred}{0}{default} zakneblował {lightred}{1}{default} na zawsze za {lightred}{2}{default}!",
"sa_admin_mute_message_time": "Admin {lightred}{0}{default} uciszył {lightred}{1}{default} za {lightred}{2}{default} na {lightred}{3}{default} minut!",
"sa_admin_mute_message_perm": "Admin {lightred}{0}{default} uciszył {lightred}{1}{default} na zawsze za {lightred}{2}{default}!",
"sa_admin_silence_message_time": "Admin {lightred}{0}{default} wyciszył {lightred}{1}{default} za {lightred}{2}{default} na {lightred}{3}{default} minut!",
"sa_admin_silence_message_perm": "Admin {lightred}{0}{default} wyciszył {lightred}{1}{default} na zawsze za {lightred}{2}{default}!",
"sa_admin_give_message": "Admin {lightred}{0}{default} dał {lightred}{1}{default} przedmiot {lightred}{2}{default}!",
"sa_admin_strip_message": "Admin {lightred}{0}{default} zabrał wszystkie bronie {lightred}{1}{default}!",
"sa_admin_hp_message": "Admin {lightred}{0}{default} zmienił ilość hp dla {lightred}{1}{default}!",
@@ -26,6 +30,7 @@
"sa_admin_noclip_message": "Admin {lightred}{0}{default} ustawił latanie dla {lightred}{1}{default}!",
"sa_admin_freeze_message": "Admin {lightred}{0}{default} zamroził {lightred}{1}{default}!",
"sa_admin_unfreeze_message": "Admin {lightred}{0}{default} odmroził {lightred}{1}{default}!",
"sa_admin_rename_message": "Admin {lightred}{0}{default} zmienił nick gracza {lightred}{1}{default} na {lightred}{2}{default}!",
"sa_admin_respawn_message": "Admin {lightred}{0}{default} odrodził {lightred}{1}{default}!",
"sa_admin_team_message": "Admin {lightred}{0}{default} przerzucił {lightred}{1}{default} do {lightred}{2}{default}!",
"sa_admin_vote_menu_title": "{lime}GŁOSOWANIE NA {gold}{0}",