From e88876a62078780d74c30cd126b90cca11d4668b Mon Sep 17 00:00:00 2001
From: Heavy Bob <ferrettclay@gmail.com>
Date: Sat, 12 Apr 2025 07:43:28 +1000
Subject: [PATCH] Hash Kills

This change updates the .csv to store kill hashes and also stores hashes made in memory. This should prevent reporting the same kill twice or saving it to the .csv twice. This will break the old .csv however it is renamed to .old with the previous change.
---
 AutoTrackR2/HomePage.xaml.cs      | 10 +++++++++-
 AutoTrackR2/KillHistoryManager.cs | 11 ++++++-----
 AutoTrackR2/Util.cs               |  1 +
 AutoTrackR2/WebHandler.cs         | 24 ++++++++++++++++++++++--
 4 files changed, 38 insertions(+), 8 deletions(-)

diff --git a/AutoTrackR2/HomePage.xaml.cs b/AutoTrackR2/HomePage.xaml.cs
index 84ee0f7..165dffe 100644
--- a/AutoTrackR2/HomePage.xaml.cs
+++ b/AutoTrackR2/HomePage.xaml.cs
@@ -192,7 +192,8 @@ public partial class HomePage : UserControl
                         TrackRver = "2.10",
                         Enlisted = playerData?.JoinDate,
                         KillTime = ((DateTimeOffset)DateTime.ParseExact(actorDeathData.Timestamp, "yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal)).ToUnixTimeSeconds().ToString(),
-                        PFP = playerData?.PFPURL ?? "https://cdn.robertsspaceindustries.com/static/images/account/avatar_default_big.jpg"
+                        PFP = playerData?.PFPURL ?? "https://cdn.robertsspaceindustries.com/static/images/account/avatar_default_big.jpg",
+                        Hash = WebHandler.GenerateKillHash(actorDeathData.VictimPilot, ((DateTimeOffset)DateTime.ParseExact(actorDeathData.Timestamp, "yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal)).ToUnixTimeSeconds())
                     };
 
                     switch (LocalPlayerData.CurrentGameMode)
@@ -205,6 +206,13 @@ public partial class HomePage : UserControl
                             break;
                     }
 
+                    // Check if this is a duplicate kill
+                    if (WebHandler.IsDuplicateKill(killData.Hash))
+                    {
+                        Console.WriteLine("Duplicate kill detected, skipping...");
+                        return;
+                    }
+
                     // Add kill to UI
                     Dispatcher.Invoke(() =>
                     {
diff --git a/AutoTrackR2/KillHistoryManager.cs b/AutoTrackR2/KillHistoryManager.cs
index eb6c3a2..65f33ad 100644
--- a/AutoTrackR2/KillHistoryManager.cs
+++ b/AutoTrackR2/KillHistoryManager.cs
@@ -7,8 +7,8 @@ namespace AutoTrackR2;
 public class KillHistoryManager
 {
     private string _killHistoryPath;
-    private readonly string _headers = "KillTime,EnemyPilot,EnemyShip,Enlisted,RecordNumber,OrgAffiliation,Player,Weapon,Ship,Method,Mode,GameVersion,TrackRver,Logged,PFP\n";
-    
+    private readonly string _headers = "KillTime,EnemyPilot,EnemyShip,Enlisted,RecordNumber,OrgAffiliation,Player,Weapon,Ship,Method,Mode,GameVersion,TrackRver,Logged,PFP,Hash\n";
+
     public KillHistoryManager(string logPath)
     {
         _killHistoryPath = logPath;
@@ -89,8 +89,8 @@ public class KillHistoryManager
         
         // Append the new kill data to the CSV file
         var csv = new StringBuilder();
-        csv.AppendLine($"\"{killData.KillTime}\",\"{killData.EnemyPilot}\",\"{killData.EnemyShip}\",\"{killData.Enlisted}\",\"{killData.RecordNumber}\",\"{killData.OrgAffiliation}\",\"{killData.Player}\",\"{killData.Weapon}\",\"{killData.Ship}\",\"{killData.Method}\",\"{killData.Mode}\",\"{killData.GameVersion}\",\"{killData.TrackRver}\",\"{killData.Logged}\",\"{killData.PFP}\"");
-        
+        csv.AppendLine($"\"{killData.KillTime}\",\"{killData.EnemyPilot}\",\"{killData.EnemyShip}\",\"{killData.Enlisted}\",\"{killData.RecordNumber}\",\"{killData.OrgAffiliation}\",\"{killData.Player}\",\"{killData.Weapon}\",\"{killData.Ship}\",\"{killData.Method}\",\"{killData.Mode}\",\"{killData.GameVersion}\",\"{killData.TrackRver}\",\"{killData.Logged}\",\"{killData.PFP}\",\"{killData.Hash}\"");
+
         // Check file can be written to
         try
         {
@@ -138,7 +138,8 @@ public class KillHistoryManager
                 GameVersion = data?[11],
                 TrackRver = data?[12],
                 Logged = data?[13],
-                PFP = data?[14]
+                PFP = data?[14],
+                Hash = data?[15]
             });
         }
 
diff --git a/AutoTrackR2/Util.cs b/AutoTrackR2/Util.cs
index f783330..1ba1ada 100644
--- a/AutoTrackR2/Util.cs
+++ b/AutoTrackR2/Util.cs
@@ -29,4 +29,5 @@ public struct KillData
     public string? TrackRver;
     public string? Logged;
     public string? PFP;
+    public string? Hash;
 }
\ No newline at end of file
diff --git a/AutoTrackR2/WebHandler.cs b/AutoTrackR2/WebHandler.cs
index 0d93b48..244d24b 100644
--- a/AutoTrackR2/WebHandler.cs
+++ b/AutoTrackR2/WebHandler.cs
@@ -6,11 +6,19 @@ using System.Text.RegularExpressions;
 using AutoTrackR2.LogEventHandlers;
 using System.Globalization;
 using System.Security.Cryptography;
+using System.Collections.Generic;
 
 namespace AutoTrackR2;
 
 public static class WebHandler
 {
+    private static HashSet<string> _recordedKillHashes = new HashSet<string>();
+
+    public static bool IsDuplicateKill(string hash)
+    {
+        return _recordedKillHashes.Contains(hash);
+    }
+
     class APIKillData
     {
         public string? victim_ship { get; set; }
@@ -28,7 +36,7 @@ public static class WebHandler
         public string hash { get; set; } = string.Empty;
     }
 
-    private static string GenerateKillHash(string victimName, long timestamp)
+    public static string GenerateKillHash(string victimName, long timestamp)
     {
         // Combine victim name and timestamp
         string combined = $"{victimName}_{timestamp}";
@@ -105,6 +113,15 @@ public static class WebHandler
     public static async Task SubmitKill(KillData killData)
     {
         var timestamp = long.Parse(killData.KillTime!);
+        var hash = GenerateKillHash(killData.EnemyPilot!, timestamp);
+
+        // Check if this kill has already been recorded
+        if (_recordedKillHashes.Contains(hash))
+        {
+            Console.WriteLine("Duplicate kill detected, skipping...");
+            return;
+        }
+
         var apiKillData = new APIKillData
         {
             victim_ship = killData.EnemyShip,
@@ -119,7 +136,7 @@ public static class WebHandler
             trackr_version = killData.TrackRver,
             location = killData.Location,
             time = timestamp,
-            hash = GenerateKillHash(killData.EnemyPilot!, timestamp)
+            hash = hash
         };
 
         if (string.IsNullOrEmpty(apiKillData.rsi))
@@ -175,6 +192,9 @@ public static class WebHandler
             var responseContent = await response.Content.ReadAsStringAsync();
             Console.WriteLine($"Response: {responseContent}");
 
+            // Add the hash to our recorded hashes
+            _recordedKillHashes.Add(hash);
+
             // Only process streamer data if streamlink is enabled
             if (ConfigManager.StreamlinkEnabled == 1)
             {