229 lines
8.5 KiB
C#
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();
|
|
}
|
|
}
|
|
} |