2025-06-09 22:13:12 +02:00

229 lines
8.5 KiB
C#

using LabApi.Features;
using LabApi.Loader.Features.Plugins;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using LabApi.Events.Arguments.PlayerEvents;
using LabApi.Events.Handlers;
using LabApi.Features.Console;
namespace StatsTracker
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public struct PlayerStat
{
public uint kills;
public uint deaths;
public uint team_damage;
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
public struct ItemStat
{
public string item_name;
public uint item_count;
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
public struct Player
{
public string player_id;
public PlayerStat player_stats;
public ItemStat[] player_items;
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
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 const string RustDllName = "stats_tracker";
[DllImport(RustDllName, EntryPoint="get_player_stats", CallingConvention = CallingConvention.Cdecl)]
private static extern ref Player GetPlayerStats(ref string player_id);
[DllImport(RustDllName, EntryPoint="save_player_stats", CallingConvention = CallingConvention.Cdecl)]
private static extern bool SavePlayerStats(ref Player player);
public override void Enable()
{
var pathVariable = Environment.GetEnvironmentVariable("PATH");
Logger.Debug($"PATH: {pathVariable}");
var extractedDllPath = ExtractRustDll(); // Call extraction
if (string.IsNullOrEmpty(extractedDllPath))
{
Logger.Error("Failed to extract Rust DLL. Exiting.");
return;
}
var libDirectory = Path.GetDirectoryName(extractedDllPath);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
SetDllDirectory(libDirectory);
Logger.Info($"Windows: Added '{libDirectory}' to DLL search path.");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", libDirectory + ":" + Environment.GetEnvironmentVariable("LD_LIBRARY_PATH"));
Logger.Info($"Linux: Extracted library to '{libDirectory}'. Relying on default search paths and manual LD_LIBRARY_PATH.");
}
else
{
Logger.Error("Only windows and linux are supported.");
return;
}
PlayerEvents.Death += OnPlayerDied;
PlayerEvents.UsedItem += OnItemUsed;
PlayerEvents.Hurt += OnHurt;
}
// Conditional P/Invoke for OS-specific functions (like SetDllDirectory on Windows)
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetDllDirectory(string lpPathName);
private static string ExtractRustDll()
{
string dllFilename;
string resourceSubPath; // Folder inside NativeLibs
string extractedLibFilename;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
dllFilename = $"{RustDllName}.dll";
resourceSubPath = "x86_64_pc_windows_gnu";
extractedLibFilename = dllFilename; // On Windows, we keep the original name
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
dllFilename = $"lib{RustDllName}.so"; // Linux uses lib prefix and .so suffix
resourceSubPath = "x86_64_unknown_linux_gnu";
extractedLibFilename = dllFilename; // On Linux, keep the lib*.so name
}
else
{
Logger.Error("Unsupported operating system detected.");
return null;
}
// Determine where to extract the DLL.
var targetDirectory = AppDomain.CurrentDomain.BaseDirectory;
var extractedLibPath = Path.Combine(targetDirectory, extractedLibFilename);
// Check if the DLL already exists (e.g., from a previous run or development environment)
if (File.Exists(extractedLibPath))
{
Logger.Warn($"Rust lib '{dllFilename}' already exists at '{extractedLibPath}'. Skipping extraction.");
return extractedLibPath; // Return the path directly if it exists
}
try
{
// Adjust this resource name to match your actual embedded resource name.
// Based on your previous comment:
var resourceName = $"StatsTracker.Rust.target.{resourceSubPath}.release.{dllFilename}";
Logger.Info($"Attempting to load embedded resource: {resourceName}");
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
if (stream == null)
{
Logger.Error($"Error: Embedded resource '{resourceName}' not found.");
Logger.Info("Available resources:");
foreach (var res in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
Logger.Info($"- {res}");
}
return null; // Return null to indicate failure
}
using (var fileStream = File.Create(extractedLibPath))
{
stream.CopyTo(fileStream);
}
}
Logger.Info($"Successfully extracted '{RustDllName}' to '{extractedLibPath}'.");
return extractedLibPath; // Return the path after successful extraction
}
catch (Exception ex)
{
Logger.Error($"Error extracting Rust DLL to {extractedLibPath}: {ex.Message}");
return null; // Return null to indicate failure
}
}
public override void Disable()
{
PlayerEvents.Death -= OnPlayerDied;
PlayerEvents.UsedItem -= OnItemUsed;
}
private static void OnHurt(PlayerHurtEventArgs ev)
{
switch (ev.Attacker)
{
case null:
case { DoNotTrack: true }:
return;
}
if(ev.Attacker.Team != ev.Player.Team) return;
var userId = ev.Attacker.UserId;
var killerStats = GetPlayerStats(ref userId);
killerStats.player_stats.team_damage++;
SavePlayerStats(ref killerStats);
}
private static void OnPlayerDied(PlayerDeathEventArgs ev)
{
if (ev.Attacker != null && ev.Attacker.Nickname != "emeraldo" && !ev.Attacker.DoNotTrack)
{
var userId = ev.Attacker.UserId;
var killerStats = GetPlayerStats(ref userId);
killerStats.player_stats.kills++;
SavePlayerStats(ref killerStats);
}
if (ev.Player.DoNotTrack)
{
Logger.Debug($"Do Not Track: {ev.Player.Nickname}");
return;
}
var victimId = ev.Player.UserId;
var victimStats = GetPlayerStats(ref victimId);
victimStats.player_stats.deaths++;
SavePlayerStats(ref victimStats);
}
private static void OnItemUsed(PlayerUsedItemEventArgs ev)
{
if (ev.Player.DoNotTrack)
{
Logger.Debug($"Do Not Track: {ev.Player.Nickname}");
return;
}
var userId = ev.Player.UserId;
var stats = GetPlayerStats(ref userId);
var stat = stats.player_items[(int)ev.UsableItem.Type];
stat.item_count++;
stat.item_name = ev.UsableItem.Type.ToString();
}
}
}