mirror of
https://github.com/daffyyyy/CS2-SimpleAdmin.git
synced 2026-03-03 14:32:26 +00:00
Compare commits
7 Commits
33175ad316
...
152d47880d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
152d47880d | ||
|
|
f654d6b085 | ||
|
|
676a18d9b4 | ||
|
|
d75a092047 | ||
|
|
d4e12fadde | ||
|
|
bedcfd0358 | ||
|
|
0c747e3e18 |
@@ -40,10 +40,10 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
||||
|
||||
var playerManager = new PlayerManager();
|
||||
|
||||
Parallel.ForEach(Helper.GetValidPlayers(), player =>
|
||||
foreach (var player in Helper.GetValidPlayers())
|
||||
{
|
||||
playerManager.LoadPlayerData(player);
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
_cBasePlayerControllerSetPawnFunc = new MemoryFunctionVoid<CBasePlayerController, CCSPlayerPawn, bool, bool>(GameData.GetSignature("CBasePlayerController_SetPawn"));
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
<None Update="Database\Migrations\012_AddUpdatedAtColumnToSaBansTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Database\Migrations\013_AddNameColumnToSaPlayerIpsTable.sql">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -34,6 +34,7 @@ public static class RegisterCommands
|
||||
new CommandMapping("css_addgroup", CS2_SimpleAdmin.Instance.OnAddGroup),
|
||||
new CommandMapping("css_delgroup", CS2_SimpleAdmin.Instance.OnDelGroupCommand),
|
||||
new CommandMapping("css_reloadadmins", CS2_SimpleAdmin.Instance.OnRelAdminCommand),
|
||||
new CommandMapping("css_reloadbans", CS2_SimpleAdmin.Instance.OnRelBans),
|
||||
new CommandMapping("css_hide", CS2_SimpleAdmin.Instance.OnHideCommand),
|
||||
new CommandMapping("css_hidecomms", CS2_SimpleAdmin.Instance.OnHideCommsCommand),
|
||||
new CommandMapping("css_who", CS2_SimpleAdmin.Instance.OnWhoCommand),
|
||||
@@ -122,6 +123,7 @@ public static class RegisterCommands
|
||||
{ "css_addgroup", new Command { Aliases = ["css_addgroup"] } },
|
||||
{ "css_delgroup", new Command { Aliases = ["css_delgroup"] } },
|
||||
{ "css_reloadadmins", new Command { Aliases = ["css_reloadadmins"] } },
|
||||
{ "css_reloadbans", new Command { Aliases = ["css_reloadbans"] } },
|
||||
{ "css_hide", new Command { Aliases = ["css_hide", "css_stealth"] } },
|
||||
{ "css_hidecomms", new Command { Aliases = ["css_hidecomms"] } },
|
||||
{ "css_who", new Command { Aliases = ["css_who"] } },
|
||||
|
||||
@@ -386,6 +386,16 @@ public partial class CS2_SimpleAdmin
|
||||
|
||||
command.ReplyToCommand("Reloaded sql admins and groups");
|
||||
}
|
||||
|
||||
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
|
||||
[RequiresPermissions("@css/root")]
|
||||
public void OnRelBans(CCSPlayerController? caller, CommandInfo command)
|
||||
{
|
||||
if (Database == null) return;
|
||||
|
||||
_ = Instance.CacheManager.ForceReInitializeCacheAsync();
|
||||
command.ReplyToCommand("Reloaded bans");
|
||||
}
|
||||
|
||||
public void ReloadAdmins(CCSPlayerController? caller)
|
||||
{
|
||||
@@ -509,6 +519,10 @@ public partial class CS2_SimpleAdmin
|
||||
printMethod($"• Total Mutes: \"{playerInfo.TotalMutes}\"");
|
||||
printMethod($"• Total Silences: \"{playerInfo.TotalSilences}\"");
|
||||
printMethod($"• Total Warns: \"{playerInfo.TotalWarns}\"");
|
||||
|
||||
var chunkedAccounts = playerInfo.AccountsAssociated.ChunkBy(3).ToList();
|
||||
foreach (var chunk in chunkedAccounts)
|
||||
printMethod($"• Associated Accounts: \"{string.Join(", ", chunk.Select(a => $"{a.PlayerName} ({a.SteamId})"))}\"");
|
||||
}
|
||||
|
||||
printMethod($"--------- END INFO ABOUT \"{player.PlayerName}\" ---------");
|
||||
|
||||
@@ -89,6 +89,17 @@ public class Discord
|
||||
new DiscordPenaltySetting { Name = "Footer", Value = "" },
|
||||
new DiscordPenaltySetting { Name = "Time", Value = "{relative}" },
|
||||
];
|
||||
|
||||
[JsonPropertyName("DiscordAssociatedAccountsSettings")]
|
||||
public DiscordPenaltySetting[] DiscordAssociatedAccountsSettings { get; set; } =
|
||||
[
|
||||
new DiscordPenaltySetting { Name = "Color", Value = "" },
|
||||
new DiscordPenaltySetting { Name = "Webhook", Value = "" },
|
||||
new DiscordPenaltySetting { Name = "ThumbnailUrl", Value = "" },
|
||||
new DiscordPenaltySetting { Name = "ImageUrl", Value = "" },
|
||||
new DiscordPenaltySetting { Name = "Footer", Value = "" },
|
||||
new DiscordPenaltySetting { Name = "Time", Value = "{relative}" },
|
||||
];
|
||||
}
|
||||
|
||||
public class CustomServerCommandData
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `sa_players_ips` ADD `name` VARCHAR(64) NULL DEFAULT NULL AFTER `steamid`;
|
||||
12
CS2-SimpleAdmin/Extensions/EnumerableExtensions.cs
Normal file
12
CS2-SimpleAdmin/Extensions/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace CS2_SimpleAdmin;
|
||||
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static IEnumerable<IEnumerable<T>> ChunkBy<T>(this IEnumerable<T> source, int chunkSize)
|
||||
{
|
||||
return source
|
||||
.Select((x, i) => new { Index = i, Value = x })
|
||||
.GroupBy(x => x.Index / chunkSize)
|
||||
.Select(x => x.Select(v => v.Value));
|
||||
}
|
||||
}
|
||||
@@ -669,12 +669,11 @@ internal static class Helper
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log or handle the exception
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static string GenerateMessageDiscord(string message)
|
||||
{
|
||||
var hostname = ConVar.Find("hostname")?.StringValue ?? CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
using System.Collections.Concurrent;
|
||||
using CS2_SimpleAdmin.Models;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZLinq;
|
||||
|
||||
namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
internal class CacheManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, BanRecord> _banCache = new();
|
||||
private readonly ConcurrentDictionary<ulong, (HashSet<string> ips, DateTime used_at)> _playerIpsCache = new();
|
||||
private readonly HashSet<string> _cachedIgnoredIps = [..CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps];
|
||||
private readonly ConcurrentDictionary<ulong, (HashSet<string> ips, DateTime usedAt, string playerName)> _playerIpsCache = new();
|
||||
private HashSet<string> _cachedIgnoredIps;
|
||||
|
||||
private DateTime _lastUpdateTime = DateTime.MinValue;
|
||||
private bool _isInitialized;
|
||||
|
||||
|
||||
public async Task InitializeCacheAsync()
|
||||
{
|
||||
if (CS2_SimpleAdmin.Database == null) return;
|
||||
if (!CS2_SimpleAdmin.ServerLoaded)
|
||||
return;
|
||||
if (!CS2_SimpleAdmin.ServerLoaded) return;
|
||||
if (_isInitialized) return;
|
||||
|
||||
|
||||
_cachedIgnoredIps = [..CS2_SimpleAdmin.Instance.Config.OtherSettings.IgnoredIps];
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.Database.GetConnectionAsync();
|
||||
@@ -41,21 +42,25 @@ internal class CacheManager
|
||||
status AS Status,
|
||||
updated_at AS UpdatedAt
|
||||
FROM sa_bans
|
||||
""");
|
||||
var ipHistory = await connection.QueryAsync<(ulong steamid, string address, DateTime used_at)>(
|
||||
"SELECT steamid, address, used_at FROM sa_players_ips");
|
||||
""");
|
||||
var ipHistory =
|
||||
await connection.QueryAsync<(ulong steamid, string? name, string address, DateTime used_at)>(
|
||||
"SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY used_at DESC");
|
||||
|
||||
|
||||
|
||||
foreach (var ban in bans)
|
||||
{
|
||||
_banCache.TryAdd(ban.Id, ban);
|
||||
}
|
||||
|
||||
|
||||
foreach (var group in ipHistory.GroupBy(x => x.steamid))
|
||||
{
|
||||
var ips = new HashSet<string>(group.Select(x => x.address));
|
||||
var lastUsed = group.Max(x => x.used_at);
|
||||
_playerIpsCache[group.Key] = (ips, lastUsed);
|
||||
var playerName = group.FirstOrDefault(x => !string.IsNullOrEmpty(x.name)).name
|
||||
?? CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
|
||||
_playerIpsCache[group.Key] = (ips, lastUsed, playerName);
|
||||
}
|
||||
|
||||
_lastUpdateTime = DateTime.Now;
|
||||
@@ -66,6 +71,18 @@ internal class CacheManager
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ForceReInitializeCacheAsync()
|
||||
{
|
||||
_isInitialized = false;
|
||||
|
||||
_banCache.Clear();
|
||||
_playerIpsCache.Clear();
|
||||
_cachedIgnoredIps.Clear();
|
||||
_lastUpdateTime = DateTime.MinValue;
|
||||
|
||||
await InitializeCacheAsync();
|
||||
}
|
||||
|
||||
public async Task RefreshCacheAsync()
|
||||
{
|
||||
@@ -78,10 +95,9 @@ internal class CacheManager
|
||||
var updatedBans = (await connection.QueryAsync<BanRecord>(
|
||||
"SELECT * FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC",
|
||||
new { lastUpdate = _lastUpdateTime }
|
||||
)).ToList();
|
||||
|
||||
var ipHistory = await connection.QueryAsync<(ulong steamid, string address, DateTime used_at)>(
|
||||
"SELECT steamid, address, used_at FROM sa_players_ips");
|
||||
)).ToList().AsValueEnumerable();
|
||||
var ipHistory = (await connection.QueryAsync<(ulong steamid, string? name, string address, DateTime used_at)>(
|
||||
"SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY used_at DESC LIMIT 500")).ToList();
|
||||
|
||||
// foreach (var group in ipHistory.GroupBy(x => x.steamid))
|
||||
// {
|
||||
@@ -89,25 +105,43 @@ internal class CacheManager
|
||||
// var lastUsed = group.Max(x => x.used_at);
|
||||
// _playerIpsCache[group.Key] = (ips, lastUsed);
|
||||
// }
|
||||
|
||||
var groupedData = ipHistory.GroupBy(x => x.steamid).ToList();
|
||||
Parallel.ForEach(groupedData, group =>
|
||||
|
||||
var groupedData = ipHistory.AsValueEnumerable()
|
||||
.GroupBy(x => x.steamid)
|
||||
.ToList();
|
||||
|
||||
groupedData.ForEach(group =>
|
||||
{
|
||||
var ips = new HashSet<string>(group.Select(x => x.address));
|
||||
var lastUsed = group.Max(x => x.used_at);
|
||||
var ips = new HashSet<string>(
|
||||
group.Select(x => x.address),
|
||||
StringComparer.OrdinalIgnoreCase
|
||||
);
|
||||
|
||||
var lastUsed = group.Max(x => x.used_at);
|
||||
var playerName = group
|
||||
.OrderByDescending(x => x.used_at) // Prefer newer records
|
||||
.Select(x => x.name)
|
||||
.FirstOrDefault(name => !string.IsNullOrEmpty(name))
|
||||
?? CS2_SimpleAdmin._localizer?["sa_unknown"]
|
||||
?? "Unknown";
|
||||
|
||||
_playerIpsCache.AddOrUpdate(
|
||||
group.Key,
|
||||
_ => (ips, lastUsed),
|
||||
// Add new entry
|
||||
_ => (ips, lastUsed, playerName),
|
||||
// Update existing
|
||||
(_, existing) =>
|
||||
{
|
||||
existing.ips.UnionWith(ips);
|
||||
return (existing.ips,
|
||||
lastUsed > existing.used_at ? lastUsed : existing.used_at);
|
||||
existing.ips.UnionWith(ips);
|
||||
return (
|
||||
existing.ips,
|
||||
lastUsed > existing.usedAt ? lastUsed : existing.usedAt,
|
||||
string.IsNullOrEmpty(existing.playerName) ? playerName : existing.playerName
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
if (updatedBans.Count == 0)
|
||||
if (updatedBans.Count() == 0)
|
||||
return;
|
||||
|
||||
foreach (var ban in updatedBans)
|
||||
@@ -126,7 +160,13 @@ internal class CacheManager
|
||||
public List<BanRecord> GetAllBans() => _banCache.Values.ToList();
|
||||
public List<BanRecord> GetActiveBans() => _banCache.Values.Where(b => b.Status == "ACTIVE").ToList();
|
||||
public List<BanRecord> GetPlayerBansBySteamId(string steamId) => _banCache.Values.Where(b => b.PlayerSteamId == steamId).ToList();
|
||||
|
||||
public List<(ulong SteamId, string PlayerName)> GetAccountsByIp(string ipAddress)
|
||||
{
|
||||
return _playerIpsCache.AsValueEnumerable()
|
||||
.Where(kvp => kvp.Value.ips.Contains(ipAddress))
|
||||
.Select(kvp => (kvp.Key, kvp.Value.playerName))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private bool IsIpBanned(string ipAddress)
|
||||
{
|
||||
@@ -168,5 +208,4 @@ internal class CacheManager
|
||||
return _playerIpsCache.TryGetValue(steamId, out var ipList)
|
||||
&& ipList.ips.Contains(ipAddress);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,33 +46,32 @@ public class PlayerManager
|
||||
|
||||
var steamId64 = CS2_SimpleAdmin.PlayersInfo[userId].SteamId.SteamId64;
|
||||
var steamId = steamId64.ToString();
|
||||
|
||||
var isBanned = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch
|
||||
{
|
||||
0 => // SteamID only check
|
||||
CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(steamId, null),
|
||||
_ => CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp // SteamID and IP address check
|
||||
? CS2_SimpleAdmin.Instance.CacheManager.IsPlayerOrAnyIpBanned(steamId64) // All associated IP addresses
|
||||
: CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(steamId, ipAddress) // Only current IP address
|
||||
};
|
||||
|
||||
if (isBanned)
|
||||
{
|
||||
// Kick the player if banned
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
if (!player.UserId.HasValue) return;
|
||||
Helper.KickPlayer(userId, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (CS2_SimpleAdmin.Database == null) return;
|
||||
|
||||
// Perform asynchronous database operations within a single method
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var isBanned = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch
|
||||
{
|
||||
0 => CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(steamId, null),
|
||||
_ => CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp
|
||||
? CS2_SimpleAdmin.Instance.CacheManager.IsPlayerOrAnyIpBanned(steamId64)
|
||||
: CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(steamId, ipAddress)
|
||||
};
|
||||
|
||||
if (isBanned)
|
||||
{
|
||||
// Kick the player if banned
|
||||
await Server.NextFrameAsync(() =>
|
||||
{
|
||||
if (!player.UserId.HasValue) return;
|
||||
Helper.KickPlayer(userId, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_config.OtherSettings.CheckMultiAccountsByIp && ipAddress != null)
|
||||
{
|
||||
try
|
||||
@@ -137,6 +136,10 @@ public class PlayerManager
|
||||
CS2_SimpleAdmin._logger?.LogError(
|
||||
$"Unable to save ip address for {CS2_SimpleAdmin.PlayersInfo[userId].Name} ({ipAddress}) {ex.Message}");
|
||||
}
|
||||
|
||||
// Get all accounts associated to the player (ip address)
|
||||
CS2_SimpleAdmin.PlayersInfo[userId].AccountsAssociated =
|
||||
CS2_SimpleAdmin.Instance.CacheManager.GetAccountsByIp(ipAddress);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -204,6 +207,8 @@ public class PlayerManager
|
||||
|
||||
if (CS2_SimpleAdmin.Instance.Config.OtherSettings.NotifyPenaltiesToAdminOnConnect && fullConnect)
|
||||
{
|
||||
var associatedAcccountsChunks = CS2_SimpleAdmin.PlayersInfo[userId].AccountsAssociated.ChunkBy(5).ToList();
|
||||
|
||||
await Server.NextFrameAsync(() =>
|
||||
{
|
||||
foreach (var admin in Helper.GetValidPlayers()
|
||||
@@ -212,6 +217,7 @@ public class PlayerManager
|
||||
p.Connected == PlayerConnectedState.PlayerConnected && !CS2_SimpleAdmin.AdminDisabledJoinComms.Contains(p.SteamID)))
|
||||
{
|
||||
if (CS2_SimpleAdmin._localizer != null && admin != player)
|
||||
{
|
||||
admin.SendLocalizedMessage(CS2_SimpleAdmin._localizer, "sa_admin_penalty_info",
|
||||
player.PlayerName,
|
||||
CS2_SimpleAdmin.PlayersInfo[userId].TotalBans,
|
||||
@@ -220,6 +226,16 @@ public class PlayerManager
|
||||
CS2_SimpleAdmin.PlayersInfo[userId].TotalSilences,
|
||||
CS2_SimpleAdmin.PlayersInfo[userId].TotalWarns
|
||||
);
|
||||
|
||||
foreach (var chunk in associatedAcccountsChunks)
|
||||
{
|
||||
admin.SendLocalizedMessage(CS2_SimpleAdmin._localizer, "sa_admin_associated_accounts",
|
||||
player.PlayerName,
|
||||
string.Join(", ",
|
||||
chunk.Select(a => $"{a.PlayerName} ({a.SteamId})"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nعقوبات اللاعبين لـ {lightred}{0}{default},\nعدد الحظر: {lightred}{1}{default}, عدد الصمت: {lightred}{2}{default}, عدد الكتم: {lightred}{3}{default}, عدد السكوت: {lightred}{4}{default}, عدد التحذيرات: {lightred}{5}{default}\nالعقوبات النشطة:\n{6}\nالتحذيرات النشطة:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}عقوبات اللاعبين لـ {lightred}{0}{grey}, حظر: {lightred}{1}{grey}, صمت: {lightred}{2}{grey}, كتم: {lightred}{3}{grey}, سكوت: {lightred}{4}{grey}, تحذيرات: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}الحسابات المرتبطة باللاعب {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "تم حظرك لمدة {lightred}{0}{default} لمدة {lightred}{1}{default} دقيقة من قبل {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "تم حظرك بشكل دائم لمدة {lightred}{0}{default} من قبل {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "تم طردك لمدة {lightred}{0}{default} من قبل {lightred}{1}{default}!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nSpielerstrafe für {lightred}{0}{default},\nAnzahl der Sperren: {lightred}{1}{default}, Anzahl der Mundtot: {lightred}{2}{default}, Anzahl der Stummschaltungen: {lightred}{3}{default}, Anzahl der Stille: {lightred}{4}{default}, Anzahl der Warnungen: {lightred}{5}{default}\nAktive Strafen:\n{6}\nAktive Warnungen:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Spielerstrafe für {lightred}{0}{grey}, Sperren: {lightred}{1}{grey}, Mundtot: {lightred}{2}{grey}, Stummschaltungen: {lightred}{3}{grey}, Stille: {lightred}{4}{grey}, Warnungen: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Verknüpfte Konten des Spielers {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Du wurdest wegen {lightred}{0}{default} für {lightred}{1}{default} Minuten von {lightred}{2}{default} gebannt!",
|
||||
"sa_player_ban_message_perm": "Du wurdest wegen {lightred}{0}{default} von {lightred}{1}{default} permanent gebannt!",
|
||||
"sa_player_kick_message": "Du wurdest wegen {lightred}{0}{default} von {lightred}{1}{default} gekickt!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPlayer penalties for {lightred}{0}{default},\nNumber of bans: {lightred}{1}{default}, Number of gags: {lightred}{2}{default}, Number of mutes: {lightred}{3}{default}, Number of silences: {lightred}{4}{default}, Number of warnings: {lightred}{5}{default}\nActive penalties:\n{6}\nActive warnings:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Player penalties for {lightred}{0}{grey}, Bans: {lightred}{1}{grey}, Gags: {lightred}{2}{grey}, Mutes: {lightred}{3}{grey}, Silences: {lightred}{4}{grey}, Warns: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Associated accounts of player {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "You have been banned for {lightred}{0}{default} for {lightred}{1}{default} minutes by {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "You have been banned permanently for {lightred}{0}{default} by {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "You have been kicked for {lightred}{0}{default} by {lightred}{1}{default}!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPenalizaciones del jugador para {lightred}{0}{default},\nNúmero de prohibiciones: {lightred}{1}{default}, Número de boqueos: {lightred}{2}{default}, Número de silenciamientos: {lightred}{3}{default}, Número de silencios: {lightred}{4}{default}, Número de advertencias: {lightred}{5}{default}\nPenalizaciones activas:\n{6}\nAdvertencias activas:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Penalizaciones del jugador para {lightred}{0}{grey}, Prohibiciones: {lightred}{1}{grey}, Boqueos: {lightred}{2}{grey}, Silenciamientos: {lightred}{3}{grey}, Silencios: {lightred}{4}{grey}, Advertencias: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Cuentas asociadas del jugador {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Has sido baneado por {lightred}{0}{default} durante {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Has sido baneado permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Has sido expulsado por {lightred}{0}{default} durante {lightred}{1}{default}!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nتنبیهات بازیکن برای {lightred}{0}{default},\nتعداد مسدودیتها: {lightred}{1}{default}, تعداد سکوتها: {lightred}{2}{default}, تعداد بیصدا کردنها: {lightred}{3}{default}, تعداد سکوتها: {lightred}{4}{default}, تعداد هشدارها: {lightred}{5}{default}\nتنبیهات فعال:\n{6}\nهشدارهای فعال:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}تنبیهات بازیکن برای {lightred}{0}{grey}, مسدودیتها: {lightred}{1}{grey}, سکوتها: {lightred}{2}{grey}, بیصدا کردنها: {lightred}{3}{grey}, سکوتها: {lightred}{4}{grey}, هشدارها: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}حسابهای مرتبط با بازیکن {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "شما توسط {lightred}{2}{default} برای {lightred}{1}{default} دقیقه به دلیل {lightred}{0}{default} مسدود شدهاید!",
|
||||
"sa_player_ban_message_perm": "شما توسط {lightred}{1}{default} به دلیل {lightred}{0}{default} برای همیشه مسدود شدهاید!",
|
||||
"sa_player_kick_message": "شما توسط {lightred}{1}{default} به دلیل {lightred}{0}{default} اخراج شدهاید!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPénalités du joueur pour {lightred}{0}{default},\nNombre de bannissements: {lightred}{1}{default}, Nombre de gag: {lightred}{2}{default}, Nombre de mutes: {lightred}{3}{default}, Nombre de silences: {lightred}{4}{default}, Nombre d’avertissements: {lightred}{5}{default}\nPénalités actives:\n{6}\nAvertissements actifs:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Pénalités du joueur pour {lightred}{0}{grey}, Bannissements: {lightred}{1}{grey}, Gags: {lightred}{2}{grey}, Mutes: {lightred}{3}{grey}, Silences: {lightred}{4}{grey}, Avertissements: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Comptes associés du joueur {lightred}{0}{grey} : {1}",
|
||||
"sa_player_ban_message_time": "Vous avez été banni pour {lightred}{0}{default} pendant {lightred}{1}{default} minutes par {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Vous avez été banni définitivement pour {lightred}{0}{default} par {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Vous avez été expulsé pour {lightred}{0}{default} par {lightred}{1}{default}!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nSpēlētāja sods priekš {lightred}{0}{default},\nAizliegumu skaits: {lightred}{1}{default}, Klusumu skaits: {lightred}{2}{default}, Izslēgšanas skaits: {lightred}{3}{default}, Klusēšanas skaits: {lightred}{4}{default}, Brīdinājumu skaits: {lightred}{5}{default}\nAktīvie sodi:\n{6}\nAktīvie brīdinājumi:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Spēlētāja sods priekš {lightred}{0}{grey}, Aizliegumi: {lightred}{1}{grey}, Klusumi: {lightred}{2}{grey}, Izslēgšana: {lightred}{3}{grey}, Klusēšana: {lightred}{4}{grey}, Brīdinājumi: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Spēlētāja {lightred}{0}{grey} saistītie konti: {1}",
|
||||
"sa_player_ban_message_time": "Tu esi nobanots uz {lightred}{0}{default} uz {lightred}{1}{default} minūtēm, iemesls: {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Tevis bans ir uz mūžu, iemesls: {lightred}{0}{default}, Admins: {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Tu esi izmests, iemesls: {lightred}{0}{default}, Admins: {lightred}{1}{default}!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nBlokady gracza {lightred}{0}{default},\nIlość banów: {lightred}{1}{default}, Ilość zakneblowań: {lightred}{2}{default}, Ilość wyciszeń: {lightred}{3}{default}, Ilość uciszeń: {lightred}{4}{default}Ilość ostrzeżeń: {lightred}{5}{default}\nAktywne blokady:\n{6}\nAktywne ostrzeżenia:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Blokady gracza {lightred}{0}{grey} - bany: {lightred}{1}{grey}, zakneblowania: {lightred}{2}{grey}, wyciszenia: {lightred}{3}{grey}, uciszenia: {lightred}{4}{grey}, ostrzeżenia: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Powiązane konta gracza {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Zostałeś zbanowany za {lightred}{0}{default} na {lightred}{1}{default} minut przez {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Zostałeś zbanowany na zawsze za {lightred}{0}{default} przez {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Zostałeś wyrzucony za {lightred}{0}{default} przez {lightred}{1}{default}!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPenalidades do jogador para {lightred}{0}{default},\nNúmero de banimentos: {lightred}{1}{default}, Número de gags: {lightred}{2}{default}, Número de mutes: {lightred}{3}{default}, Número de silêncios: {lightred}{4}{default}, Número de avisos: {lightred}{5}{default}\nPenalidades ativas:\n{6}\nAvisos ativos:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Penalidades do jogador para {lightred}{0}{grey}, Banimentos: {lightred}{1}{grey}, Gags: {lightred}{2}{grey}, Mutes: {lightred}{3}{grey}, Silêncios: {lightred}{4}{grey}, Avisos: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Contas associadas do jogador {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Você foi banido por {lightred}{0}{default} por {lightred}{1}{default} minutos por {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Você foi banido permanentemente por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Você foi expulso por {lightred}{0}{default} por {lightred}{1}{default}!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nPenalidades do jogador para {lightred}{0}{default},\nNúmero de banimentos: {lightred}{1}{default}, Número de gags: {lightred}{2}{default}, Número de mutes: {lightred}{3}{default}, Número de silêncios: {lightred}{4}{default}, Número de avisos: {lightred}{5}{default}\nPenalidades ativas:\n{6}\nAvisos ativos:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Penalidades do jogador para {lightred}{0}{grey}, Banimentos: {lightred}{1}{grey}, Gags: {lightred}{2}{grey}, Mutes: {lightred}{3}{grey}, Silêncios: {lightred}{4}{grey}, Avisos: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Contas associadas do jogador {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Foste banido pelo administrador {lightred}{0}{default} durante {lightred}{1}{default} minutos. Motivo: {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Foste banido permanentemente pelo administrador {lightred}{0}{default}. Motivo: {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Foste expulso pelo administrador {lightred}{0}{default}. Motivo: {lightred}{1}{default}!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nШтрафы игрока для {lightred}{0}{default},\nКоличество банов: {lightred}{1}{default}, Количество гэгов: {lightred}{2}{default}, Количество мутов: {lightred}{3}{default}, Количество тишин: {lightred}{4}{default}, Количество предупреждений: {lightred}{5}{default}\nАктивные штрафы:\n{6}\nАктивные предупреждения:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Штрафы игрока для {lightred}{0}{grey}, Баны: {lightred}{1}{grey}, Гэги: {lightred}{2}{grey}, Муты: {lightred}{3}{grey}, Тишины: {lightred}{4}{grey}, Предупреждения: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}Связанные аккаунты игрока {lightred}{0}{grey}: {1}",
|
||||
"sa_player_ban_message_time": "Вы были забанены по причине {lightred}{0}{default} на {lightred}{1}{default} минут(ы) администратором {lightred}{2}{default}!",
|
||||
"sa_player_ban_message_perm": "Вас забанили навсегда по причине {lightred}{0}{default} администратором {lightred}{1}{default}!",
|
||||
"sa_player_kick_message": "Вы были выгнаны {lightred}{0}{default} администратором {lightred}{1}{default}!",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\nOyuncunun cezaları {lightred}{0}{default} için,\nBan sayısı: {lightred}{1}{default}, Gag sayısı: {lightred}{2}{default}, Mute sayısı: {lightred}{3}{default}, Sessizlik sayısı: {lightred}{4}{default}, Uyarı sayısı: {lightred}{5}{default}\nAktif cezalar:\n{6}\nAktif uyarılar:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}Oyuncunun cezaları {lightred}{0}{grey}, Banlar: {lightred}{1}{grey}, Gaglar: {lightred}{2}{grey}, Mute'lar: {lightred}{3}{grey}, Sessizlikler: {lightred}{4}{grey}, Uyarılar: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}{lightred}{0}{grey} oyuncusunun bağlı hesapları: {1}",
|
||||
"sa_player_ban_message_time": "Senaryo nedeniyle {lightred}{0}{default} dakika boyunca {lightred}{1}{default} tarafından yasaklandınız!",
|
||||
"sa_player_ban_message_perm": "Senaryo nedeniyle kalıcı olarak {lightred}{0}{default} tarafından yasaklandınız!",
|
||||
"sa_player_kick_message": "Senaryo nedeniyle {lightred}{0}{default} tarafından atıldınız!",
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
|
||||
"sa_player_penalty_info": "===========================\n玩家 {lightred}{0}{default} 的处罚信息,\n禁止次数: {lightred}{1}{default}, 禁言次数: {lightred}{2}{default}, 静音次数: {lightred}{3}{default}, 沉默次数: {lightred}{4}{default}, 警告次数: {lightred}{5}{default}\n活跃的处罚:\n{6}\n活跃的警告:\n{7}\n===========================",
|
||||
"sa_admin_penalty_info": "{grey}玩家 {lightred}{0}{grey} 的处罚信息, 禁止: {lightred}{1}{grey}, 禁言: {lightred}{2}{grey}, 静音: {lightred}{3}{grey}, 沉默: {lightred}{4}{grey}, 警告: {lightred}{5}",
|
||||
"sa_admin_associated_accounts": "{grey}玩家 {lightred}{0}{grey} 的关联账户:{1}",
|
||||
"sa_player_ban_message_time": "您已被 {lightred}{0}{default} 因 {lightred}{2}{default} 禁止 {lightred}{1}{default} 分钟!",
|
||||
"sa_player_ban_message_perm": "您已被 {lightred}{0}{default} 因 {lightred}{1}{default} 永久禁止!",
|
||||
"sa_player_kick_message": "您已被 {lightred}{0}{default} 因 {lightred}{1}{default} 踢出!",
|
||||
|
||||
@@ -26,6 +26,7 @@ public class PlayerInfo(
|
||||
public int TotalSilences { get; set; } = totalSilences;
|
||||
public int TotalWarns { get; set; } = totalWarns;
|
||||
public bool WaitingForKick { get; set; } = false;
|
||||
public List<(ulong SteamId, string PlayerName)> AccountsAssociated { get; set; } = [];
|
||||
public DiePosition? DiePosition { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -10,14 +10,17 @@ using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Capabilities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using AntiDLL.API;
|
||||
using System.Text.Json;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
public class PluginConfig : IBasePluginConfig
|
||||
{
|
||||
[JsonPropertyName("ConfigVersion")] public int Version { get; set; } = 1;
|
||||
[JsonPropertyName("ConfigVersion")] public int Version { get; set; } = 2;
|
||||
[JsonPropertyName("Reason")] public string Reason { get; set; } = "Invalid event detected!";
|
||||
[JsonPropertyName("Duration")] public int Duration { get; set; } = 0;
|
||||
[JsonPropertyName("CommandToExecute")] public string CommandToExecute { get; set; } = "css_addban {steamid64} {duration} {reason}";
|
||||
[JsonPropertyName("BanType")] public string BanType { get; set; } = "auto";
|
||||
[JsonPropertyName("WebhookUrl")] public string WebhookUrl { get; set; } = "";
|
||||
}
|
||||
|
||||
public sealed class AntiDLL_CS2_SimpleAdmin : BasePlugin, IPluginConfig<PluginConfig>
|
||||
@@ -29,7 +32,7 @@ public sealed class AntiDLL_CS2_SimpleAdmin : BasePlugin, IPluginConfig<PluginCo
|
||||
private static PluginCapability<IAntiDLL> AntiDll { get; } = new("AntiDLL");
|
||||
private static PluginCapability<ICS2_SimpleAdminApi> SimpleAdminApi { get; } = new("simpleadmin:api");
|
||||
private static ICS2_SimpleAdminApi? _simpleAdminApi;
|
||||
|
||||
|
||||
public override string ModuleName => "AntiDLL [CS2-SimpleAdmin Module]";
|
||||
public override string ModuleDescription => "AntiDLL module for CS2-SimpleAdmin integration";
|
||||
public override string ModuleVersion => "1.0.1";
|
||||
@@ -39,12 +42,12 @@ public sealed class AntiDLL_CS2_SimpleAdmin : BasePlugin, IPluginConfig<PluginCo
|
||||
{
|
||||
RegisterListener<Listeners.OnClientDisconnect>(OnClientDisconnect);
|
||||
}
|
||||
|
||||
|
||||
public void OnConfigParsed(PluginConfig config)
|
||||
{
|
||||
Config = config;
|
||||
}
|
||||
|
||||
|
||||
public override void OnAllPluginsLoaded(bool hotReload)
|
||||
{
|
||||
try
|
||||
@@ -56,7 +59,7 @@ public sealed class AntiDLL_CS2_SimpleAdmin : BasePlugin, IPluginConfig<PluginCo
|
||||
Unload(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
antidll.OnDetection += OnDetection;
|
||||
}
|
||||
catch (Exception)
|
||||
@@ -94,9 +97,9 @@ public sealed class AntiDLL_CS2_SimpleAdmin : BasePlugin, IPluginConfig<PluginCo
|
||||
public HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo _)
|
||||
{
|
||||
var player = @event.Userid;
|
||||
if (player == null || !player.IsValid || player.IsBot || !_detections.Contains(player.Slot))
|
||||
if (player == null || !player.IsValid || player.IsBot || !_detections.Contains(player.Slot))
|
||||
return HookResult.Continue;
|
||||
|
||||
|
||||
if (!_bannedPlayers.Contains(player.Slot) && player.Connected == PlayerConnectedState.PlayerConnected && player.TeamNum != 0)
|
||||
PunishPlayer(player);
|
||||
|
||||
@@ -108,22 +111,23 @@ public sealed class AntiDLL_CS2_SimpleAdmin : BasePlugin, IPluginConfig<PluginCo
|
||||
if (player == null || !player.IsValid || player.IsBot) return;
|
||||
if (!_detections.Add(player.Slot))
|
||||
return;
|
||||
|
||||
|
||||
// if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||
// {
|
||||
// _detections.Add(player.Slot);
|
||||
// // AddTimer(3.0f, () => OnDetection(player, eventName));
|
||||
// return;
|
||||
// }
|
||||
|
||||
|
||||
Logger.LogInformation("Detected \"{eventName}\" for \"{player}({steamid})\"", eventName, player.PlayerName, player.SteamID.ToString());
|
||||
_ = SendWebhook(eventName, player.PlayerName, player.SteamID.ToString());
|
||||
}
|
||||
|
||||
private void PunishPlayer(CCSPlayerController player)
|
||||
{
|
||||
if (!_bannedPlayers.Add(player.Slot))
|
||||
return;
|
||||
|
||||
|
||||
if (_banType == 1 && _simpleAdminApi != null)
|
||||
{
|
||||
_simpleAdminApi.IssuePenalty(new SteamID(player.SteamID), null, PenaltyType.Ban, Config.Reason, Config.Duration);
|
||||
@@ -143,11 +147,36 @@ public sealed class AntiDLL_CS2_SimpleAdmin : BasePlugin, IPluginConfig<PluginCo
|
||||
public override void Unload(bool hotReload)
|
||||
{
|
||||
RemoveListener<Listeners.OnClientDisconnect>(OnClientDisconnect);
|
||||
|
||||
|
||||
var antidll = AntiDll.Get();
|
||||
if (antidll != null)
|
||||
{
|
||||
antidll.OnDetection -= OnDetection;
|
||||
}
|
||||
}
|
||||
|
||||
// Discord Webhook
|
||||
private async Task SendWebhook(string eventName, string playerName, string steamid)
|
||||
{
|
||||
var webhookUrl = Config.WebhookUrl;
|
||||
if (string.IsNullOrEmpty(webhookUrl))
|
||||
return;
|
||||
|
||||
var payload = new
|
||||
{
|
||||
content = $"Detected \"{eventName}\" for \"{playerName}({steamid})\""
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(payload);
|
||||
|
||||
using var client = new HttpClient();
|
||||
using var content = new StringContent(json, System.Text.Encoding.UTF8, new MediaTypeHeaderValue("application/json"));
|
||||
|
||||
var response = await client.PostAsync(webhookUrl, content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Logger.LogError("Failed to forward event to webhook: {response}", response.ReasonPhrase);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user