mirror of
https://github.com/daffyyyy/CS2-SimpleAdmin.git
synced 2026-04-25 10:16:26 +00:00
Version bump and multi-account/IP ban fixes
Bump version to 1.7.9a and update package references (CounterStrikeSharp.API, Dapper, System.Linq.Async, ZLinq). Replace usages of PlayerConnectedState.PlayerConnected with PlayerConnectedState.Connected across commands, events, and helpers. Add DB method GetExpireOldPlayerIpsQuery (MySQL/SQLite) and call it to purge old player IP records during ban expiration. Improve CacheManager multi-account/ip ban detection (direct IP bans, cross-account IP checks) and adjust ban logic to respect expiration window. In PlayerManager: reduce semaphore, save player IP to DB before performing ban checks (add SavePlayerIpAddress), and always perform ban checks on connect. Simplify config upgrade logic in Helper.UpdateConfig to update the JSON file Version via JsonNode. Misc: adjust ban sound volume, minor exception catch cleanup, and add module folder to .gitignore.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ Modules/CS2-SimpleAdmin_ExampleModule/CS2-SimpleAdmin_ExampleModule.sln.DotSetti
|
||||
CS2-SimpleAdmin_BanSoundModule — kopia
|
||||
*.user
|
||||
CLAUDE.md
|
||||
/Modules/CS2-SimpleAdmin_BanSoundModule
|
||||
|
||||
@@ -22,7 +22,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
||||
public override string ModuleName => "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)");
|
||||
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
|
||||
public override string ModuleAuthor => "daffyy";
|
||||
public override string ModuleVersion => "1.7.8-beta-10b";
|
||||
public override string ModuleVersion => "1.7.9a";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
@@ -46,7 +46,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdmin
|
||||
CachedPlayers.Clear();
|
||||
BotPlayers.Clear();
|
||||
|
||||
foreach (var player in Utilities.GetPlayers().Where(p => p.IsValid && p.Connected == PlayerConnectedState.PlayerConnected && !p.IsHLTV).ToArray())
|
||||
foreach (var player in Utilities.GetPlayers().Where(p => p.IsValid && p is { Connected: PlayerConnectedState.Connected, IsHLTV: false }).ToArray())
|
||||
{
|
||||
if (!player.IsBot)
|
||||
PlayerManager.LoadPlayerData(player, true);
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.361">
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.367">
|
||||
<PrivateAssets>none</PrivateAssets>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||
<PackageReference Include="Dapper" Version="2.1.72" />
|
||||
<PackageReference Include="MySqlConnector" Version="2.5.0" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.119" />
|
||||
<PackageReference Include="System.Linq.Async" Version="7.0.0" />
|
||||
<PackageReference Include="ZLinq" Version="1.5.4" />
|
||||
<PackageReference Include="System.Linq.Async" Version="7.0.1" />
|
||||
<PackageReference Include="ZLinq" Version="1.5.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -27,7 +27,7 @@ public partial class CS2_SimpleAdmin
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, IsHLTV: false }).ToList();
|
||||
var playersToTarget = targets.Players.Where(player => player is { IsValid: true, Connected: PlayerConnectedState.Connected, IsHLTV: false }).ToList();
|
||||
|
||||
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
|
||||
{
|
||||
@@ -373,7 +373,7 @@ public partial class CS2_SimpleAdmin
|
||||
|
||||
var targets = GetTarget(command);
|
||||
if (targets == null) return;
|
||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected && !player.IsHLTV).ToList();
|
||||
var playersToTarget = targets.Players.Where(player => player.IsValid && player.Connected == PlayerConnectedState.Connected && !player.IsHLTV).ToList();
|
||||
|
||||
if (playersToTarget.Count > 1 && Config.OtherSettings.DisableDangerousCommands || playersToTarget.Count == 0)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ public partial class CS2_SimpleAdmin
|
||||
/// <param name="command">Optional command info for logging.</param>
|
||||
internal static void Slay(CCSPlayerController? caller, CCSPlayerController player, string? callerName = null, CommandInfo? command = null)
|
||||
{
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected) return;
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.Connected) return;
|
||||
if (!caller.CanTarget(player)) return;
|
||||
|
||||
// Set default caller name if not provided
|
||||
@@ -93,7 +93,7 @@ public partial class CS2_SimpleAdmin
|
||||
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
if (player.Connected != PlayerConnectedState.PlayerConnected)
|
||||
if (player.Connected != PlayerConnectedState.Connected)
|
||||
return;
|
||||
|
||||
if (caller!.CanTarget(player))
|
||||
@@ -207,7 +207,7 @@ public partial class CS2_SimpleAdmin
|
||||
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)
|
||||
if (!player.IsValid || player.Connected != PlayerConnectedState.Connected)
|
||||
return;
|
||||
|
||||
// Ensure the caller can target the player
|
||||
@@ -284,7 +284,7 @@ public partial class CS2_SimpleAdmin
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
// Check if the player is connected and can be targeted
|
||||
if (player.Connected != PlayerConnectedState.PlayerConnected || !caller!.CanTarget(player))
|
||||
if (player.Connected != PlayerConnectedState.Connected || !caller!.CanTarget(player))
|
||||
return;
|
||||
|
||||
// Determine message key and arguments for the rename notification
|
||||
@@ -330,7 +330,7 @@ public partial class CS2_SimpleAdmin
|
||||
playersToTarget.ForEach(player =>
|
||||
{
|
||||
// Check if the player is connected and can be targeted
|
||||
if (player.Connected != PlayerConnectedState.PlayerConnected || !caller!.CanTarget(player))
|
||||
if (player.Connected != PlayerConnectedState.Connected || !caller!.CanTarget(player))
|
||||
return;
|
||||
|
||||
// Determine message key and arguments for the rename notification
|
||||
@@ -379,7 +379,7 @@ public partial class CS2_SimpleAdmin
|
||||
return;
|
||||
|
||||
destinationPlayer = targets.Players.FirstOrDefault(p =>
|
||||
p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||
p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.Connected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||
|
||||
if (destinationPlayer == null || !caller.CanTarget(destinationPlayer) || caller.PlayerPawn.Value == null)
|
||||
return;
|
||||
@@ -399,7 +399,7 @@ public partial class CS2_SimpleAdmin
|
||||
return;
|
||||
|
||||
playersToTeleport = targets.Players
|
||||
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller.CanTarget(p))
|
||||
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.Connected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller.CanTarget(p))
|
||||
.ToList();
|
||||
|
||||
if (!playersToTeleport.Any())
|
||||
@@ -476,7 +476,7 @@ public partial class CS2_SimpleAdmin
|
||||
destinationPlayer = caller;
|
||||
|
||||
playersToTeleport = targets.Players
|
||||
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller.CanTarget(p))
|
||||
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.Connected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller.CanTarget(p))
|
||||
.ToList();
|
||||
}
|
||||
else
|
||||
@@ -486,7 +486,7 @@ public partial class CS2_SimpleAdmin
|
||||
return;
|
||||
|
||||
destinationPlayer = destination.Players.FirstOrDefault(p =>
|
||||
p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||
p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.Connected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE });
|
||||
|
||||
if (destinationPlayer == null)
|
||||
return;
|
||||
@@ -497,7 +497,7 @@ public partial class CS2_SimpleAdmin
|
||||
return;
|
||||
|
||||
playersToTeleport = targets.Players
|
||||
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller!.CanTarget(p))
|
||||
.Where(p => p is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.Connected, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE } && caller!.CanTarget(p))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ public interface IDatabaseProvider
|
||||
string GetUpdateBanStatusQuery();
|
||||
string GetExpireBansQuery(bool multiServer);
|
||||
string GetExpireIpBansQuery(bool multiServer);
|
||||
string GetExpireOldPlayerIpsQuery();
|
||||
|
||||
// MuteManager
|
||||
string GetAddMuteQuery(bool includePlayerName);
|
||||
|
||||
@@ -252,6 +252,11 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider
|
||||
: "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime AND server_id = @serverid";
|
||||
}
|
||||
|
||||
public string GetExpireOldPlayerIpsQuery()
|
||||
{
|
||||
return "DELETE FROM sa_players_ips WHERE used_at <= @ipBansTime";
|
||||
}
|
||||
|
||||
public string GetAddMuteQuery(bool includePlayerName) =>
|
||||
includePlayerName
|
||||
? """
|
||||
|
||||
@@ -167,6 +167,9 @@ public class SqliteDatabaseProvider(string filePath) : IDatabaseProvider
|
||||
? "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime"
|
||||
: "UPDATE sa_bans SET player_ip = NULL WHERE status = 'ACTIVE' AND ends <= @ipBansTime AND server_id = @serverid";
|
||||
|
||||
public string GetExpireOldPlayerIpsQuery() =>
|
||||
"DELETE FROM sa_players_ips WHERE used_at <= @ipBansTime";
|
||||
|
||||
public string GetAdminsQuery() =>
|
||||
"""
|
||||
SELECT sa_admins.player_steamid, sa_admins.player_name, sa_admins_flags.flag, sa_admins.immunity, sa_admins.ends
|
||||
|
||||
@@ -259,7 +259,7 @@ public partial class CS2_SimpleAdmin
|
||||
{
|
||||
var player = Utilities.GetPlayerFromSteamId(list.Key);
|
||||
|
||||
if (player == null || !player.IsValid || player.Connected != PlayerConnectedState.PlayerConnected)
|
||||
if (player == null || !player.IsValid || player.Connected != PlayerConnectedState.Connected)
|
||||
continue;
|
||||
|
||||
if (player.PlayerName.Equals(list.Value))
|
||||
@@ -328,7 +328,7 @@ public partial class CS2_SimpleAdmin
|
||||
? Utilities.GetPlayerFromUserid(userId)
|
||||
: null;
|
||||
|
||||
if (target == null || !target.IsValid || target.Connected != PlayerConnectedState.PlayerConnected)
|
||||
if (target == null || !target.IsValid || target.Connected != PlayerConnectedState.Connected)
|
||||
return HookResult.Continue;
|
||||
|
||||
return !player.CanTarget(target) ? HookResult.Stop : HookResult.Continue;
|
||||
@@ -473,7 +473,7 @@ public partial class CS2_SimpleAdmin
|
||||
var player = @event.Userid;
|
||||
|
||||
if (player?.UserId == null || !player.IsValid || player.IsHLTV ||
|
||||
player.Connected != PlayerConnectedState.PlayerConnected || !PlayersInfo.ContainsKey(player.SteamID) ||
|
||||
player.Connected != PlayerConnectedState.Connected || !PlayersInfo.ContainsKey(player.SteamID) ||
|
||||
@event.Attacker == null)
|
||||
return HookResult.Continue;
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ using CounterStrikeSharp.API.ValveConstants.Protobuf;
|
||||
using CS2_SimpleAdminApi;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.RegularExpressions;
|
||||
using CounterStrikeSharp.API.Core.Plugin.Host;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
@@ -64,7 +66,7 @@ internal static class Helper
|
||||
|
||||
public static List<CCSPlayerController> GetValidPlayers()
|
||||
{
|
||||
return CS2_SimpleAdmin.CachedPlayers.AsValueEnumerable().Where(p => p.IsValid && p.Connected == PlayerConnectedState.PlayerConnected).ToList();
|
||||
return CS2_SimpleAdmin.CachedPlayers.AsValueEnumerable().Where(p => p.IsValid && p.Connected == PlayerConnectedState.Connected).ToList();
|
||||
}
|
||||
|
||||
public static List<CCSPlayerController> GetValidPlayersWithBots()
|
||||
@@ -855,26 +857,34 @@ internal static class Helper
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateConfig<T>(T config) where T : BasePluginConfig, new()
|
||||
public static void UpdateConfig(BasePluginConfig config)
|
||||
{
|
||||
// get newest config version
|
||||
var newCfgVersion = new T().Version;
|
||||
var configType = config.GetType();
|
||||
var newCfgVersion = ((BasePluginConfig)Activator.CreateInstance(configType)!).Version;
|
||||
|
||||
// loaded config is up to date
|
||||
if (config.Version == newCfgVersion)
|
||||
return;
|
||||
|
||||
// update the version
|
||||
config.Version = newCfgVersion;
|
||||
// Load existing JSON file and update version property
|
||||
if (!File.Exists(CfgPath))
|
||||
return;
|
||||
|
||||
// serialize the updated config back to json
|
||||
var updatedJsonContent = JsonSerializer.Serialize(config,
|
||||
new JsonSerializerOptions
|
||||
var json = File.ReadAllText(CfgPath);
|
||||
var node = JsonNode.Parse(json);
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
node["Version"] = newCfgVersion;
|
||||
var updatedJsonContent = node.ToJsonString(new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
});
|
||||
File.WriteAllText(CfgPath, updatedJsonContent);
|
||||
|
||||
File.WriteAllText(CfgPath, updatedJsonContent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TryLogCommandOnDiscord(CCSPlayerController? caller, string commandString)
|
||||
|
||||
@@ -431,6 +431,9 @@ public async Task UnbanPlayer(string playerPattern, string adminSteamId, string
|
||||
var ipBansTime = currentTime.AddDays(-CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans);
|
||||
sql = databaseProvider.GetExpireIpBansQuery(CS2_SimpleAdmin.Instance.Config.MultiServerMode);
|
||||
await connection.ExecuteAsync(sql, new { ipBansTime, CS2_SimpleAdmin.ServerId });
|
||||
|
||||
sql = databaseProvider.GetExpireOldPlayerIpsQuery();
|
||||
await connection.ExecuteAsync(sql, new { ipBansTime });
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
|
||||
@@ -626,34 +626,81 @@ internal class CacheManager: IDisposable
|
||||
if (_cachedIgnoredIps.Contains(ipUInt))
|
||||
return false;
|
||||
|
||||
if (!_ipIndex.TryGetValue(ipUInt, out var ipBanRecords))
|
||||
return false;
|
||||
|
||||
var ipBan = ipBanRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
if (ipBan == null)
|
||||
return false;
|
||||
|
||||
if (!_banCache.TryGetValue(ipBan.Id, out var cachedIpBan) || cachedIpBan.StatusEnum != BanStatus.ACTIVE)
|
||||
return false;
|
||||
|
||||
var expireOldIpBans = CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans;
|
||||
if (expireOldIpBans > 0)
|
||||
// Direct ip ban (ban record has player_ip set)
|
||||
if (_ipIndex.TryGetValue(ipUInt, out var ipBanRecords))
|
||||
{
|
||||
var cutoff = Time.ActualDateTime().AddDays(-expireOldIpBans);
|
||||
if (ipBan.Created < cutoff)
|
||||
return false;
|
||||
var ipBan = ipBanRecords.FirstOrDefault(r => r.StatusEnum == BanStatus.ACTIVE);
|
||||
if (ipBan != null && _banCache.TryGetValue(ipBan.Id, out var cachedIpBan) && cachedIpBan.StatusEnum == BanStatus.ACTIVE)
|
||||
{
|
||||
var expireOldIpBans = CS2_SimpleAdmin.Instance.Config.OtherSettings.ExpireOldIpBans;
|
||||
if (expireOldIpBans <= 0 || ipBan.Created >= Time.ActualDateTime().AddDays(-expireOldIpBans))
|
||||
{
|
||||
if (string.IsNullOrEmpty(ipBan.PlayerName))
|
||||
ipBan.PlayerName = playerName;
|
||||
ipBan.PlayerSteamId ??= steamId;
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unknownName = CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown";
|
||||
// Multiaccount ban - check if other accounts using current ip are banned
|
||||
if (!_playerIpsCache.IsEmpty)
|
||||
{
|
||||
foreach (var (otherSteamId, ipSet) in _playerIpsCache)
|
||||
{
|
||||
// Skip current player
|
||||
if (otherSteamId == steamId)
|
||||
continue;
|
||||
|
||||
if (string.IsNullOrEmpty(ipBan.PlayerName))
|
||||
ipBan.PlayerName = playerName;
|
||||
// Check if this ip is in the other accounts ip history
|
||||
if (ipSet.All(record => record.Ip != ipUInt)) continue;
|
||||
// Found another account using this ip - check if its banned
|
||||
if (!_steamIdIndex.TryGetValue(otherSteamId, out var otherSteamBans)) continue;
|
||||
var activeBan = otherSteamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
if (activeBan == null || !_banCache.TryGetValue(activeBan.Id, out var cachedBan) ||
|
||||
cachedBan.StatusEnum != BanStatus.ACTIVE) continue;
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ipBan.PlayerSteamId ??= steamId;
|
||||
// Multiaccount ban - check if this player used any ip where other banned accounts are connected
|
||||
// Search sa_players_ips for all accounts sharing the same ips as current player
|
||||
if (!CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp)
|
||||
return false;
|
||||
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
if (!_playerIpsCache.TryGetValue(steamId, out var playerIps))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
// For each ip the player used (current or historical)
|
||||
foreach (var playerIpRecord in playerIps)
|
||||
{
|
||||
// Search sa_players_ips for other accounts using this same ip (as uint)
|
||||
foreach (var (otherSteamId, otherIpSet) in _playerIpsCache)
|
||||
{
|
||||
if (otherSteamId == steamId)
|
||||
continue;
|
||||
|
||||
// Check if this other account used the player ip
|
||||
if (otherIpSet.All(record => record.Ip != playerIpRecord.Ip))
|
||||
continue;
|
||||
|
||||
// Check if this other account is banned
|
||||
if (!_steamIdIndex.TryGetValue(otherSteamId, out var otherSteamBans))
|
||||
continue;
|
||||
|
||||
var activeBan = otherSteamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE);
|
||||
if (activeBan == null || !_banCache.TryGetValue(activeBan.Id, out var cachedBan) ||
|
||||
cachedBan.StatusEnum != BanStatus.ACTIVE)
|
||||
continue;
|
||||
|
||||
_ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace CS2_SimpleAdmin.Managers;
|
||||
|
||||
internal class PlayerManager
|
||||
{
|
||||
private readonly SemaphoreSlim _loadPlayerSemaphore = new(10);
|
||||
private readonly SemaphoreSlim _loadPlayerSemaphore = new(6);
|
||||
private readonly CS2_SimpleAdminConfig _config = CS2_SimpleAdmin.Instance.Config;
|
||||
|
||||
/// <summary>
|
||||
@@ -51,69 +51,41 @@ internal class PlayerManager
|
||||
try
|
||||
{
|
||||
await _loadPlayerSemaphore.WaitAsync();
|
||||
if (!CS2_SimpleAdmin.PlayersInfo.ContainsKey(steamId))
|
||||
|
||||
// Save ip address before ban check
|
||||
await SavePlayerIpAddress(steamId, playerName, ipAddress);
|
||||
|
||||
// Always check bans first, regardless of PlayersInfo state
|
||||
var isBanned = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch
|
||||
{
|
||||
var isBanned = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch
|
||||
0 => CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(playerName, steamId, null),
|
||||
_ => CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp
|
||||
? CS2_SimpleAdmin.Instance.CacheManager.IsPlayerOrAnyIpBanned(playerName, steamId,
|
||||
ipAddress)
|
||||
: CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(playerName, steamId, ipAddress)
|
||||
};
|
||||
|
||||
CS2_SimpleAdmin._logger?.LogInformation($"[BAN CHECK] Player {playerName} ({steamId}) IP: {ipAddress} - BanType: {CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType} - CheckMultiAccounts: {CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp} - isBanned: {isBanned}");
|
||||
|
||||
if (isBanned)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogInformation($"[BAN CHECK] KICKING {playerName} ({steamId})");
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
0 => CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(playerName, steamId, null),
|
||||
_ => CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp
|
||||
? CS2_SimpleAdmin.Instance.CacheManager.IsPlayerOrAnyIpBanned(playerName, steamId,
|
||||
ipAddress)
|
||||
: CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(playerName, steamId, ipAddress)
|
||||
};
|
||||
CS2_SimpleAdmin._logger?.LogInformation($"[BAN CHECK] Executing kick for {playerName}");
|
||||
Helper.KickPlayer(userId, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED);
|
||||
});
|
||||
|
||||
// CS2_SimpleAdmin._logger?.LogInformation($"Player {playerName} ({steamId} - {ipAddress}) is banned? {isBanned.ToString()}");
|
||||
|
||||
if (isBanned)
|
||||
{
|
||||
await Server.NextWorldUpdateAsync(() =>
|
||||
{
|
||||
// CS2_SimpleAdmin._logger?.LogInformation($"Kicking {playerName}");
|
||||
Helper.KickPlayer(userId, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullConnect)
|
||||
if (!CS2_SimpleAdmin.PlayersInfo.ContainsKey(steamId))
|
||||
{
|
||||
var playerInfo = new PlayerInfo(userId, slot, new SteamID(steamId), playerName, ipAddress);
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId] = playerInfo;
|
||||
|
||||
if (_config.OtherSettings.CheckMultiAccountsByIp && ipAddress != null &&
|
||||
CS2_SimpleAdmin.PlayersInfo[steamId] != null)
|
||||
if (_config.OtherSettings.CheckMultiAccountsByIp && ipAddress != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
|
||||
// Eliminates the need for SELECT COUNT and duplicate UPDATE queries
|
||||
var steamId64 = CS2_SimpleAdmin.PlayersInfo[steamId].SteamId.SteamId64;
|
||||
var ipUint = IpHelper.IpToUint(ipAddress);
|
||||
|
||||
// Use database-specific UPSERT query (handles MySQL vs SQLite syntax differences)
|
||||
var upsertQuery = CS2_SimpleAdmin.DatabaseProvider.GetUpsertPlayerIpQuery();
|
||||
|
||||
await connection.ExecuteAsync(upsertQuery, new
|
||||
{
|
||||
SteamID = steamId64,
|
||||
playerName,
|
||||
IPAddress = ipUint
|
||||
});
|
||||
|
||||
// // Cache will be updated on next refresh cycle
|
||||
// if (!CS2_SimpleAdmin.Instance.CacheManager.HasIpForPlayer(steamId, ipAddress))
|
||||
// {
|
||||
// // IP association will be reflected after cache refresh
|
||||
// }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(
|
||||
$"Unable to save ip address for {playerInfo.Name} ({ipAddress}): {ex.Message}");
|
||||
}
|
||||
|
||||
playerInfo.AccountsAssociated =
|
||||
CS2_SimpleAdmin.Instance.CacheManager?.GetAccountsByIp(ipAddress).AsValueEnumerable()
|
||||
.Select(x => (x.SteamId, x.PlayerName)).ToList() ?? [];
|
||||
@@ -200,7 +172,7 @@ internal class PlayerManager
|
||||
AdminManager.PlayerHasPermissions(
|
||||
new SteamID(p.SteamID),
|
||||
"@css/ban")) &&
|
||||
p.Connected == PlayerConnectedState.PlayerConnected &&
|
||||
p.Connected == PlayerConnectedState.Connected &&
|
||||
!CS2_SimpleAdmin.AdminDisabledJoinComms
|
||||
.Contains(p.SteamID)))
|
||||
{
|
||||
@@ -254,6 +226,38 @@ internal class PlayerManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves player's IP address to the database for multi-account detection.
|
||||
/// This is called before ban checks to ensure IP is recorded even if player is banned.
|
||||
/// </summary>
|
||||
private async Task SavePlayerIpAddress(ulong steamId, string playerName, string? ipAddress)
|
||||
{
|
||||
if (!_config.OtherSettings.CheckMultiAccountsByIp || ipAddress == null || CS2_SimpleAdmin.DatabaseProvider == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync();
|
||||
|
||||
var steamId64 = steamId;
|
||||
var ipUint = IpHelper.IpToUint(ipAddress);
|
||||
|
||||
var upsertQuery = CS2_SimpleAdmin.DatabaseProvider.GetUpsertPlayerIpQuery();
|
||||
|
||||
await connection.ExecuteAsync(upsertQuery, new
|
||||
{
|
||||
SteamID = steamId64,
|
||||
playerName,
|
||||
IPAddress = ipUint
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CS2_SimpleAdmin._logger?.LogError(
|
||||
$"Unable to save ip address for {playerName} ({ipAddress}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Periodically checks the status of online players and applies timers for speed, gravity,
|
||||
/// and penalty expiration validation.
|
||||
|
||||
@@ -42,7 +42,7 @@ internal class WarnManager(IDatabaseProvider? databaseProvider)
|
||||
|
||||
return warnId;
|
||||
}
|
||||
catch(Exception e)
|
||||
catch(Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.7.8-beta-10b1
|
||||
1.7.9a
|
||||
@@ -9,7 +9,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.361" />
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.367" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -45,7 +45,7 @@ public class CS2_SimpleAdmin_BanSoundModule: BasePlugin
|
||||
foreach (var player in Utilities.GetPlayers().Where(p => p.IsValid && !p.IsBot))
|
||||
{
|
||||
var filter = new RecipientFilter(player);
|
||||
player?.EmitSound("bansound", volume: 0.9f, recipients: filter);
|
||||
player?.EmitSound("bansound", volume: 0.75f, recipients: filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user