diff --git a/AutoTrackR2/AutoTrackR2.csproj b/AutoTrackR2/AutoTrackR2.csproj index 875c53d..0ba95b7 100644 --- a/AutoTrackR2/AutoTrackR2.csproj +++ b/AutoTrackR2/AutoTrackR2.csproj @@ -109,6 +109,9 @@ </ItemGroup> <ItemGroup> + <None Update="KillTrackR_MainScript.ps1"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> <None Update="update.ps1"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> diff --git a/AutoTrackR2/ConfigPage.xaml b/AutoTrackR2/ConfigPage.xaml index 937fe68..3b2016f 100644 --- a/AutoTrackR2/ConfigPage.xaml +++ b/AutoTrackR2/ConfigPage.xaml @@ -1,7 +1,7 @@ <UserControl x:Class="AutoTrackR2.ConfigPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - Height="410" Width="626"> + Height="396" Width="626"> <Grid Background="{DynamicResource BackgroundLightBrush}"> <!-- Main Layout Grid --> @@ -23,58 +23,51 @@ <StackPanel Grid.Column="0" VerticalAlignment="Center" Height="389"> <!-- Log File --> <StackPanel Margin="0,10,0,15" Orientation="Horizontal"> - <TextBlock Text="ⓘ" ToolTip="Set this to the Game.log file in your StarCitizen\LIVE directory." Foreground="{DynamicResource TextBrush}" FontSize="20" Margin="0,0,3,5"/> <TextBlock Text="Log File:" Foreground="{DynamicResource TextBrush}" FontSize="16" Margin="0,5,0,5" FontFamily="{StaticResource Roboto}"/> <StackPanel Orientation="Horizontal" Margin="30,0,0,0"> - <TextBox Name="LogFilePath" Width="330" Height="30" Style="{StaticResource RoundedTextBox}"/> + <TextBox Name="LogFilePath" Width="340" Height="30" Style="{StaticResource RoundedTextBox}"/> <Button Content="Browse" Width="75" Height="30" FontFamily="{StaticResource Orbitron}" Margin="5,0" Style="{StaticResource ButtonStyle}" Click="LogFileBrowseButton_Click"/> </StackPanel> </StackPanel> <!-- API URL --> <StackPanel Margin="0,0,0,15" Orientation="Horizontal"> - <TextBlock Text="ⓘ" ToolTip="Need a URL? No idea what to do? Contact heavy_bob on Discord!" Foreground="{DynamicResource TextBrush}" FontSize="20" Margin="0,3,3,5"/> <TextBlock Text="API URL:" Foreground="{DynamicResource TextBrush}" FontSize="16" Margin="0,5,0,5"/> <StackPanel Orientation="Horizontal" Margin="30,0,0,0"> - <TextBox Name="ApiUrl" Width="330" Height="30" Style="{StaticResource RoundedTextBox}"/> + <TextBox Name="ApiUrl" Width="340" Height="30" Style="{StaticResource RoundedTextBox}"/> <Button Content="Test" Width="75" Height="30" FontFamily="{StaticResource Orbitron}" Margin="5,0" Style="{StaticResource ButtonStyle}" Click="TestApiButton_Click"/> </StackPanel> </StackPanel> <!-- API Key --> <StackPanel Margin="0,0,0,15" Orientation="Horizontal"> - <TextBlock Text="ⓘ" ToolTip="Need a key? No idea what to do? Contact heavy_bob on Discord!" Foreground="{DynamicResource TextBrush}" FontSize="20" Margin="0,3,3,5"/> <TextBlock Text="API Key:" Foreground="{DynamicResource TextBrush}" FontSize="16" Margin="0,5,0,5"/> - <TextBox Name="ApiKey" Width="330" Height="30" Margin="33,0,0,0" Style="{StaticResource RoundedTextBox}"/> + <TextBox Name="ApiKey" Width="340" Height="30" Margin="33,0,0,0" Style="{StaticResource RoundedTextBox}"/> </StackPanel> <!-- Video Path --> <StackPanel Margin="0,0,0,15" Orientation="Horizontal"> - <TextBlock Text="ⓘ" ToolTip="The directory where your clipping software saves kills. Check the README." Foreground="{DynamicResource TextBrush}" FontSize="20" Margin="0,3,3,5"/> <TextBlock Text="Video Path:" Foreground="{DynamicResource TextBrush}" FontSize="16" Margin="0,5,0,5"/> <StackPanel Orientation="Horizontal"> - <TextBox Name="VideoPath" Width="330" Height="30" Margin="10,0,0,0" Style="{StaticResource RoundedTextBox}"/> + <TextBox Name="VideoPath" Width="340" Height="30" Margin="10,0,0,0" Style="{StaticResource RoundedTextBox}"/> <Button Content="Browse" Width="75" Height="30" FontFamily="{StaticResource Orbitron}" Margin="5,0" Style="{StaticResource ButtonStyle}" Click="VideoPathBrowseButton_Click"/> </StackPanel> </StackPanel> <!-- Visor Wipe Toggle Slider --> <StackPanel Margin="0,0,0,15" Orientation="Horizontal"> - <TextBlock Text="ⓘ" ToolTip="Perform a Visor Wipe animation on player kill. Requires AHKv2." Foreground="{DynamicResource TextBrush}" FontSize="20" Margin="0,4,3,5"/> <TextBlock Text="Visor Wipe:" Foreground="{DynamicResource TextBrush}" FontSize="16" Margin="0,7,0,5"/> <Slider Name="VisorWipeSlider" Minimum="0" Maximum="1" TickFrequency="1" IsSnapToTickEnabled="True" Value="0" Style="{StaticResource ToggleSliderStyle}" Margin="27,-4,0,0" ValueChanged="VisorWipeSlider_ValueChanged"/> </StackPanel> <!-- Video Record Toggle Slider --> <StackPanel Margin="0,0,0,15" Orientation="Horizontal"> - <TextBlock Text="ⓘ" ToolTip="Automatically clip your last kill. Check the README for more info." Foreground="{DynamicResource TextBrush}" FontSize="20" Margin="0,4,3,5"/> <TextBlock Text="Video Record:" Foreground="{DynamicResource TextBrush}" FontSize="16" Margin="0,7,0,5"/> <Slider Name="VideoRecordSlider" Minimum="0" Maximum="1" TickFrequency="1" IsSnapToTickEnabled="True" Value="0" Style="{StaticResource ToggleSliderStyle}" Margin="10,-4,0,0" ValueChanged="VideoRecordSlider_ValueChanged"/> </StackPanel> <!-- Offline Mode Toggle Slider --> - <StackPanel Margin="0,0,0,10" Orientation="Horizontal"> - <TextBlock Text="ⓘ" ToolTip="With Offline Mode enabled, kills will not be submitted to the configured API." Foreground="{DynamicResource TextBrush}" FontSize="20" Margin="0,4,3,5"/> + <StackPanel Margin="0,0,0,15" Orientation="Horizontal"> <TextBlock Text="Offline Mode:" Foreground="{DynamicResource TextBrush}" FontSize="16" Margin="0,7,0,5"/> <Slider Name="OfflineModeSlider" Minimum="0" Maximum="1" TickFrequency="1" IsSnapToTickEnabled="True" Value="0" Style="{StaticResource ToggleSliderStyle}" Margin="12,-4,0,0" ValueChanged="OfflineModeSlider_ValueChanged"/> </StackPanel> diff --git a/AutoTrackR2/ConfigPage.xaml.cs b/AutoTrackR2/ConfigPage.xaml.cs index f718aed..5e7f366 100644 --- a/AutoTrackR2/ConfigPage.xaml.cs +++ b/AutoTrackR2/ConfigPage.xaml.cs @@ -413,7 +413,8 @@ namespace AutoTrackR2 // Build the dynamic file path for the current user string filePath = Path.Combine( - ConfigManager.AHKScriptFolder, + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "AutoTrackR2", "visorwipe.ahk" ); @@ -507,19 +508,36 @@ namespace AutoTrackR2 private void SaveButton_Click(object sender, RoutedEventArgs e) { - ConfigManager.ApiKey = ApiKey.Text; - ConfigManager.ApiUrl = ApiUrl.Text; - ConfigManager.LogFile = LogFilePath.Text; - ConfigManager.VideoPath = VideoPath.Text; - ConfigManager.VisorWipe = (int)VisorWipeSlider.Value; - ConfigManager.VideoRecord = (int)VideoRecordSlider.Value; - ConfigManager.OfflineMode = (int)OfflineModeSlider.Value; - ConfigManager.Theme = (int)ThemeSlider.Value; - - // Save the current config values - ConfigManager.SaveConfig(); + // Get the directory for the user's local application data + string appDataDirectory = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "AutoTrackR2" + ); + + // Ensure the directory exists + if (!Directory.Exists(appDataDirectory)) + { + Directory.CreateDirectory(appDataDirectory); + } + + // Combine the app data directory with the config file name + string configFilePath = Path.Combine(appDataDirectory, "config.ini"); + + using (StreamWriter writer = new StreamWriter(configFilePath)) + { + writer.WriteLine($"LogFile={LogFilePath.Text}"); + writer.WriteLine($"ApiUrl={ApiUrl.Text}"); + writer.WriteLine($"ApiKey={ApiKey.Text}"); + writer.WriteLine($"VideoPath={VideoPath.Text}"); + writer.WriteLine($"VisorWipe={(int)VisorWipeSlider.Value}"); + writer.WriteLine($"VideoRecord={(int)VideoRecordSlider.Value}"); + writer.WriteLine($"OfflineMode={(int)OfflineModeSlider.Value}"); + writer.WriteLine($"Theme={(int)ThemeSlider.Value}"); // Assumes you are saving the theme slider value (0, 1, or 2) + } + // Start the flashing effect FlashSaveButton(); + ConfigManager.LoadConfig(); } private void FlashSaveButton() diff --git a/AutoTrackR2/HomePage.xaml.cs b/AutoTrackR2/HomePage.xaml.cs index cd264e6..ccd42ab 100644 --- a/AutoTrackR2/HomePage.xaml.cs +++ b/AutoTrackR2/HomePage.xaml.cs @@ -5,31 +5,38 @@ using System.Windows.Media; using System.Windows.Media.Effects; using System.Windows.Documents; using System.Globalization; -using System.IO; -using System.Text; using System.Windows.Media.Imaging; using AutoTrackR2.LogEventHandlers; namespace AutoTrackR2; +public struct PlayerData +{ + public string? PFPURL; + public string? UEERecord; + public string? OrgURL; + public string? OrgName; + public string? JoinDate; +} + public partial class HomePage : UserControl { - private Process runningProcess; // Field to store the running process - private LogHandler? _logHandler; - private KillHistoryManager _killHistoryManager; - private bool _UIEventsRegistered = false; - public HomePage() { InitializeComponent(); - - _killHistoryManager = new KillHistoryManager(ConfigManager.KillHistoryFile); + + // Get the current month + string currentMonth = DateTime.Now.ToString("MMMM", CultureInfo.InvariantCulture); // Set the TextBlock text - KillTallyTitle.Text = $"Kill Tally - {_killHistoryManager.GetKillsInCurrentMonth().Count}"; - AddKillHistoryKillsToUI(); + KillTallyTitle.Text = $"Kill Tally - {currentMonth}"; } - + + private Process runningProcess; // Field to store the running process + private LogHandler _logHandler; + private bool _UIEventsRegistered = false; + + // Update Start/Stop button states based on the isRunning flag public void UpdateButtonState(bool isRunning) { @@ -81,16 +88,6 @@ public partial class HomePage : UserControl // _logHandler = new LogHandler(@"U:\\StarCitizen\\StarCitizen\\LIVE\\Game.log"); _logHandler = new LogHandler(ConfigManager.LogFile); _logHandler.Initialize(); - - } - - private void AddKillHistoryKillsToUI() - { - var kills = _killHistoryManager.GetKills(); - foreach (var kill in kills) - { - Dispatcher.Invoke(() => { AddKillToScreen(kill); }); - } } private void RegisterUIEventHandlers() @@ -137,38 +134,15 @@ public partial class HomePage : UserControl }; // Actor Death - TrackREventDispatcher.ActorDeathEvent += async (actorDeathData) => { - if (actorDeathData.VictimPilot != LocalPlayerData.Username) + TrackREventDispatcher.ActorDeathEvent += async (data) => { + if (data.VictimPilot != LocalPlayerData.Username) { - var playerData = await WebHandler.GetPlayerData(actorDeathData.VictimPilot); + var playerData = await WebHandler.GetPlayerData(data.VictimPilot); if (playerData != null) { - var killData = new KillData - { - EnemyPilot = actorDeathData.VictimPilot, - EnemyShip = actorDeathData.VictimShip, - OrgAffiliation = playerData?.OrgName, - Enlisted = playerData?.JoinDate, - KillTime = DateTime.UtcNow.ToString("dd MMM yyyy HH:mm"), - PFP = playerData?.PFPURL - }; - - // Add kill to UI - Dispatcher.Invoke(() => - { - AddKillToScreen(killData); - }); - - // Only submit kill data if not in offline mode - if (ConfigManager.OfflineMode == 0) - { - await WebHandler.SubmitKill(actorDeathData, playerData); - } - - _killHistoryManager.AddKill(killData); - VisorWipe(); - VideoRecord(); + Dispatcher.Invoke(() => { AddKillToScreen(data, playerData); }); + await WebHandler.SubmitKill(data, playerData); } } }; @@ -181,7 +155,7 @@ public partial class HomePage : UserControl _UIEventsRegistered = true; } - private void AddKillToScreen(KillData killData) + private void AddKillToScreen(ActorDeathData deathData, PlayerData? playerData) { // Fetch the dynamic resource for AltTextColor var altTextColorBrush = new SolidColorBrush((Color)Application.Current.Resources["AltTextColor"]); @@ -207,7 +181,7 @@ public partial class HomePage : UserControl Foreground = altTextColorBrush, FontFamily = orbitronFontFamily, }); - killTextBlock.Inlines.Add(new Run($"{killData.EnemyPilot}\n")); + killTextBlock.Inlines.Add(new Run($"{deathData.VictimPilot}\n")); // Repeat for other lines killTextBlock.Inlines.Add(new Run("Victim Ship: ") @@ -215,21 +189,21 @@ public partial class HomePage : UserControl Foreground = altTextColorBrush, FontFamily = orbitronFontFamily, }); - killTextBlock.Inlines.Add(new Run($"{killData.EnemyShip}\n")); + killTextBlock.Inlines.Add(new Run($"{deathData.VictimShip}\n")); killTextBlock.Inlines.Add(new Run("Victim Org: ") { Foreground = altTextColorBrush, FontFamily = orbitronFontFamily, }); - killTextBlock.Inlines.Add(new Run($"{killData.OrgAffiliation}\n")); + killTextBlock.Inlines.Add(new Run($"{playerData?.OrgName}\n")); killTextBlock.Inlines.Add(new Run("Join Date: ") { Foreground = altTextColorBrush, FontFamily = orbitronFontFamily, }); - killTextBlock.Inlines.Add(new Run($"{killData.Enlisted}\n")); + killTextBlock.Inlines.Add(new Run($"{playerData?.JoinDate}\n")); killTextBlock.Inlines.Add(new Run("UEE Record: ") { @@ -237,12 +211,16 @@ public partial class HomePage : UserControl FontFamily = orbitronFontFamily, }); + + const string dateFormatString = "dd MMM yyyy HH:mm"; + var currentTime = DateTime.UtcNow.ToString(dateFormatString); + killTextBlock.Inlines.Add(new Run("Kill Time: ") { Foreground = altTextColorBrush, FontFamily = orbitronFontFamily, }); - killTextBlock.Inlines.Add(new Run($"{killData.KillTime}")); + killTextBlock.Inlines.Add(new Run($"{currentTime}")); // Create a Border and apply the RoundedTextBlockWithBorder style var killBorder = new Border @@ -268,7 +246,7 @@ public partial class HomePage : UserControl // Create the Image for the profile var profileImage = new Image { - Source = new BitmapImage(new Uri(killData.PFP)), // Assuming the 8th part contains the profile image URL + Source = new BitmapImage(new Uri(playerData?.PFPURL)), // Assuming the 8th part contains the profile image URL Width = 90, Height = 90, Stretch = Stretch.Fill, // Adjust how the image fits @@ -301,15 +279,15 @@ public partial class HomePage : UserControl public void StopButton_Click(object sender, RoutedEventArgs e) { - _logHandler?.Stop(); + _logHandler.Stop(); // Clear the text boxes - // System.Threading.Thread.Sleep(200); - // PilotNameTextBox.Text = string.Empty; - // PlayerShipTextBox.Text = string.Empty; - // GameModeTextBox.Text = string.Empty; - // KillTallyTextBox.Text = string.Empty; - // KillFeedStackPanel.Children.Clear(); + System.Threading.Thread.Sleep(200); + PilotNameTextBox.Text = string.Empty; + PlayerShipTextBox.Text = string.Empty; + GameModeTextBox.Text = string.Empty; + KillTallyTextBox.Text = string.Empty; + KillFeedStackPanel.Children.Clear(); } private void AdjustFontSize(TextBlock textBlock) @@ -350,39 +328,4 @@ public partial class HomePage : UserControl // Apply the adjusted font size textBlock.FontSize = fontSize; } - - public static void RunAHKScript(string path) - { - string scriptPath = Path.Combine(ConfigManager.AHKScriptFolder, path); - - if (!File.Exists(scriptPath)) - { - return; - } - - // Run the script using powershell - using var ahkProcess = new Process(); - - // Runs the script via Explorer, ensuring it uses whatever the - // default binary for AHK is. Skips having to find a specific path to AHK - ahkProcess.StartInfo.FileName = "explorer"; - ahkProcess.StartInfo.Arguments = "\"" + scriptPath + "\""; - ahkProcess.Start(); - } - - private void VisorWipe() - { - if (ConfigManager.VisorWipe == 1) - { - RunAHKScript(ConfigManager.VisorWipeScript); - } - } - - private void VideoRecord() - { - if (ConfigManager.VideoRecord == 1) - { - RunAHKScript(ConfigManager.VideoRecordScript); - } - } } diff --git a/AutoTrackR2/KillHistoryManager.cs b/AutoTrackR2/KillHistoryManager.cs deleted file mode 100644 index fd07f2e..0000000 --- a/AutoTrackR2/KillHistoryManager.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Globalization; -using System.IO; -using System.Text; - -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"; - - public KillHistoryManager(string logPath) - { - _killHistoryPath = logPath; - - if (!File.Exists(_killHistoryPath)) - { - File.WriteAllText(_killHistoryPath, _headers); - } - } - - public void AddKill(KillData killData) - { - // Ensure the CSV file exists - // This should only happen if the file was deleted or corrupted - if (!File.Exists(_killHistoryPath)) - { - File.WriteAllText(_killHistoryPath, _headers); - } - - // 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}\""); - File.AppendAllText(_killHistoryPath, csv.ToString()); - } - - public List<KillData> GetKills() - { - var kills = new List<KillData>(); - - using var reader = new StreamReader(_killHistoryPath); - reader.ReadLine(); // Skip headers - - while (reader.Peek() >= 0) - { - var line = reader.ReadLine(); - - // Remove extra quotes from CSV data - // Todo: These quotes are for handling commas in the data, but not sure if they're necessary - line = line?.Replace("\"", string.Empty); - - var data = line?.Split(','); - - kills.Add(new KillData - { - KillTime = data?[0], - EnemyPilot = data?[1], - EnemyShip = data?[2], - Enlisted = data?[3], - RecordNumber = data?[4], - OrgAffiliation = data?[5], - Player = data?[6], - Weapon = data?[7], - Ship = data?[8], - Method = data?[9], - Mode = data?[10], - GameVersion = data?[11], - TrackRver = data?[12], - Logged = data?[13], - PFP = data?[14] - }); - } - - return kills; - } - - public List<KillData> GetKillsInCurrentMonth() - { - string currentMonth = DateTime.Now.ToString("MMM", CultureInfo.InvariantCulture); - var kills = GetKills(); - return kills.Where(kill => kill.KillTime?.Contains(currentMonth) == true).ToList(); - } -} \ No newline at end of file diff --git a/AutoTrackR2/KillTrackR_MainScript.ps1 b/AutoTrackR2/KillTrackR_MainScript.ps1 new file mode 100644 index 0000000..26f0ed2 --- /dev/null +++ b/AutoTrackR2/KillTrackR_MainScript.ps1 @@ -0,0 +1,516 @@ +$TrackRver = "2.07" + +# Path to the config file +$appName = "AutoTrackR2" +$scriptFolder = Join-Path -Path $env:LOCALAPPDATA -ChildPath $appName +$configFile = Join-Path -Path $scriptFolder -ChildPath "config.ini" + +# Read the config file into a hashtable +if (Test-Path $configFile) { + Write-Output "PlayerName=Config.ini found." + $configContent = Get-Content $configFile | Where-Object { $_ -notmatch '^#|^\s*$' } + + # Escape backslashes by doubling them + $configContent = $configContent -replace '\\', '\\\\' + + # Convert to key-value pairs + $config = $configContent -replace '^([^=]+)=(.+)$', '$1=$2' | ConvertFrom-StringData +} else { + Write-Output "Config.ini not found." + exit +} + +$parentApp = (Get-Process -Name AutoTrackR2).ID + +# Access config values +$logFilePath = $config.Logfile +$apiUrl = $config.ApiUrl +$apiKey = $config.ApiKey +$videoPath = $config.VideoPath +$visorWipe = $config.VisorWipe +$videoRecord = $config.VideoRecord +$offlineMode = $config.OfflineMode + +if ($offlineMode -eq 1){ + $offlineMode = $true +} else { + $offlineMode = $false +} +Write-Output "PlayerName=OfflineMode: $offlineMode" + +if ($videoRecord -eq 1){ + $videoRecord = $true +} else { + $videoRecord = $false +} +Write-Output "PlayerName=VideoRecord: $videoRecord" + +if ($visorWipe -eq 1){ + $visorWipe = $true +} else { + $visorWipe = $false +} +Write-Output "PlayerName=VisorWipe: $visorWipe" + +If (Test-Path $logFilePath) { + Write-Output "PlayerName=Logfile found" +} else { + Write-Output "Logfile not found." +} + +If ($null -ne $apiUrl){ + if ($apiUrl -notlike "*/register-kill") { + $apiUrl = $apiUrl.TrimEnd("/") + "/register-kill" + } + Write-output "PlayerName=$apiURL" +} + +# Ship Manufacturers +$prefixes = @( + "ORIG", + "CRUS", + "RSI", + "AEGS", + "VNCL", + "DRAK", + "ANVL", + "BANU", + "MISC", + "CNOU", + "XIAN", + "GAMA", + "TMBL", + "ESPR", + "KRIG", + "GRIN", + "XNAA", + "MRAI" +) + +# Define the regex pattern to extract information +$killPattern = "<Actor Death> CActor::Kill: '(?<EnemyPilot>[^']+)' \[\d+\] in zone '(?<EnemyShip>[^']+)' killed by '(?<Player>[^']+)' \[[^']+\] using '(?<Weapon>[^']+)' \[Class (?<Class>[^\]]+)\] with damage type '(?<DamageType>[^']+)'" +$puPattern = '<\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z> \[Notice\] <ContextEstablisherTaskFinished> establisher="CReplicationModel" message="CET completed" taskname="StopLoadingScreen" state=[^\s()]+\(\d+\) status="Finished" runningTime=\d+\.\d+ numRuns=\d+ map="megamap" gamerules="SC_Default" sessionId="[a-f0-9\-]+" \[Team_Network\]\[Network\]\[Replication\]\[Loading\]\[Persistence\]' +$acPattern = "Requesting Mode Change" # "ArenaCommanderFeature" +$loadoutPattern = '\[InstancedInterior\] OnEntityLeaveZone - InstancedInterior \[(?<InstancedInterior>[^\]]+)\] \[\d+\] -> Entity \[(?<Entity>[^\]]+)\] \[\d+\] -- m_openDoors\[\d+\], m_managerGEID\[(?<ManagerGEID>\d+)\], m_ownerGEID\[(?<OwnerGEID>[^\[]+)\]' +$shipManPattern = "^(" + ($prefixes -join "|") + ")" +# $loginPattern = "\[Notice\] <AccountLoginCharacterStatus_Character> Character: createdAt [A-Za-z0-9]+ - updatedAt [A-Za-z0-9]+ - geid [A-Za-z0-9]+ - accountId [A-Za-z0-9]+ - name (?<Player>[A-Za-z0-9_-]+) - state STATE_CURRENT" # KEEP THIS INCASE LEGACY LOGIN IS REMOVED +$loginPattern = "\[Notice\] <Legacy login response> \[CIG-net\] User Login Success - Handle\[(?<Player>[A-Za-z0-9_-]+)\]" +$cleanupPattern = '^(.+?)_\d+$' +$versionPattern = "--system-trace-env-id='pub-sc-alpha-(?<gameversion>\d{3,4}-\d{7})'" +$vehiclePattern = "<(?<timestamp>[^>]+)> \[Notice\] <Vehicle Destruction> CVehicle::OnAdvanceDestroyLevel: " + + "Vehicle '(?<vehicle>[^']+)' \[\d+\] in zone '(?<vehicle_zone>[^']+)' " + + "\[pos x: (?<pos_x>[-\d\.]+), y: (?<pos_y>[-\d\.]+), z: (?<pos_z>[-\d\.]+) " + + "vel x: [^,]+, y: [^,]+, z: [^\]]+\] driven by '(?<driver>[^']+)' \[\d+\] " + + "advanced from destroy level (?<destroy_level_from>\d+) to (?<destroy_level_to>\d+) " + + "caused by '(?<caused_by>[^']+)' \[\d+\] with '(?<damage_type>[^']+)'" + +# Lookup Patterns +$joinDatePattern = '<span class="label">Enlisted</span>\s*<strong class="value">([^<]+)</strong>' +$ueePattern = '<p class="entry citizen-record">\s*<span class="label">UEE Citizen Record<\/span>\s*<strong class="value">#?(n\/a|\d+)<\/strong>\s*<\/p>' + +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$process = Get-Process | Where-Object {$_.Name -like "AutoTrackR2"} +$global:killTally = 0 + +# Load historic kills from csv +if (Test-Path "$scriptFolder\Kill-Log.csv") { + $historicKills = Import-CSV "$scriptFolder\Kill-log.csv" + $currentDate = Get-Date + $dateFormat = "dd MMM yyyy HH:mm UTC" + foreach ($kill in $historicKills) { + $killDate = [datetime]::ParseExact($kill.KillTime.Trim(), $dateFormat, [System.Globalization.CultureInfo]::InvariantCulture) + If ($killdate.year -eq $currentDate.Year -and $killdate.month -eq $currentDate.Month) { + $global:killTally++ + } + Try { + Write-Output "NewKill=throwaway,$($kill.EnemyPilot),$($kill.EnemyShip),$($kill.OrgAffiliation),$($kill.Enlisted),$($kill.RecordNumber),$($kill.KillTime), $($kill.PFP)" + } Catch { + Write-Output "Error Loading Kill: $($kill.EnemyPilot)" + } + } +} +Write-Output "KillTally=$global:killTally" + +# Match and extract username from gamelog +Do { + # Load gamelog into memory + $authLog = Get-Content -Path $logFilePath + + # Initialize variable to store username + $global:userName = $null + $global:loadout = "Player" + + # Loop through each line in the log to find the matching line + foreach ($line in $authLog) { + if ($line -match $loginPattern) { + $global:userName = $matches['Player'] + Write-Output "PlayerName=$global:userName" + } + # Get Loadout + if ($line -match $loadoutPattern) { + $entity = $matches['Entity'] + $ownerGEID = $matches['OwnerGEID'] + + If ($ownerGEID -eq $global:userName -and $entity -match $shipManPattern) { + $tryloadOut = $entity + If ($tryloadOut -match $cleanupPattern){ + if ($null -ne $matches[1]){ + $global:loadOut = $matches[1] + } + } + } + } + Write-Output "PlayerShip=$global:loadOut" + + If ($line -match $versionPattern){ + $global:GameVersion = $matches['gameversion'] + } + if ($line -match $acPattern){ + $global:GameMode = "AC" + } + if ($line -match $puPattern){ + $global:GameMode = "PU" + } + Write-Output "GameMode=$global:GameMode" + + } + # If no match found, print "Logged In: False" + if (-not $global:userName) { + Write-Output "PlayerName=No Player Found..." + Start-Sleep -Seconds 30 + } + + # Clear the log from memory + $authLog = $null +} until ($null -ne $global:userName) + +# Function to process new log entries and write to the host +function Read-LogEntry { + param ( + [string]$line + ) + + # Look for vehicle events + if ($line -match $vehiclePattern) { + # Access the named capture groups from the regex match + $global:vehicle_id = $matches['vehicle'] + $global:location = $matches['vehicle_zone'] + + } + + # Apply the regex pattern to the line + if ($line -match $killPattern) { + # Access the named capture groups from the regex match + $enemyPilot = $matches['EnemyPilot'] + $enemyShip = $matches['EnemyShip'] + $player = $matches['Player'] + $weapon = $matches['Weapon'] + $damageType = $matches['DamageType'] + $ship = $global:loadOut + + If ($enemyShip -ne "vehicle_id"){ + + $global:got_location = $location + } + else + { + $global:got_location = "NONE" + } + + Try { + $page1 = Invoke-WebRequest -uri "https://robertsspaceindustries.com/citizens/$enemyPilot" + } Catch { + $page1 = $null + } + + If ($null -ne $page1){ + # Check if the Autotrackr2 process is running + if ($null -eq (Get-Process -ID $parentApp -ErrorAction SilentlyContinue)) { + Stop-Process -Id $PID -Force + } + If ($enemyShip -ne "Player"){ + If ($enemyShip -eq $global:lastKill){ + $enemyShip = "Passenger" + } Else { + $global:lastKill = $enemyShip + } + } + + If ($player -eq $global:userName -and $enemyPilot -ne $global:userName){ + If ($enemyShip -match $cleanupPattern){ + $enemyShip = $matches[1] + } + If ($weapon -match $cleanupPattern){ + $weapon = $matches[1] + } + If ($weapon -eq "KLWE_MassDriver_S10"){ + $global:loadOut = "AEGS_Idris" + $ship = "AEGS_Idris" + } + if ($damageType -eq "Bullet" -or $weapon -like "apar_special_ballistic*") { + $ship = "Player" + $enemyShip = "Player" + } + If ($ship -match $cleanupPattern){ + $ship = $matches[1] + } + if ($ship -notmatch $shipManPattern){ + $ship = "Player" + } + If ($enemyShip -notmatch $shipManPattern -and $enemyShip -notlike "Passenger" ) { + $enemyShip = "Player" + } + + # Repeatedly remove all suffixes + while ($enemyShip -match '_(PU|AI|CIV|MIL|PIR)$') { + $enemyShip = $enemyShip -replace '_(PU|AI|CIV|MIL|PIR)$', '' + } + # Repeatedly remove all suffixes + while ($ship -match '_(PU|AI|CIV|MIL|PIR)$') { + $ship = $ship -replace '_(PU|AI|CIV|MIL|PIR)$', '' + } + while ($enemyShip -match '-00(1|2|3|4|5|6|7|8|9|0)$') { + $enemyShip = $enemyShip -replace '-00(1|2|3|4|5|6|7|8|9|0)$', '' + }while ($ship -match '-00(1|2|3|4|5|6|7|8|9|0)$') { + $ship = $ship -replace '-00(1|2|3|4|5|6|7|8|9|0)$', '' + } + + $KillTime = (Get-Date).ToUniversalTime().ToString("dd MMM yyyy HH:mm 'UTC'", [System.Globalization.CultureInfo]::InvariantCulture) + + # Get Enlisted Date + if ($($page1.content) -match $joinDatePattern) { + $joinDate = $matches[1] + $joinDate2 = $joinDate -replace ',', '' + } else { + $joinDate2 = "-" + } + + # Check if there are any matches + If ($null -eq $page1.links[0].innerHTML) { + $enemyOrgs = $page1.links[4].innerHTML + } Else { + $enemyOrgs = $page1.links[3].innerHTML + } + + if ($null -eq $enemyOrgs) { + $enemyOrgs = "-" + } + + # Get UEE Number + if ($($page1.content) -match $ueePattern) { + # The matched UEE Citizen Record number is in $matches[1] + $citizenRecord = $matches[1] + } else { + $citizenRecord = "n/a" + } + If ($citizenRecord -eq "n/a") { + $citizenRecordAPI = "-1" + $citizenRecord = "-" + } Else { + $citizenRecordAPI = $citizenRecord + } + + # Get PFP + if ($page1.images[0].src -like "/media/*") { + $victimPFP = "https://robertsspaceindustries.com$($page1.images[0].src)" + } Else { + $victimPFP = "https://cdn.robertsspaceindustries.com/static/images/account/avatar_default_big.jpg" + } + + $global:killTally++ + Write-Output "KillTally=$global:killTally" + Write-Output "NewKill=throwaway,$enemyPilot,$enemyShip,$enemyOrgs,$joinDate2,$citizenRecord,$killTime,$victimPFP" + + $global:GameMode = $global:GameMode.ToLower() + # Send to API + # Define the data to send + If ($null -ne $apiUrl -and $offlineMode -eq $false){ + $data = @{ + victim_ship = $enemyShip + victim = $enemyPilot + enlisted = $joinDate + rsi = $citizenRecordAPI + weapon = $weapon + method = $damageType + loadout_ship = $ship + game_version = $global:GameVersion + gamemode = $global:GameMode + trackr_version = $TrackRver + location = $got_location + } + + # Headers which may or may not be necessary + $headers = @{ + "Authorization" = "Bearer $apiKey" + "Content-Type" = "application/json" + "User-Agent" = "AutoTrackR2" + } + + try { + # Send the POST request with JSON data + $null = Invoke-RestMethod -Uri $apiURL -Method Post -Body ($data | ConvertTo-Json -Depth 5) -Headers $headers + $logMode = "API" + $global:got_location = "NONE" + } catch { + # Catch and display errors + $apiError = $_ + # Add to output file + $logMode = "Err-Local" + } + } Else { + $logMode = "Local" + } + + # Define the output CSV path + $csvPath = "$scriptFolder\Kill-log.csv" + + # Create an object to hold the data + $killData = [PSCustomObject]@{ + KillTime = $killTime + EnemyPilot = $enemyPilot + EnemyShip = $enemyShip + Enlisted = $joinDate2 + RecordNumber = $citizenRecord + OrgAffiliation = $enemyOrgs + Player = $player + Weapon = $weapon + Ship = $ship + Method = $damageType + Mode = $global:GameMode + GameVersion = $global:GameVersion + TrackRver = $TrackRver + Logged = $logMode + PFP = $victimPFP + } + + # Remove commas from all properties + foreach ($property in $killData.PSObject.Properties) { + if ($property.Value -is [string]) { + $property.Value = $property.Value -replace ',', '' + } + } + + # Export to CSV + if (-Not (Test-Path $csvPath)) { + # If file doesn't exist, create it with headers + $killData | Export-Csv -Path $csvPath -NoTypeInformation + } else { + # Append data to the existing file without adding headers + $killData | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1 | Out-File -Append -Encoding utf8 -FilePath $csvPath + } + + $sleeptimer = 10 + + # VisorWipe + If ($visorwipe -eq $true -and $enemyShip -ne "Passenger" -and $damageType -notlike "*Bullet*"){ + # send keybind for visorwipe + start-sleep 1 + $sleeptimer = $sleeptimer -1 + &"$scriptFolder\visorwipe.ahk" + } + + # Record video + if ($videoRecord -eq $true -and $enemyShip -ne "Passenger"){ + # send keybind for windows game bar recording + Start-Sleep 2 + $sleeptimer = $sleeptimer -9 + &"$scriptFolder\videorecord.ahk" + Start-Sleep 7 + + $latestFile = Get-ChildItem -Path $videoPath | Where-Object { -not $_.PSIsContainer } | Sort-Object CreationTime -Descending | Select-Object -First 1 + # Check if the latest file is no more than 30 seconds old + if ($latestFile) { + $fileAgeInSeconds = (New-TimeSpan -Start $latestFile.CreationTime -End (Get-Date)).TotalSeconds + if ($fileAgeInSeconds -le 30) { + # Generate a timestamp in ddMMMyyyy-HH:mm format + $timestamp = (Get-Date).ToString("ddMMMyyyy-HHmm") + + # Extract the file extension to preserve it + $fileExtension = $latestFile.Extension + + # Rename the file, preserving the original file extension + Rename-Item -Path $latestFile.FullName -NewName "$enemyPilot.$enemyShip.$timestamp$fileExtension" + } else {} + } else {} + } + Start-Sleep $sleeptimer + } + } + } + + # Get Logged-in User + If ($line -match $loginPattern) { + # Load gamelog into memory + $authLog = Get-Content -Path $logFilePath + $authLog = $authlog -match $loginPattern + $authLog = $authLog | Out-String + + # Extract User Name + $nameExtract = "name\s+(?<PlayerName>[^\s-]+)" + + If ($authLog -match $nameExtract -and $global:userName -ne $nameExtract){ + $global:userName = $matches['PlayerName'] + Write-Output "PlayerName=$global:userName" + } + } + + # Detect PU or AC + if ($line -match $puPattern) { + $global:GameMode = "PU" + Write-Output "GameMode=$global:GameMode" + } + if ($line -match $acPattern) { + $global:GameMode = "AC" + Write-Output "GameMode=$global:GameMode" + } + + #Set loadout + if ($line -match $loadoutPattern) { + $entity = $matches['Entity'] + $ownerGEID = $matches['OwnerGEID'] + + If ($ownerGEID -eq $global:userName -and $entity -match $shipManPattern) { + $tryloadOut = $entity + If ($tryloadOut -match $cleanupPattern){ + $global:loadOut = $matches[1] + } + Write-Output "PlayerShip=$global:loadOut" + } + } +} + + +# Monitor the log file and process new lines as they are added +Get-Content -Path $logFilePath -Wait -Tail 0 | ForEach-Object { + Read-LogEntry $_ +} + +<# +# Open the log file with shared access for reading and writing +$fileStream = [System.IO.FileStream]::new($logFilePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) +$reader = [System.IO.StreamReader]::new($fileStream, [System.Text.Encoding]::UTF8) # Ensure we're reading as UTF-8 + +try { + # Move to the end of the file to start monitoring new entries + $reader.BaseStream.Seek(0, [System.IO.SeekOrigin]::End) + + while ($true) { + # Read the next line from the file + $line = $reader.ReadLine() + + # Ensure we have new content to process + if ($line) { + # Process the line (this is where your log entry handler would go) + Read-LogEntry $line + } + + # Sleep for a brief moment to avoid high CPU usage + Start-Sleep -Milliseconds 100 + } +} +finally { + # Ensure we close the reader and file stream properly when done + $reader.Close() + $fileStream.Close() +} +#> diff --git a/AutoTrackR2/MainWindow.xaml.cs b/AutoTrackR2/MainWindow.xaml.cs index 637e90f..e432e97 100644 --- a/AutoTrackR2/MainWindow.xaml.cs +++ b/AutoTrackR2/MainWindow.xaml.cs @@ -1,6 +1,4 @@ //using System.Collections.Generic; - -using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -35,6 +33,9 @@ namespace AutoTrackR2 { InitializeComponent(); + // Load configuration settings before setting them in any page + ConfigManager.LoadConfig(); + homePage = new HomePage(); // Create a single instance of HomePage ContentControl.Content = homePage; // Default to HomePage @@ -194,13 +195,6 @@ namespace AutoTrackR2 public static class ConfigManager { public static string LogFile { get; set; } - public static string KillHistoryFile { get; set; } - - public static string AHKScriptFolder { get; set; } - - public static string VisorWipeScript { get; set; } - public static string VideoRecordScript { get; set; } - public static string ApiUrl { get; set; } public static string ApiKey { get; set; } public static string VideoPath { get; set; } @@ -208,27 +202,6 @@ namespace AutoTrackR2 public static int VideoRecord { get; set; } public static int OfflineMode { get; set; } public static int Theme { get; set; } - - static ConfigManager() - { - LoadConfig(); - - // Set default values - // AppData\Local\AutoTrackR2\Kill-log.csv - KillHistoryFile = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "AutoTrackR2", - "Kill-log.csv" - ); - - AHKScriptFolder = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "AutoTrackR2" - ); - - VisorWipeScript = "visorwipe.ahk"; - VideoRecordScript = "videorecord.ahk"; - } public static void LoadConfig() { @@ -268,7 +241,7 @@ namespace AutoTrackR2 // Define the config file path in a writable location string configDirectory = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "AutoTrackR2" + "YourAppName" ); // Ensure the directory exists diff --git a/AutoTrackR2/UpdatePage.xaml.cs b/AutoTrackR2/UpdatePage.xaml.cs index 87b260e..75d9e70 100644 --- a/AutoTrackR2/UpdatePage.xaml.cs +++ b/AutoTrackR2/UpdatePage.xaml.cs @@ -9,7 +9,7 @@ namespace AutoTrackR2 { public partial class UpdatePage : UserControl { - public static string currentVersion = "v2.09"; + public static string currentVersion = "v2.08"; private string latestVersion; public UpdatePage() diff --git a/AutoTrackR2/Util.cs b/AutoTrackR2/Util.cs deleted file mode 100644 index b5931de..0000000 --- a/AutoTrackR2/Util.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace AutoTrackR2; - -// Data returned from the CIG API -public struct PlayerData -{ - public string? PFPURL; - public string? UEERecord; - public string? OrgURL; - public string? OrgName; - public string? JoinDate; -} - -// Amalgamation of all data from a single kill -public struct KillData -{ - public string? KillTime; - public string? EnemyPilot; - public string? EnemyShip; - public string? Enlisted; - public string? RecordNumber; - public string? OrgAffiliation; - public string? Player; - public string? Weapon; - public string? Ship; - public string? Method; - public string? Mode; - public string? GameVersion; - public string? TrackRver; - public string? Logged; - public string? PFP; -} \ No newline at end of file