mirror of
https://github.com/BubbaGumpShrump/AutoTrackR2.git
synced 2025-06-28 07:36:39 +00:00
Added process Log Back and Import CSV
Still working on fixing the malformed csv issue but we're getting really close. Process Log Back will re-import all kills that were not recorded to the api. This was added so that any kills that were missed during downtime can be re-submitted to the api.
This commit is contained in:
parent
316911ba7d
commit
0f97d758f8
7 changed files with 413 additions and 227 deletions
|
@ -52,115 +52,115 @@
|
|||
Margin="0,0,0,10"
|
||||
VerticalAlignment="Top">
|
||||
<StackPanel
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Center"
|
||||
Width="152"
|
||||
Margin="10,5,10,5">
|
||||
<TextBlock Name="PilotNameTitle"
|
||||
Text="Pilot"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="PilotNameTextBox"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBlock Name="PlayerShipTitle"
|
||||
Text="Ship"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="PlayerShipTextBox"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBlock Name="GameModeTitle"
|
||||
Text="Game Mode"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="GameModeTextBox"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBlock Name="LocationTitle"
|
||||
Text="Location"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="LocationTextBox"
|
||||
Text="Unknown"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBlock Name="KillTallyTitle"
|
||||
Text="Kill Tally"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="KillTallyTextBox"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBox x:Name="DebugPanel"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="98"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="8"
|
||||
BorderThickness="0"
|
||||
Margin="0,9,0,0"/>
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Center"
|
||||
Width="152"
|
||||
Margin="10,5,10,5">
|
||||
<TextBlock Name="PilotNameTitle"
|
||||
Text="Pilot"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="PilotNameTextBox"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBlock Name="PlayerShipTitle"
|
||||
Text="Ship"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="PlayerShipTextBox"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBlock Name="GameModeTitle"
|
||||
Text="Game Mode"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="GameModeTextBox"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBlock Name="LocationTitle"
|
||||
Text="Location"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="LocationTextBox"
|
||||
Text="Unknown"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBlock Name="KillTallyTitle"
|
||||
Text="Kill Tally"
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource AltTextBrush}"
|
||||
FontSize="14"/>
|
||||
<TextBlock Name="KillTallyTextBox"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="20"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Margin="0,0,0,0"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="10"
|
||||
TextAlignment="Center"/>
|
||||
<TextBox x:Name="DebugPanel"
|
||||
Text=""
|
||||
Width="152"
|
||||
Height="98"
|
||||
Background="Transparent"
|
||||
FontFamily="{StaticResource Orbitron}"
|
||||
Foreground="{DynamicResource TextBrush}"
|
||||
FontSize="8"
|
||||
BorderThickness="0"
|
||||
Margin="0,9,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<StackPanel Grid.Row="1"
|
||||
|
|
|
@ -21,14 +21,14 @@ public partial class HomePage : UserControl
|
|||
|
||||
private LogHandler? _logHandler;
|
||||
private KillHistoryManager _killHistoryManager;
|
||||
private LogBackupProcessor? _logBackupProcessor;
|
||||
private bool _UIEventsRegistered = false;
|
||||
private System.Timers.Timer _statusCheckTimer;
|
||||
private bool _isLogHandlerRunning = false;
|
||||
private int _counter = 1;
|
||||
private System.Timers.Timer _counterTimer;
|
||||
private bool _isInitializing = false;
|
||||
private System.Timers.Timer? _initializationTimer;
|
||||
private bool _wasStarCitizenRunningOnStart = false;
|
||||
private bool _isProcessingLogBackups = false;
|
||||
|
||||
public HomePage()
|
||||
{
|
||||
|
@ -62,15 +62,16 @@ public partial class HomePage : UserControl
|
|||
_statusCheckTimer = new System.Timers.Timer(1000); // Check every second
|
||||
_statusCheckTimer.Elapsed += CheckStarCitizenStatus;
|
||||
_statusCheckTimer.Start();
|
||||
|
||||
// Initialize and start the counter timer
|
||||
_counterTimer = new System.Timers.Timer(1000); // Update every second
|
||||
_counterTimer.Elapsed += UpdateCounter;
|
||||
_counterTimer.Start();
|
||||
}
|
||||
|
||||
private void CheckStarCitizenStatus(object? sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (_isProcessingLogBackups)
|
||||
{
|
||||
// Simulate TrackR as running during log backup processing
|
||||
Dispatcher.Invoke(() => UpdateStatusIndicator(true));
|
||||
return;
|
||||
}
|
||||
bool isRunning = IsStarCitizenRunning();
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
|
@ -569,12 +570,40 @@ public partial class HomePage : UserControl
|
|||
return Process.GetProcessesByName("StarCitizen").Length > 0;
|
||||
}
|
||||
|
||||
private void UpdateCounter(object? sender, ElapsedEventArgs e)
|
||||
public async void ProcessLogBackups_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
if (_isProcessingLogBackups)
|
||||
{
|
||||
DebugPanel.Text = _counter.ToString();
|
||||
_counter = (_counter % 10) + 1; // Count from 1 to 10 and loop
|
||||
});
|
||||
MessageBox.Show("Already processing log backups. Please wait.", "Processing", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_isProcessingLogBackups = true;
|
||||
UpdateStatusIndicator(true, true); // Set to yellow for processing
|
||||
|
||||
if (_logBackupProcessor == null)
|
||||
{
|
||||
var logBackupsPath = Path.Combine(Path.GetDirectoryName(ConfigManager.LogFile)!, "logbackups");
|
||||
_logBackupProcessor = new LogBackupProcessor(logBackupsPath, _killHistoryManager, _logHandler?.GetEventHandlers() ?? new List<ILogEventHandler>());
|
||||
}
|
||||
|
||||
await _logBackupProcessor.ProcessLogBackupsAsync((logFile) =>
|
||||
{
|
||||
DebugPanel.Text = $"Processing: {Path.GetFileName(logFile)}";
|
||||
});
|
||||
MessageBox.Show("Log backups processed successfully!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
DebugPanel.Text = ""; // Clear the debug panel after successful processing
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error processing log backups: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isProcessingLogBackups = false;
|
||||
UpdateStatusIndicator(IsStarCitizenRunning());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,117 +2,132 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AutoTrackR2;
|
||||
|
||||
public class KillHistoryManager
|
||||
{
|
||||
private string _killHistoryPath;
|
||||
private readonly string _killHistoryPath;
|
||||
private readonly string _headers = "KillTime,EnemyPilot,EnemyShip,Enlisted,RecordNumber,OrgAffiliation,Player,Weapon,Ship,Method,Mode,GameVersion,TrackRver,Logged,PFP,Hash\n";
|
||||
private readonly KillStreakManager _killStreakManager;
|
||||
private readonly ConcurrentQueue<KillData> _killQueue;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
private readonly Task _processingTask;
|
||||
private bool _killStreakSoundEnabled = true;
|
||||
|
||||
public KillHistoryManager(string logPath, string soundsPath)
|
||||
{
|
||||
_killHistoryPath = logPath;
|
||||
_killStreakManager = new KillStreakManager(soundsPath);
|
||||
var appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AutoTrackR2");
|
||||
Directory.CreateDirectory(appDataPath); // Ensure the directory exists
|
||||
_killHistoryPath = Path.Combine(appDataPath, "Kill-log.csv");
|
||||
|
||||
// Create the CSV file with headers if it doesn't exist
|
||||
if (!File.Exists(_killHistoryPath))
|
||||
{
|
||||
File.WriteAllText(_killHistoryPath, _headers);
|
||||
}
|
||||
else
|
||||
|
||||
_killStreakManager = new KillStreakManager(soundsPath);
|
||||
_killQueue = new ConcurrentQueue<KillData>();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// Start the background processing task
|
||||
_processingTask = Task.Run(ProcessKillQueue);
|
||||
}
|
||||
|
||||
private async Task ProcessKillQueue()
|
||||
{
|
||||
while (!_cancellationTokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
CheckAndFixMalformedCsv();
|
||||
try
|
||||
{
|
||||
if (_killQueue.TryDequeue(out var kill))
|
||||
{
|
||||
await ProcessKillAsync(kill);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(100, _cancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error processing kill: {ex.Message}");
|
||||
await Task.Delay(1000, _cancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckAndFixMalformedCsv()
|
||||
private async Task ProcessKillAsync(KillData kill)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to read the file to check if it's malformed
|
||||
using var reader = new StreamReader(_killHistoryPath);
|
||||
var firstLine = reader.ReadLine();
|
||||
|
||||
// If the file is empty or doesn't start with the correct headers, it's malformed
|
||||
if (string.IsNullOrEmpty(firstLine) || firstLine != _headers.TrimEnd('\n'))
|
||||
// Ensure all fields are properly escaped for CSV
|
||||
var fields = new[]
|
||||
{
|
||||
// Create a backup of the malformed file
|
||||
string backupPath = Path.Combine(
|
||||
Path.GetDirectoryName(_killHistoryPath)!,
|
||||
"Kill-log.old"
|
||||
);
|
||||
kill.KillTime.ToString(),
|
||||
EscapeCsvField(kill.EnemyPilot),
|
||||
EscapeCsvField(kill.EnemyShip),
|
||||
EscapeCsvField(kill.Enlisted),
|
||||
EscapeCsvField(kill.RecordNumber),
|
||||
EscapeCsvField(kill.OrgAffiliation),
|
||||
EscapeCsvField(kill.Player),
|
||||
EscapeCsvField(kill.Weapon),
|
||||
EscapeCsvField(kill.Ship),
|
||||
EscapeCsvField(kill.Method),
|
||||
EscapeCsvField(kill.Mode),
|
||||
EscapeCsvField(kill.GameVersion),
|
||||
EscapeCsvField(kill.TrackRver),
|
||||
EscapeCsvField(kill.Logged),
|
||||
EscapeCsvField(kill.PFP),
|
||||
EscapeCsvField(kill.Hash)
|
||||
};
|
||||
|
||||
// If Kill-log.old already exists, delete it
|
||||
if (File.Exists(backupPath))
|
||||
{
|
||||
File.Delete(backupPath);
|
||||
}
|
||||
var csvLine = string.Join(",", fields);
|
||||
|
||||
// Rename the malformed file
|
||||
File.Move(_killHistoryPath, backupPath);
|
||||
|
||||
// Create a new file with correct headers
|
||||
File.WriteAllText(_killHistoryPath, _headers);
|
||||
}
|
||||
// Use FileShare.Read to allow other processes to read while we write
|
||||
using var stream = new FileStream(_killHistoryPath, FileMode.Append, FileAccess.Write, FileShare.Read);
|
||||
using var writer = new StreamWriter(stream);
|
||||
await writer.WriteLineAsync(csvLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// If there's any error reading the file, consider it malformed
|
||||
Console.WriteLine($"Error reading CSV file: {ex.Message}");
|
||||
string backupPath = Path.Combine(
|
||||
Path.GetDirectoryName(_killHistoryPath)!,
|
||||
"Kill-log.old"
|
||||
);
|
||||
|
||||
// If Kill-log.old already exists, delete it
|
||||
if (File.Exists(backupPath))
|
||||
{
|
||||
File.Delete(backupPath);
|
||||
}
|
||||
|
||||
// Rename the malformed file
|
||||
File.Move(_killHistoryPath, backupPath);
|
||||
|
||||
// Create a new file with correct headers
|
||||
File.WriteAllText(_killHistoryPath, _headers);
|
||||
Debug.WriteLine($"Error writing kill to CSV: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddKill(KillData killData)
|
||||
private string EscapeCsvField(string field)
|
||||
{
|
||||
// Ensure the CSV file exists
|
||||
// This should only happen if the file was deleted or corrupted
|
||||
if (!File.Exists(_killHistoryPath))
|
||||
{
|
||||
File.WriteAllText(_killHistoryPath, _headers);
|
||||
}
|
||||
|
||||
// Remove comma from Enlisted
|
||||
killData.Enlisted = killData.Enlisted?.Replace(",", string.Empty);
|
||||
|
||||
// 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}\",\"{killData.Hash}\"");
|
||||
if (string.IsNullOrEmpty(field)) return "";
|
||||
|
||||
// Check file can be written to
|
||||
try
|
||||
// If the field contains any special characters, wrap it in quotes
|
||||
if (field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r"))
|
||||
{
|
||||
using var fileStream = new FileStream(_killHistoryPath, FileMode.Append, FileAccess.Write, FileShare.Read);
|
||||
using var writer = new StreamWriter(fileStream);
|
||||
writer.Write(csv.ToString());
|
||||
// Double up any quotes
|
||||
field = field.Replace("\"", "\"\"");
|
||||
return $"\"{field}\"";
|
||||
}
|
||||
|
||||
// Trigger kill streak sound only if enabled
|
||||
if (ConfigManager.KillStreakEnabled == 1)
|
||||
{
|
||||
_killStreakManager.OnKill();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
// Handle the exception (e.g., log it)
|
||||
Console.WriteLine($"Error writing to file: {ex.Message}");
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public void AddKill(KillData kill)
|
||||
{
|
||||
_killQueue.Enqueue(kill);
|
||||
}
|
||||
|
||||
public void PlayKillStreakSound()
|
||||
{
|
||||
_killStreakManager.OnKill();
|
||||
}
|
||||
|
||||
public void ResetKillStreak()
|
||||
|
@ -120,23 +135,30 @@ public class KillHistoryManager
|
|||
_killStreakManager.OnDeath();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
_processingTask.Wait();
|
||||
_cancellationTokenSource.Dispose();
|
||||
}
|
||||
|
||||
public List<KillData> GetKills()
|
||||
{
|
||||
var kills = new List<KillData>();
|
||||
|
||||
using var reader = new StreamReader(new FileStream(_killHistoryPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
|
||||
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],
|
||||
|
|
83
AutoTrackR2/LogBackupProcessor.cs
Normal file
83
AutoTrackR2/LogBackupProcessor.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using AutoTrackR2.LogEventHandlers;
|
||||
using System.Windows;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace AutoTrackR2;
|
||||
|
||||
public class LogBackupProcessor
|
||||
{
|
||||
private readonly string _logBackupsPath;
|
||||
private readonly KillHistoryManager _killHistoryManager;
|
||||
private readonly List<ILogEventHandler> _logEventHandlers;
|
||||
|
||||
public LogBackupProcessor(string logBackupsPath, KillHistoryManager killHistoryManager, List<ILogEventHandler> logEventHandlers)
|
||||
{
|
||||
_logBackupsPath = logBackupsPath;
|
||||
_killHistoryManager = killHistoryManager;
|
||||
_logEventHandlers = logEventHandlers;
|
||||
}
|
||||
|
||||
public async Task ProcessLogBackupsAsync(Action<string>? onLogFileProcessed = null)
|
||||
{
|
||||
if (!Directory.Exists(_logBackupsPath))
|
||||
{
|
||||
Console.WriteLine($"Log backups directory not found: {_logBackupsPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
var logFiles = Directory.GetFiles(_logBackupsPath, "*.log", SearchOption.AllDirectories)
|
||||
.Where(file => File.GetLastWriteTime(file) >= new DateTime(2025, 3, 27))
|
||||
.ToArray();
|
||||
Array.Sort(logFiles); // Process files in chronological order
|
||||
|
||||
for (int i = 0; i < logFiles.Length; i++)
|
||||
{
|
||||
var logFile = logFiles[i];
|
||||
onLogFileProcessed?.Invoke(logFile);
|
||||
await ProcessLogFileAsync(logFile);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessLogFileAsync(string logFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var reader = new StreamReader(logFilePath);
|
||||
string? line;
|
||||
var lines = new List<string>();
|
||||
while ((line = await reader.ReadLineAsync()) != null)
|
||||
{
|
||||
lines.Add(line);
|
||||
}
|
||||
int actorDeathCount = 0;
|
||||
await Task.Run(() => Parallel.ForEach(lines, line =>
|
||||
{
|
||||
var entry = new LogEntry { Message = line };
|
||||
foreach (var handler in _logEventHandlers)
|
||||
{
|
||||
if (handler.Pattern.IsMatch(line))
|
||||
{
|
||||
handler.Handle(entry);
|
||||
if (handler is ActorDeathEvent)
|
||||
{
|
||||
Interlocked.Increment(ref actorDeathCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
Console.WriteLine($"Processed {actorDeathCount} actor deaths in {logFilePath}");
|
||||
// Wait 5 seconds after processing the file before moving to the next file
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error processing log file {logFilePath}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,9 +46,10 @@ public class LogHandler
|
|||
new GameVersionEvent(),
|
||||
new JumpDriveStateChangedEvent(),
|
||||
new RequestJumpFailedEvent(),
|
||||
new VehicleDestructionEvent()
|
||||
new VehicleDestructionEvent(),
|
||||
new ActorDeathEvent()
|
||||
];
|
||||
|
||||
|
||||
public LogHandler(string? logPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(logPath))
|
||||
|
@ -104,8 +105,6 @@ public class LogHandler
|
|||
HandleLogEntry(line);
|
||||
}
|
||||
|
||||
// Ensures that any deaths already in log aren't sent to the APIs until the monitor thread is running
|
||||
_eventHandlers.Add(new ActorDeathEvent());
|
||||
StartMonitoring();
|
||||
}
|
||||
|
||||
|
@ -218,4 +217,9 @@ public class LogHandler
|
|||
}
|
||||
_gameProcessState = newGameProcessState;
|
||||
}
|
||||
|
||||
public List<ILogEventHandler> GetEventHandlers()
|
||||
{
|
||||
return new List<ILogEventHandler>(_eventHandlers);
|
||||
}
|
||||
}
|
|
@ -1,37 +1,80 @@
|
|||
<Window x:Class="AutoTrackR2.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="AutoTrackR" Height="450" Width="800"
|
||||
WindowStyle="None" ResizeMode="NoResize"
|
||||
Title="AutoTrackR"
|
||||
Height="450"
|
||||
Width="800"
|
||||
WindowStyle="None"
|
||||
ResizeMode="NoResize"
|
||||
AllowsTransparency="True"
|
||||
Style="{StaticResource CustomWindowStyle}">
|
||||
|
||||
<Grid>
|
||||
<!-- Custom Title Bar -->
|
||||
<DockPanel Height="30" VerticalAlignment="Top" MouseDown="TitleBar_MouseDown" Margin="5" Background="Transparent">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Content="_" Width="30" Height="25" Click="MinimizeWindow" Style="{StaticResource TitleButtonStyle}" FontFamily="{StaticResource Orbitron}"/>
|
||||
<Button Content="X" Width="30" Height="25" Click="CloseWindow" Style="{StaticResource TitleButtonStyle}" FontFamily="{StaticResource Orbitron}"/>
|
||||
<DockPanel Height="30"
|
||||
VerticalAlignment="Top"
|
||||
MouseDown="TitleBar_MouseDown"
|
||||
Margin="5"
|
||||
Background="Transparent">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Right">
|
||||
<Button Content="_"
|
||||
Width="30"
|
||||
Height="25"
|
||||
Click="MinimizeWindow"
|
||||
Style="{StaticResource TitleButtonStyle}"
|
||||
FontFamily="{StaticResource Orbitron}"/>
|
||||
<Button Content="X"
|
||||
Width="30"
|
||||
Height="25"
|
||||
Click="CloseWindow"
|
||||
Style="{StaticResource TitleButtonStyle}"
|
||||
FontFamily="{StaticResource Orbitron}"/>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
<!-- Main Content Area -->
|
||||
<Grid Margin="0,30,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Margin="0,30,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Left Tab Panel -->
|
||||
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="5,0,0,0">
|
||||
<Image x:Name="Logo" Height="138" Source="/Assets/AutoTrackR.png" Stretch="Fill" Width="141" RenderOptions.BitmapScalingMode="Fant"/>
|
||||
<Button Content="Home" Name="HomeTab" Margin="10,40,10,10" Height="40" Style="{StaticResource TabButtonStyle}" Click="TabButton_Click"/>
|
||||
<Button Content="Config" Name="ConfigTab" Margin="10" Height="40" Style="{StaticResource TabButtonStyle}" Click="TabButton_Click"/>
|
||||
</StackPanel>
|
||||
<!-- Left Tab Panel -->
|
||||
<StackPanel VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="5,0,0,0">
|
||||
<Image x:Name="Logo"
|
||||
Height="138"
|
||||
Source="/Assets/AutoTrackR.png"
|
||||
Stretch="Fill"
|
||||
Width="141"
|
||||
RenderOptions.BitmapScalingMode="Fant"/>
|
||||
<Button Content="Home"
|
||||
Name="HomeTab"
|
||||
Margin="10,40,10,10"
|
||||
Height="40"
|
||||
Style="{StaticResource TabButtonStyle}"
|
||||
Click="TabButton_Click"/>
|
||||
<Button Content="Config"
|
||||
Name="ConfigTab"
|
||||
Margin="10"
|
||||
Height="40"
|
||||
Style="{StaticResource TabButtonStyle}"
|
||||
Click="TabButton_Click"/>
|
||||
<Button Content="Process Log Backups"
|
||||
Name="ProcessLogBackupsButton"
|
||||
Margin="10"
|
||||
Height="40"
|
||||
Style="{StaticResource TabButtonStyle}"
|
||||
Click="ProcessLogBackups_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Content Area -->
|
||||
<ContentControl Grid.Column="1" Name="ContentControl" Margin="10">
|
||||
<!-- Default content can be set here -->
|
||||
</ContentControl>
|
||||
</Grid>
|
||||
<!-- Content Area -->
|
||||
<ContentControl Grid.Column="1"
|
||||
Name="ContentControl"
|
||||
Margin="10">
|
||||
<!-- Default content can be set here -->
|
||||
</ContentControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
|
@ -120,6 +120,11 @@ namespace AutoTrackR2
|
|||
UpdateTabVisuals();
|
||||
}
|
||||
|
||||
private void ProcessLogBackups_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
homePage.ProcessLogBackups_Click(sender, e);
|
||||
}
|
||||
|
||||
private void UpdateTabStates(string activeTab)
|
||||
{
|
||||
foreach (var key in tabStates.Keys)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue