- MultiServerMode fix
- New feature `TimeMode`
This commit is contained in:
Dawid Bepierszcz
2024-05-06 22:56:57 +02:00
parent d5a6ceacb6
commit 4244104d6e
9 changed files with 172 additions and 70 deletions

View File

@@ -22,9 +22,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore
@@ -38,9 +38,9 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore
@@ -55,13 +55,13 @@ jobs:
${{ env.OUTPUT_PATH }}/Microsoft.DotNet.PlatformAbstractions.dll \
${{ env.OUTPUT_PATH }}/Microsoft.Extensions.DependencyModel.dll \
- name: Zip
uses: thedoctor0/zip-release@0.7.5
uses: thedoctor0/zip-release@0.7.6
with:
type: 'zip'
filename: '${{ env.PROJECT_NAME }}.zip'
path: ${{ env.OUTPUT_PATH }}
- name: CS2-SimpleAdmin
uses: ncipollo/release-action@v1.12.0
uses: ncipollo/release-action@v1.14.0
with:
artifacts: "${{ env.PROJECT_NAME }}.zip"
name: "Build ${{ env.BUILD_NUMBER }}"

View File

@@ -11,7 +11,7 @@ using System.Collections.Concurrent;
namespace CS2_SimpleAdmin;
[MinimumApiVersion(220)]
[MinimumApiVersion(225)]
public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdminConfig>
{
public static CS2_SimpleAdmin Instance { get; private set; } = new();
@@ -37,7 +37,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 & Dliix66";
public override string ModuleVersion => "1.4.3d";
public override string ModuleVersion => "1.4.4a";
public CS2_SimpleAdminConfig Config { get; set; } = new();

View File

@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.225" />
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.228" />
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="Discord.Net.Webhook" Version="3.14.1" />
<PackageReference Include="MySqlConnector" Version="2.3.7" />

View File

@@ -142,6 +142,9 @@ namespace CS2_SimpleAdmin
[JsonPropertyName("BanType")]
public int BanType { get; set; } = 1;
[JsonPropertyName("TimeMode")]
public int TimeMode { get; set; } = 1;
[JsonPropertyName("MaxBanDuration")]
public int MaxBanDuration { get; set; } = 60 * 24 * 7; // 7 days
[JsonPropertyName("MultiServerMode")]

View File

@@ -148,7 +148,6 @@ public partial class CS2_SimpleAdmin
string muteType = mute.type;
DateTime ends = mute.ends;
int duration = mute.duration;
switch (muteType)
{
// Apply mute penalty based on mute type
@@ -275,7 +274,7 @@ public partial class CS2_SimpleAdmin
{
var ipAddress = ConVar.Find("ip")?.StringValue;
if (string.IsNullOrEmpty(ipAddress))
if (string.IsNullOrEmpty(ipAddress) || ipAddress.StartsWith("0.0.0"))
{
Logger.LogError("Unable to get server ip, Check that you have added the correct start parameter \"-ip <ip>\"");
}
@@ -352,8 +351,8 @@ public partial class CS2_SimpleAdmin
var players = Helper.GetValidPlayers();
var onlinePlayers = players
.Where(player => player.IpAddress != null && player.SteamID.ToString().Length == 17)
.Select(player => (player.IpAddress, player.SteamID, player.UserId))
.Where(player => player.IpAddress != null)
.Select(player => (player.IpAddress, player.SteamID, player.UserId, player.Slot))
.ToList();
Task.Run(async () =>
@@ -363,18 +362,25 @@ public partial class CS2_SimpleAdmin
MuteManager muteManager = new(_database);
await banManager.ExpireOldBans();
await muteManager.ExpireOldMutes();
await adminManager.DeleteOldAdmins();
BannedPlayers.Clear();
if (onlinePlayers.Count > 0)
{
try
{
await banManager.CheckOnlinePlayers(onlinePlayers);
if (Config.TimeMode == 0)
{
await muteManager.CheckOnlineModeMutes(onlinePlayers);
}
}
catch { }
}
await muteManager.ExpireOldMutes();
await Server.NextFrameAsync(() =>
{
try

View File

@@ -165,8 +165,8 @@ internal class BanManager(Database.Database database, CS2_SimpleAdminConfig conf
var sql = "";
sql = config.MultiServerMode
? "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND server_id = @serverid"
: "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)";
? "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP)"
: "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND server_id = @serverid";
int banCount;
@@ -210,18 +210,12 @@ internal class BanManager(Database.Database database, CS2_SimpleAdminConfig conf
{
await using var connection = await database.GetConnectionAsync();
string sqlRetrieveBans;
if (config.MultiServerMode)
{
sqlRetrieveBans = "SELECT id FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE' " +
"AND server_id = @serverid";
}
else
{
sqlRetrieveBans = "SELECT id FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE'";
}
var sqlRetrieveBans = config.MultiServerMode
? "SELECT id FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE'"
: "SELECT id FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE' AND server_id = @serverid";
var bans = await connection.QueryAsync(sqlRetrieveBans, new { pattern = playerPattern, serverid = CS2_SimpleAdmin.ServerId });
var bansList = bans as dynamic[] ?? bans.ToArray();
if (bansList.Length == 0)
return;
@@ -262,25 +256,18 @@ internal class BanManager(Database.Database database, CS2_SimpleAdminConfig conf
catch { }
}
public async Task CheckOnlinePlayers(List<(string? IpAddress, ulong SteamID, int? UserId)> players)
public async Task CheckOnlinePlayers(List<(string? IpAddress, ulong SteamID, int? UserId, int Slot)> players)
{
try
{
await using var connection = await database.GetConnectionAsync();
string sql;
bool checkIpBans = config.BanType > 0;
if (config.MultiServerMode)
{
sql = "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND status = 'ACTIVE' AND" +
" server_id = @serverid";
}
else
{
sql = "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND status = 'ACTIVE'";
}
var sql = config.MultiServerMode
? "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND status = 'ACTIVE'"
: "SELECT COUNT(*) FROM sa_bans WHERE (player_steamid = @PlayerSteamID OR player_ip = @PlayerIP) AND status = 'ACTIVE' AND server_id = @serverid";
foreach (var (IpAddress, SteamID, UserId) in players)
foreach (var (IpAddress, SteamID, UserId, Slot) in players)
{
if (!UserId.HasValue) continue;
@@ -336,7 +323,6 @@ internal class BanManager(Database.Database database, CS2_SimpleAdminConfig conf
`duration` > 0
AND
ends <= @currentTime
AND server_id = @serverid
""" : """
UPDATE sa_bans
@@ -348,6 +334,7 @@ internal class BanManager(Database.Database database, CS2_SimpleAdminConfig conf
`duration` > 0
AND
ends <= @currentTime
AND server_id = @serverid
""";
await connection.ExecuteAsync(sql, new { currentTime, serverid = CS2_SimpleAdmin.ServerId });
@@ -364,7 +351,6 @@ internal class BanManager(Database.Database database, CS2_SimpleAdminConfig conf
status = 'ACTIVE'
AND
ends <= @ipBansTime
AND server_id = @serverid
""" : """
UPDATE sa_bans
@@ -374,6 +360,7 @@ internal class BanManager(Database.Database database, CS2_SimpleAdminConfig conf
status = 'ACTIVE'
AND
ends <= @ipBansTime
AND server_id = @serverid
""";
await connection.ExecuteAsync(sql, new { ipBansTime, CS2_SimpleAdmin.ServerId });

View File

@@ -1,4 +1,5 @@
using Dapper;
using CounterStrikeSharp.API.Core;
using Dapper;
using Microsoft.Extensions.Logging;
namespace CS2_SimpleAdmin;
@@ -96,16 +97,20 @@ internal class MuteManager(Database.Database database)
{
await using var connection = await database.GetConnectionAsync();
var currentTime = DateTime.UtcNow.ToLocalTime();
string sql;
var sql = "";
if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
{
sql = "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime) " +
"AND server_id = @serverid";
sql = CS2_SimpleAdmin.Instance.Config.TimeMode == 1
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)"
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0))";
}
else
{
sql = "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime)";
sql = CS2_SimpleAdmin.Instance.Config.TimeMode == 1
? "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR ends > @CurrentTime) AND server_id = @serverid"
: "SELECT * FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND status = 'ACTIVE' AND (duration = 0 OR duration > COALESCE(passed, 0)) AND server_id = @serverid";
}
var parameters = new { PlayerSteamID = steamId, CurrentTime = currentTime, serverid = CS2_SimpleAdmin.ServerId };
@@ -125,8 +130,8 @@ internal class MuteManager(Database.Database database)
await using var connection = await database.GetConnectionAsync();
var sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode
? "SELECT COUNT(*) FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND server_id = @serverid"
: "SELECT COUNT(*) FROM sa_mutes WHERE player_steamid = @PlayerSteamID";
? "SELECT COUNT(*) FROM sa_mutes WHERE player_steamid = @PlayerSteamID"
: "SELECT COUNT(*) FROM sa_mutes WHERE player_steamid = @PlayerSteamID AND server_id = @serverid";
var muteCount = await connection.ExecuteScalarAsync<int>(sql, new { PlayerSteamID = steamId, serverid = CS2_SimpleAdmin.ServerId });
return muteCount;
@@ -136,6 +141,56 @@ internal class MuteManager(Database.Database database)
return 0;
}
}
public async Task CheckOnlineModeMutes(List<(string? IpAddress, ulong SteamID, int? UserId, int Slot)> players)
{
try
{
int batchSize = 10;
await using var connection = await database.GetConnectionAsync();
var sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode
? "UPDATE `sa_mutes` SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE'"
: "UPDATE `sa_mutes` SET passed = COALESCE(passed, 0) + 1 WHERE (player_steamid = @PlayerSteamID) AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
/*
foreach (var (IpAddress, SteamID, UserId, Slot) in players)
{
await connection.ExecuteAsync(sql,
new { PlayerSteamID = SteamID, serverid = CS2_SimpleAdmin.ServerId });
}*/
for (var i = 0; i < players.Count; i += batchSize)
{
var batch = players.Skip(i).Take(batchSize);
var parametersList = new List<object>();
foreach (var (IpAddress, SteamID, UserId, Slot) in batch)
{
parametersList.Add(new { PlayerSteamID = SteamID, serverid = CS2_SimpleAdmin.ServerId });
}
await connection.ExecuteAsync(sql, parametersList);
}
sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode
? "SELECT * FROM `sa_mutes` WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE'"
: "SELECT * FROM `sa_mutes` WHERE player_steamid = @PlayerSteamID AND passed >= duration AND duration > 0 AND status = 'ACTIVE' AND server_id = @serverid";
foreach (var (IpAddress, SteamID, UserId, Slot) in players)
{
var muteRecords = await connection.QueryAsync(sql, new { PlayerSteamID = SteamID, serverid = CS2_SimpleAdmin.ServerId });
foreach (var muteRecord in muteRecords)
{
DateTime endDateTime = muteRecord.ends;
PlayerPenaltyManager.RemovePenaltiesByDateTime(Slot, endDateTime);
}
}
}
catch { }
}
public async Task UnmutePlayer(string playerPattern, string adminSteamId, string reason, int type = 0)
{
@@ -160,12 +215,12 @@ internal class MuteManager(Database.Database database)
if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
{
sqlRetrieveMutes = "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND " +
"type = @muteType AND status = 'ACTIVE' AND server_id = @serverid";
"type = @muteType AND status = 'ACTIVE'";
}
else
{
sqlRetrieveMutes = "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND " +
"type = @muteType AND status = 'ACTIVE'";
"type = @muteType AND status = 'ACTIVE' AND server_id = @serverid";
}
var mutes = await connection.QueryAsync(sqlRetrieveMutes, new { pattern = playerPattern, muteType, serverid = CS2_SimpleAdmin.ServerId });
@@ -212,10 +267,20 @@ internal class MuteManager(Database.Database database)
try
{
await using var connection = await database.GetConnectionAsync();
var sql = CS2_SimpleAdmin.Instance.Config.MultiServerMode
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime AND server_id = @serverid"
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime";
var sql = "";
if (CS2_SimpleAdmin.Instance.Config.MultiServerMode)
{
sql = CS2_SimpleAdmin.Instance.Config.TimeMode == 1
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime"
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND `passed` >= `duration`";
}
else
{
sql = CS2_SimpleAdmin.Instance.Config.TimeMode == 1
? "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND ends <= @CurrentTime AND server_id = @serverid"
: "UPDATE sa_mutes SET status = 'EXPIRED' WHERE status = 'ACTIVE' AND `duration` > 0 AND `passed` >= `duration` AND server_id = @serverid";
}
await connection.ExecuteAsync(sql, new { CurrentTime = DateTime.UtcNow.ToLocalTime(), serverid = CS2_SimpleAdmin.ServerId });
}

View File

@@ -11,18 +11,18 @@ public enum PenaltyType
public class PlayerPenaltyManager
{
private static readonly ConcurrentDictionary<int, Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration)>>> Penalties =
private static readonly ConcurrentDictionary<int, Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration, bool Passed)>>> Penalties =
new();
// Add a penalty for a player
public static void AddPenalty(int slot, PenaltyType penaltyType, DateTime endDateTime, int durationSeconds)
public static void AddPenalty(int slot, PenaltyType penaltyType, DateTime endDateTime, int durationInMinutes)
{
Penalties.AddOrUpdate(slot,
(_) =>
{
var dict = new Dictionary<PenaltyType, List<(DateTime, int)>>
var dict = new Dictionary<PenaltyType, List<(DateTime, int, bool)>>
{
[penaltyType] = [(endDateTime, durationSeconds)]
[penaltyType] = [(endDateTime, durationInMinutes, false)]
};
return dict;
},
@@ -30,11 +30,11 @@ public class PlayerPenaltyManager
{
if (!existingDict.TryGetValue(penaltyType, out var value))
{
value = new List<(DateTime, int)>();
value = new List<(DateTime, int, bool)>();
existingDict[penaltyType] = value;
}
value.Add((endDateTime, durationSeconds));
value.Add((endDateTime, durationInMinutes, false));
return existingDict;
});
}
@@ -46,6 +46,9 @@ public class PlayerPenaltyManager
if (!Penalties.TryGetValue(slot, out var penaltyDict) ||
!penaltyDict.TryGetValue(penaltyType, out var penaltiesList)) return false;
//Console.WriteLine($"Found penalties for player with slot {slot} and penalty type {penaltyType}");
if (CS2_SimpleAdmin.Instance.Config.TimeMode == 0)
return penaltiesList.Count != 0;
var now = DateTime.UtcNow.ToLocalTime();
@@ -53,7 +56,7 @@ public class PlayerPenaltyManager
foreach (var penalty in penaltiesList.ToList())
{
// Check if the penalty is still active
if (penalty.Duration > 0 && now >= penalty.EndDateTime.AddSeconds(penalty.Duration))
if (penalty.Duration > 0 && now >= penalty.EndDateTime)
{
//Console.WriteLine($"Removing expired penalty for player with slot {slot} and penalty type {penaltyType}");
penaltiesList.Remove(penalty); // Remove expired penalty
@@ -80,7 +83,7 @@ public class PlayerPenaltyManager
}
// Get the end datetime and duration of penalties for a player and penalty type
public static List<(DateTime EndDateTime, int Duration)> GetPlayerPenalties(int slot, PenaltyType penaltyType)
public static List<(DateTime EndDateTime, int Duration, bool Passed)> GetPlayerPenalties(int slot, PenaltyType penaltyType)
{
if (Penalties.TryGetValue(slot, out var penaltyDict) &&
penaltyDict.TryGetValue(penaltyType, out var penaltiesList))
@@ -113,26 +116,64 @@ public class PlayerPenaltyManager
// Remove all penalties of a selected type from a specific player
public static void RemovePenaltiesByType(int slot, PenaltyType penaltyType)
{
if (Penalties.TryGetValue(slot, out Dictionary<PenaltyType, List<(DateTime EndDateTime, int Duration)>>? penaltyDict) &&
if (Penalties.TryGetValue(slot, out var penaltyDict) &&
penaltyDict.ContainsKey(penaltyType))
{
penaltyDict.Remove(penaltyType);
}
}
public static void RemovePenaltiesByDateTime(int slot, DateTime dateTime)
{
if (!Penalties.TryGetValue(slot, out var penaltyDict)) return;
foreach (var penaltiesList in penaltyDict.Values)
{
for (var i = 0; i < penaltiesList.Count; i++)
{
if (penaltiesList[i].EndDateTime != dateTime) continue;
// Create a copy of the penalty
var penalty = penaltiesList[i];
// Update the end datetime of the copied penalty to the current datetime
penalty.Passed = true;
// Replace the original penalty with the modified one
penaltiesList[i] = penalty;
}
}
}
// Remove all expired penalties for all players and penalty types
public static void RemoveExpiredPenalties()
{
var now = DateTime.UtcNow.ToLocalTime();
foreach (var kvp in Penalties.ToList()) // Use ToList to avoid modification while iterating
if (CS2_SimpleAdmin.Instance.Config.TimeMode == 0)
{
var playerSlot = kvp.Key;
var penaltyDict = kvp.Value;
foreach (var (playerSlot, penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating
{
// Remove expired penalties for the player
foreach (var penaltiesList in penaltyDict.Values)
{
penaltiesList.RemoveAll(p => p is { Duration: > 0, Passed: true });
}
// Remove player slot if no penalties left
if (penaltyDict.Count == 0)
{
Penalties.TryRemove(playerSlot, out _);
}
}
return;
}
var now = DateTime.UtcNow.ToLocalTime();
foreach (var (playerSlot, penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating
{
// Remove expired penalties for the player
foreach (var penaltiesList in penaltyDict.Values)
{
penaltiesList.RemoveAll(p => p.Duration > 0 && now >= p.EndDateTime.AddSeconds(p.Duration).ToLocalTime());
penaltiesList.RemoveAll(p => p.Duration > 0 && now >= p.EndDateTime);
}
// Remove player slot if no penalties left

View File

@@ -1 +1 @@
1.4.3d
1.4.4a