2025-06-08 00:54:06 +02:00

162 lines
6.5 KiB
C#

using LabApi.Features;
using LabApi.Loader.Features.Plugins;
using System.Collections.Concurrent;
using System.Data.SQLite;
using LabApi.Events.Arguments.PlayerEvents;
using LabApi.Events.Handlers;
using LabApi.Loader;
namespace StatsTracker
{
public class StatsTracker : Plugin
{
public override string Name => "StatsTracker";
public override string Author => "Code002Lover";
public override Version Version { get; } = new(1, 0, 0);
public override string Description => "Tracks stats for players.";
public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion);
private string _dbPath;
private readonly ConcurrentDictionary<string, PlayerStats> _currentSessionStats = new();
private class PlayerStats
{
public int Kills { get; set; }
public int Deaths { get; set; }
public Dictionary<ItemType, int> ItemUsage { get; set; } = new();
}
public override void Enable()
{
_dbPath = Path.Combine(this.GetConfigDirectory().FullName, "stats.db");
InitializeDatabase();
PlayerEvents.Death += OnPlayerDied;
PlayerEvents.UsedItem += OnItemUsed;
PlayerEvents.Left += OnPlayerLeft;
}
public override void Disable()
{
PlayerEvents.Death -= OnPlayerDied;
PlayerEvents.UsedItem -= OnItemUsed;
PlayerEvents.Left -= OnPlayerLeft;
// Save any remaining stats
foreach (var player in _currentSessionStats)
{
SavePlayerStats(player.Key, player.Value);
}
_currentSessionStats.Clear();
}
private void InitializeDatabase()
{
using var connection = new SQLiteConnection($"Data Source={_dbPath}");
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = """
CREATE TABLE IF NOT EXISTS PlayerStats (
UserId TEXT PRIMARY KEY,
Kills INTEGER DEFAULT 0,
Deaths INTEGER DEFAULT 0
);
CREATE TABLE IF NOT EXISTS ItemUsage (
UserId TEXT,
ItemType INTEGER,
UsageCount INTEGER DEFAULT 0,
PRIMARY KEY (UserId, ItemType)
);
""";
command.ExecuteNonQuery();
}
private void OnPlayerDied(PlayerDeathEventArgs ev)
{
if (ev.Attacker != null)
{
var killerStats = _currentSessionStats.GetOrAdd(ev.Attacker.UserId, _ => new PlayerStats());
killerStats.Kills++;
}
var victimStats = _currentSessionStats.GetOrAdd(ev.Player.UserId, _ => new PlayerStats());
victimStats.Deaths++;
}
private void OnItemUsed(PlayerUsedItemEventArgs ev)
{
var stats = _currentSessionStats.GetOrAdd(ev.Player.UserId, _ => new PlayerStats());
if (!stats.ItemUsage.ContainsKey(ev.UsableItem.Type))
stats.ItemUsage[ev.UsableItem.Type] = 0;
stats.ItemUsage[ev.UsableItem.Type]++;
}
private void OnPlayerLeft(PlayerLeftEventArgs ev)
{
if (_currentSessionStats.TryRemove(ev.Player.UserId, out var stats))
{
SavePlayerStats(ev.Player.UserId, stats);
}
}
private void SavePlayerStats(string userId, PlayerStats stats)
{
using var connection = new SQLiteConnection($"Data Source={_dbPath}");
connection.Open();
using var transaction = connection.BeginTransaction();
try
{
// Update player stats
using (var command = connection.CreateCommand())
{
command.CommandText = """
INSERT INTO PlayerStats (UserId, Kills, Deaths)
VALUES (@userId, @kills, @deaths)
ON CONFLICT(UserId) DO UPDATE SET
Kills = Kills + @kills,
Deaths = Deaths + @deaths;
""";
command.Parameters.AddWithValue("@userId", userId);
command.Parameters.AddWithValue("@kills", stats.Kills);
command.Parameters.AddWithValue("@deaths", stats.Deaths);
command.ExecuteNonQuery();
}
// Update item usage
foreach (var itemInfoPair in stats.ItemUsage)
{
var itemType = itemInfoPair.Key;
var count = itemInfoPair.Value;
using var command = connection.CreateCommand();
command.CommandText = """
INSERT INTO ItemUsage (UserId, ItemType, UsageCount)
VALUES (@userId, @itemType, @count)
ON CONFLICT(UserId, ItemType) DO UPDATE SET
UsageCount = UsageCount + @count;
""";
command.Parameters.AddWithValue("@userId", userId);
command.Parameters.AddWithValue("@itemType", (int)itemType);
command.Parameters.AddWithValue("@count", count);
command.ExecuteNonQuery();
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
}