Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class Additional

[JsonPropertyName("CommandMusic")]
public List<string> CommandMusic { get; set; } = ["music"];

[JsonPropertyName("CommandPin")]
public List<string> CommandPin { get; set; } = ["pin", "pins", "coin", "coins"];

Expand All @@ -43,7 +43,7 @@ public class Additional

[JsonPropertyName("CommandAgent")]
public List<string> CommandAgent { get; set; } = ["agents"];

[JsonPropertyName("CommandStattrak")]
public List<string> CommandStattrak { get; set; } = ["stattrak", "st"];

Expand All @@ -69,11 +69,29 @@ public class Additional
public bool ShowSkinImage { get; set; } = true;
}

public class DatabaseCleanup
{
[JsonPropertyName("Enabled")]
public bool Enabled { get; set; } = false;

[JsonPropertyName("InactiveDays")]
public int InactiveDays { get; set; } = 30;

[JsonPropertyName("CleanupIntervalMinutes")]
public int CleanupIntervalMinutes { get; set; } = 360;

[JsonPropertyName("RunOnStartup")]
public bool RunOnStartup { get; set; } = true;

[JsonPropertyName("LogCleanup")]
public bool LogCleanup { get; set; } = true;
}

public class WeaponPaintsConfig : BasePluginConfig
{
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 10;
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 11;

[JsonPropertyName("SkinsLanguage")]
[JsonPropertyName("SkinsLanguage")]
public string SkinsLanguage { get; set; } = "en";

[JsonPropertyName("DatabaseHost")]
Expand All @@ -99,8 +117,11 @@ public class WeaponPaintsConfig : BasePluginConfig

[JsonPropertyName("Additional")]
public Additional Additional { get; set; } = new();

[JsonPropertyName("MenuType")]
public string MenuType { get; set; } = "selectable";

[JsonPropertyName("DatabaseCleanup")]
public DatabaseCleanup DatabaseCleanup { get; set; } = new();
}
}
191 changes: 191 additions & 0 deletions DatabaseCleanup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Modules.Timers;
using Microsoft.Extensions.Logging;
using Dapper;

namespace WeaponPaints;

public class DatabaseCleanupService
{
private readonly Database _database;
private readonly WeaponPaintsConfig _config;
private readonly ILogger _logger;
private CounterStrikeSharp.API.Modules.Timers.Timer? _cleanupTimer;

public DatabaseCleanupService(Database database, WeaponPaintsConfig config, ILogger logger)
{
_database = database;
_config = config;
_logger = logger;
}

public void Initialize()
{
if (!_config.DatabaseCleanup.Enabled)
{
_logger.LogInformation("Database cleanup is disabled in configuration.");
return;
}

if (_config.DatabaseCleanup.InactiveDays <= 0)
{
_logger.LogWarning("Invalid InactiveDays configuration. Database cleanup will not run.");
return;
}

if (_config.DatabaseCleanup.RunOnStartup)
{
WeaponPaints.Instance.AddTimer(10.0f, () =>
{
_ = Task.Run(async () => await PerformCleanup());
}, TimerFlags.STOP_ON_MAPCHANGE);
}

float intervalSeconds = _config.DatabaseCleanup.CleanupIntervalMinutes * 60.0f;
_cleanupTimer = WeaponPaints.Instance.AddTimer(intervalSeconds, () =>
{
_ = Task.Run(async () => await PerformCleanup());
}, TimerFlags.REPEAT | TimerFlags.STOP_ON_MAPCHANGE);

_logger.LogInformation($"Database cleanup service initialized. Will run every {_config.DatabaseCleanup.CleanupIntervalMinutes} minutes for users inactive more than {_config.DatabaseCleanup.InactiveDays} days.");
}

public async Task PerformCleanup()
{
try
{
await using var connection = await _database.GetConnectionAsync();

var cutoffDate = DateTime.UtcNow.AddDays(-_config.DatabaseCleanup.InactiveDays);
var cutoffDateString = cutoffDate.ToString("yyyy-MM-dd HH:mm:ss");

int totalDeleted = 0;

const string selectInactiveSteamIds = @"
SELECT DISTINCT steamid
FROM wp_player_tracking
WHERE last_seen < @cutoffDate";

var inactiveSteamIds = (await connection.QueryAsync<string>(
selectInactiveSteamIds,
new { cutoffDate = cutoffDateString }
)).ToList();

if (inactiveSteamIds.Count == 0)
{
if (_config.DatabaseCleanup.LogCleanup)
{
_logger.LogInformation("Database cleanup: No inactive users found.");
}
return;
}

if (_config.Additional.SkinEnabled)
{
const string deleteSkins = "DELETE FROM `wp_player_skins` WHERE `steamid` IN @steamids";
var deleted = await connection.ExecuteAsync(deleteSkins, new { steamids = inactiveSteamIds });
totalDeleted += deleted;
if (_config.DatabaseCleanup.LogCleanup)
{
_logger.LogInformation($"Deleted {deleted} skin records for inactive users.");
}
}

if (_config.Additional.KnifeEnabled)
{
const string deleteKnives = "DELETE FROM `wp_player_knife` WHERE `steamid` IN @steamids";
var deleted = await connection.ExecuteAsync(deleteKnives, new { steamids = inactiveSteamIds });
totalDeleted += deleted;
if (_config.DatabaseCleanup.LogCleanup)
{
_logger.LogInformation($"Deleted {deleted} knife records for inactive users.");
}
}

if (_config.Additional.GloveEnabled)
{
const string deleteGloves = "DELETE FROM `wp_player_gloves` WHERE `steamid` IN @steamids";
var deleted = await connection.ExecuteAsync(deleteGloves, new { steamids = inactiveSteamIds });
totalDeleted += deleted;
if (_config.DatabaseCleanup.LogCleanup)
{
_logger.LogInformation($"Deleted {deleted} glove records for inactive users.");
}
}

if (_config.Additional.AgentEnabled)
{
const string deleteAgents = "DELETE FROM `wp_player_agents` WHERE `steamid` IN @steamids";
var deleted = await connection.ExecuteAsync(deleteAgents, new { steamids = inactiveSteamIds });
totalDeleted += deleted;
if (_config.DatabaseCleanup.LogCleanup)
{
_logger.LogInformation($"Deleted {deleted} agent records for inactive users.");
}
}

if (_config.Additional.MusicEnabled)
{
const string deleteMusic = "DELETE FROM `wp_player_music` WHERE `steamid` IN @steamids";
var deleted = await connection.ExecuteAsync(deleteMusic, new { steamids = inactiveSteamIds });
totalDeleted += deleted;
if (_config.DatabaseCleanup.LogCleanup)
{
_logger.LogInformation($"Deleted {deleted} music records for inactive users.");
}
}

if (_config.Additional.PinsEnabled)
{
const string deletePins = "DELETE FROM `wp_player_pins` WHERE `steamid` IN @steamids";
var deleted = await connection.ExecuteAsync(deletePins, new { steamids = inactiveSteamIds });
totalDeleted += deleted;
if (_config.DatabaseCleanup.LogCleanup)
{
_logger.LogInformation($"Deleted {deleted} pin records for inactive users.");
}
}

const string deleteTracking = "DELETE FROM `wp_player_tracking` WHERE `steamid` IN @steamids";
await connection.ExecuteAsync(deleteTracking, new { steamids = inactiveSteamIds });

if (_config.DatabaseCleanup.LogCleanup)
{
_logger.LogInformation($"Database cleanup completed: {totalDeleted} total records deleted for {inactiveSteamIds.Count} inactive users.");
}
}
catch (Exception ex)
{
_logger.LogError($"Error during database cleanup: {ex.Message}");
}
}

public async Task UpdatePlayerActivity(string steamId)
{
if (!_config.DatabaseCleanup.Enabled || string.IsNullOrEmpty(steamId))
return;

try
{
await using var connection = await _database.GetConnectionAsync();

var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");

const string query = @"
INSERT INTO `wp_player_tracking` (`steamid`, `last_seen`, `first_seen`)
VALUES (@steamid, @now, @now)
ON DUPLICATE KEY UPDATE `last_seen` = @now";

await connection.ExecuteAsync(query, new { steamid = steamId, now });
}
catch (Exception ex)
{
_logger.LogError($"Error updating player activity tracking: {ex.Message}");
}
}

public void Dispose()
{
_cleanupTimer?.Kill();
}
}
5 changes: 5 additions & 0 deletions Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public HookResult OnClientFullConnect(EventPlayerConnectFull @event, GameEventIn
_ = Task.Run(async () => await weaponSync.GetMusicFromDatabase(playerInfo));
}
*/

if (_cleanupService != null)
{
_ = Task.Run(async () => await _cleanupService.UpdatePlayerActivity(playerInfo.SteamId ?? ""));
}
}
catch
{
Expand Down
10 changes: 9 additions & 1 deletion Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,15 @@ internal static async Task CheckDatabaseTables()
`weapon_team` int(1) NOT NULL,
`id` int(11) NOT NULL,
UNIQUE (`steamid`, `weapon_team`) -- Unique constraint
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",

@"
CREATE TABLE IF NOT EXISTS `wp_player_tracking` (
`steamid` VARCHAR(64) NOT NULL PRIMARY KEY,
`last_seen` DATETIME NOT NULL,
`first_seen` DATETIME NOT NULL,
INDEX `idx_last_seen` (`last_seen`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"
];

foreach (var query in createTableQueries)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2a
3.2c
1 change: 1 addition & 0 deletions Variables.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public partial class WeaponPaints

private static readonly Dictionary<int, DateTime> CommandsCooldown = new();
internal static Database? Database;
private DatabaseCleanupService? _cleanupService;

private static readonly MemoryFunctionVoid<nint, string, float> CAttributeListSetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName"));

Expand Down
Loading