Fixed warnings, added kill streak option]

This commit is contained in:
Heavy Bob 2025-04-14 05:02:28 +10:00
parent 3c23f34d3f
commit 5f2f429dcc
8 changed files with 363 additions and 339 deletions

View file

@ -12,7 +12,7 @@ namespace AutoTrackR2
/// </summary> /// </summary>
public partial class App : System.Windows.Application public partial class App : System.Windows.Application
{ {
private static Mutex _mutex = null; private static Mutex? _mutex = null;
private static bool _mutexOwned = false; private static bool _mutexOwned = false;
private static readonly string CrashLogPath = Path.Combine( private static readonly string CrashLogPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
@ -33,7 +33,11 @@ namespace AutoTrackR2
try try
{ {
// Ensure crash log directory exists // Ensure crash log directory exists
Directory.CreateDirectory(Path.GetDirectoryName(CrashLogPath)); string? crashLogDir = Path.GetDirectoryName(CrashLogPath);
if (!string.IsNullOrEmpty(crashLogDir))
{
Directory.CreateDirectory(crashLogDir);
}
// Set up unhandled exception handlers // Set up unhandled exception handlers
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
@ -94,36 +98,34 @@ namespace AutoTrackR2
$"Target Site: {ex?.TargetSite}\n" + $"Target Site: {ex?.TargetSite}\n" +
"----------------------------------------\n"; "----------------------------------------\n";
File.AppendAllText(CrashLogPath, logMessage); // Ensure directory exists
string? crashLogDir = Path.GetDirectoryName(CrashLogPath);
if (!string.IsNullOrEmpty(crashLogDir))
{
Directory.CreateDirectory(crashLogDir);
}
// Show error message to user // Write to log file
MessageBox.Show( File.AppendAllText(CrashLogPath, logMessage);
"AutoTrackR2 has encountered an error. A crash log has been created.\n" +
$"Location: {CrashLogPath}",
"AutoTrackR2 Error",
MessageBoxButton.OK,
MessageBoxImage.Error
);
} }
catch (Exception logEx) catch (Exception logEx)
{ {
// If logging fails, at least show a basic error message // If we can't log to file, at least try to show a message box
MessageBox.Show( Console.WriteLine($"Failed to write crash log: {logEx.Message}");
"AutoTrackR2 has encountered an error and failed to create a crash log.\n" + MessageBox.Show($"Failed to write crash log: {logEx.Message}", "AutoTrackR2 Error", MessageBoxButton.OK, MessageBoxImage.Error);
$"Error: {ex?.Message}",
"AutoTrackR2 Error",
MessageBoxButton.OK,
MessageBoxImage.Error
);
} }
} }
protected override void OnExit(ExitEventArgs e) protected override void OnExit(ExitEventArgs e)
{ {
// Clean up resources if (_mutexOwned)
_killStreakManager?.Cleanup(); {
_mutex?.Dispose(); // Clean up resources
base.OnExit(e); _streamlinkHandler?.Dispose();
_killStreakManager?.Dispose();
_mutex?.Dispose();
base.OnExit(e);
}
} }
} }
} }

View file

@ -1,181 +1,163 @@
<UserControl x:Class="AutoTrackR2.ConfigPage" <UserControl x:Class="AutoTrackR2.ConfigPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="410" Height="380"
Width="700"> Width="700">
<Grid Background="{DynamicResource BackgroundLightBrush}"> <Grid Background="{DynamicResource BackgroundLightBrush}">
<!-- Main Layout Grid --> <!-- Main Content -->
<Grid> <StackPanel Margin="20,5,20,0">
<Grid.RowDefinitions> <!-- Log File -->
<RowDefinition Height="*"/> <StackPanel Margin="0,0,0,5"
<RowDefinition Height="Auto"/> Orientation="Horizontal">
</Grid.RowDefinitions> <TextBlock Text="ⓘ"
ToolTip="Set this to the Game.log file in your StarCitizen\LIVE directory."
<Grid.ColumnDefinitions> Foreground="{DynamicResource TextBrush}"
<!-- Left column for the main content area --> FontSize="20"
<ColumnDefinition Width="*"/> Margin="0,0,3,5"/>
<!-- Right column for the buttons --> <TextBlock Text="Log File:"
<ColumnDefinition Width="Auto"/> Foreground="{DynamicResource TextBrush}"
</Grid.ColumnDefinitions> FontSize="16"
Margin="0,5,0,5"
<!-- Section for Config Fields --> FontFamily="{StaticResource Roboto}"/>
<StackPanel Grid.Row="0" <StackPanel Orientation="Horizontal">
VerticalAlignment="Top" <TextBox Name="LogFilePath"
Margin="20,10,20,0"> Width="260"
<!-- Log File --> Height="30"
<StackPanel Margin="0,5,0,10" Margin="10,0,0,0"
Orientation="Horizontal"> Style="{StaticResource RoundedTextBox}"/>
<TextBlock Text="ⓘ" <Button Content="Browse"
ToolTip="Set this to the Game.log file in your StarCitizen\LIVE directory." Width="75"
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">
<TextBox Name="LogFilePath"
Width="260"
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="LogFileBrowseButton_Click"/>
<Button Content="Open"
Width="75"
Height="30"
FontFamily="{StaticResource Orbitron}"
Margin="0,0"
Style="{StaticResource ButtonStyle}"
Click="LogFileOpenButton_Click"/>
</StackPanel>
</StackPanel>
<!-- API URL -->
<StackPanel Margin="0,0,0,10"
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">
<TextBox Name="ApiUrl"
Width="260"
Height="30"
Margin="10,0,0,0"
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,10"
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"/>
<PasswordBox Name="ApiKey"
Width="260"
Height="30"
Margin="10,0,0,0"
Style="{StaticResource RoundedPasswordBox}"/>
</StackPanel>
<!-- Video Path -->
<StackPanel Margin="0,0,0,10"
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="260"
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"/>
<Button Content="Open"
Width="75"
Height="30"
FontFamily="{StaticResource Orbitron}"
Margin="0,0"
Style="{StaticResource ButtonStyle}"
Click="VideoPathOpenButton_Click"/>
</StackPanel>
</StackPanel>
<!-- Toggle Settings Grid -->
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Kill Streak Test Button -->
<!--
<Button Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="2"
Content="Test Kill Streak Sounds"
Height="30" Height="30"
FontFamily="{StaticResource Orbitron}" FontFamily="{StaticResource Orbitron}"
Margin="0,0,0,10" Margin="5,0"
Style="{StaticResource ButtonStyle}" Style="{StaticResource ButtonStyle}"
Click="TestKillStreakButton_Click"/> Click="LogFileBrowseButton_Click"/>
--> <Button Content="Open"
Width="75"
Height="30"
FontFamily="{StaticResource Orbitron}"
Margin="0,0"
Style="{StaticResource ButtonStyle}"
Click="LogFileOpenButton_Click"/>
</StackPanel>
</StackPanel>
<!-- Left Column Controls --> <!-- API URL -->
<StackPanel Margin="0,0,0,5"
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">
<TextBox Name="ApiUrl"
Width="260"
Height="30"
Margin="10,0,0,0"
Style="{StaticResource RoundedTextBox}"/>
<Button Content="Test"
Width="75"
Height="30"
FontFamily="{StaticResource Orbitron}"
Margin="5,0"
Style="{StaticResource ButtonStyle}"
Click="TestApiButton_Click"/>
</StackPanel>
</StackPanel>
<!-- Visor Wipe Toggle --> <!-- API Key -->
<StackPanel Grid.Column="0" <StackPanel Margin="0,0,0,5"
Grid.Row="0" Orientation="Horizontal">
Orientation="Horizontal" <TextBlock Text="ⓘ"
Margin="0,0,5,5"> 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"/>
<PasswordBox Name="ApiKey"
Width="260"
Height="30"
Margin="10,0,0,0"
Style="{StaticResource RoundedPasswordBox}"/>
</StackPanel>
<!-- Video Path -->
<StackPanel Margin="0,0,0,5"
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="260"
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"/>
<Button Content="Open"
Width="75"
Height="30"
FontFamily="{StaticResource Orbitron}"
Margin="0,0"
Style="{StaticResource ButtonStyle}"
Click="VideoPathOpenButton_Click"/>
</StackPanel>
</StackPanel>
<!-- Toggle Settings Grid -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Left Column -->
<StackPanel Grid.Column="0"
Margin="0,0,5,0">
<!-- Kill Streak -->
<StackPanel Orientation="Horizontal"
Margin="0,0,0,3">
<TextBlock Text="ⓘ"
ToolTip="Enable or disable kill streak sound effects."
Foreground="{DynamicResource TextBrush}"
FontSize="20"
Margin="0,4,3,0"/>
<TextBlock Text="Kill Streak:"
Foreground="{DynamicResource TextBrush}"
FontSize="16"
Margin="0,7,10,0"/>
<Slider x:Name="KillStreakSlider"
Width="100"
Minimum="0"
Maximum="1"
Style="{StaticResource ToggleSliderStyle}"
ValueChanged="KillStreakSlider_ValueChanged"/>
</StackPanel>
<!-- Visor Wipe -->
<StackPanel Orientation="Horizontal"
Margin="0,0,0,3">
<TextBlock Text="ⓘ" <TextBlock Text="ⓘ"
ToolTip="Perform a Visor Wipe animation on player kill. Requires AHKv2." ToolTip="Perform a Visor Wipe animation on player kill. Requires AHKv2."
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
@ -185,23 +167,17 @@
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
FontSize="16" FontSize="16"
Margin="0,7,0,0"/> Margin="0,7,0,0"/>
<Slider Name="VisorWipeSlider" <Slider x:Name="VisorWipeSlider"
Width="100"
Minimum="0" Minimum="0"
Maximum="1" Maximum="1"
TickFrequency="1"
IsSnapToTickEnabled="True"
Value="0"
Style="{StaticResource ToggleSliderStyle}" Style="{StaticResource ToggleSliderStyle}"
Margin="10,-4,0,0"
Width="100"
ValueChanged="VisorWipeSlider_ValueChanged"/> ValueChanged="VisorWipeSlider_ValueChanged"/>
</StackPanel> </StackPanel>
<!-- Video Record Toggle --> <!-- Video Record -->
<StackPanel Grid.Column="0" <StackPanel Orientation="Horizontal"
Grid.Row="1" Margin="0,0,0,3">
Orientation="Horizontal"
Margin="0,0,5,5">
<TextBlock Text="ⓘ" <TextBlock Text="ⓘ"
ToolTip="Record video on player kill. Requires AHKv2." ToolTip="Record video on player kill. Requires AHKv2."
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
@ -211,23 +187,17 @@
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
FontSize="16" FontSize="16"
Margin="0,7,0,0"/> Margin="0,7,0,0"/>
<Slider Name="VideoRecordSlider" <Slider x:Name="VideoRecordSlider"
Width="100"
Minimum="0" Minimum="0"
Maximum="1" Maximum="1"
TickFrequency="1"
IsSnapToTickEnabled="True"
Value="0"
Style="{StaticResource ToggleSliderStyle}" Style="{StaticResource ToggleSliderStyle}"
Margin="10,-4,0,0"
Width="100"
ValueChanged="VideoRecordSlider_ValueChanged"/> ValueChanged="VideoRecordSlider_ValueChanged"/>
</StackPanel> </StackPanel>
<!-- Offline Mode Toggle --> <!-- Offline Mode -->
<StackPanel Grid.Column="0" <StackPanel Orientation="Horizontal"
Grid.Row="2" Margin="0,0,0,3">
Orientation="Horizontal"
Margin="0,0,5,5">
<TextBlock Text="ⓘ" <TextBlock Text="ⓘ"
ToolTip="With Offline Mode enabled, kills will not be submitted to the configured API." ToolTip="With Offline Mode enabled, kills will not be submitted to the configured API."
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
@ -237,25 +207,21 @@
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
FontSize="16" FontSize="16"
Margin="0,7,0,0"/> Margin="0,7,0,0"/>
<Slider Name="OfflineModeSlider" <Slider x:Name="OfflineModeSlider"
Width="100"
Minimum="0" Minimum="0"
Maximum="1" Maximum="1"
TickFrequency="1"
IsSnapToTickEnabled="True"
Value="0"
Style="{StaticResource ToggleSliderStyle}" Style="{StaticResource ToggleSliderStyle}"
Margin="10,-4,0,0"
Width="100"
ValueChanged="OfflineModeSlider_ValueChanged"/> ValueChanged="OfflineModeSlider_ValueChanged"/>
</StackPanel> </StackPanel>
</StackPanel>
<!-- Right Column Controls --> <!-- Right Column -->
<StackPanel Grid.Column="1"
<!-- Streamlink Toggle --> Margin="5,0,0,0">
<StackPanel Grid.Column="1" <!-- Streamlink -->
Grid.Row="0" <StackPanel Orientation="Horizontal"
Orientation="Horizontal" Margin="0,0,0,3">
Margin="5,0,0,5">
<TextBlock Text="ⓘ" <TextBlock Text="ⓘ"
ToolTip="Use Streamlink for video recording." ToolTip="Use Streamlink for video recording."
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
@ -265,23 +231,17 @@
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
FontSize="16" FontSize="16"
Margin="0,7,0,0"/> Margin="0,7,0,0"/>
<Slider Name="StreamlinkSlider" <Slider x:Name="StreamlinkSlider"
Width="100"
Minimum="0" Minimum="0"
Maximum="1" Maximum="1"
TickFrequency="1"
IsSnapToTickEnabled="True"
Value="0"
Style="{StaticResource ToggleSliderStyle}" Style="{StaticResource ToggleSliderStyle}"
Margin="10,-4,0,0"
Width="100"
ValueChanged="StreamlinkSlider_ValueChanged"/> ValueChanged="StreamlinkSlider_ValueChanged"/>
</StackPanel> </StackPanel>
<!-- Streamlink Duration --> <!-- Duration -->
<StackPanel Grid.Column="1" <StackPanel Orientation="Horizontal"
Grid.Row="1" Margin="0,0,0,3">
Orientation="Horizontal"
Margin="5,0,0,5">
<TextBlock Text="ⓘ" <TextBlock Text="ⓘ"
ToolTip="Duration of Streamlink recordings in seconds." ToolTip="Duration of Streamlink recordings in seconds."
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
@ -291,15 +251,11 @@
Foreground="{DynamicResource TextBrush}" Foreground="{DynamicResource TextBrush}"
FontSize="16" FontSize="16"
Margin="0,7,0,0"/> Margin="0,7,0,0"/>
<Slider Name="StreamlinkDurationSlider" <Slider x:Name="StreamlinkDurationSlider"
Width="100"
Minimum="10" Minimum="10"
Maximum="600" Maximum="600"
TickFrequency="30"
IsSnapToTickEnabled="True"
Value="30"
Style="{StaticResource SliderStyle}" Style="{StaticResource SliderStyle}"
Margin="10,-4,0,0"
Width="100"
ValueChanged="StreamlinkDurationSlider_ValueChanged"/> ValueChanged="StreamlinkDurationSlider_ValueChanged"/>
<TextBlock Name="StreamlinkDurationText" <TextBlock Name="StreamlinkDurationText"
Text="30s" Text="30s"
@ -309,50 +265,49 @@
</StackPanel> </StackPanel>
<!-- Test Streamlink Button --> <!-- Test Streamlink Button -->
<Button Grid.Column="1" <Button Content="Test Streamlink"
Grid.Row="2"
Content="Test Streamlink"
Width="120" Width="120"
Height="30" Height="30"
FontFamily="{StaticResource Orbitron}" FontFamily="{StaticResource Orbitron}"
Margin="5,0,0,5" Margin="0,0,0,3"
Style="{StaticResource ButtonStyle}" Style="{StaticResource ButtonStyle}"
Click="TestStreamlinkButton_Click"/> Click="TestStreamlinkButton_Click"/>
</StackPanel>
</Grid>
<!-- Theme Slider --> <!-- Theme Slider -->
<StackPanel Grid.Column="0" <StackPanel Orientation="Horizontal"
Grid.Row="3" Margin="0,5,0,0">
Grid.ColumnSpan="2" <TextBlock Text="ⓘ"
Orientation="Horizontal" ToolTip="Select theme for the application."
Margin="0,0,0,5"> Foreground="{DynamicResource TextBrush}"
<TextBlock Text="Theme:" FontSize="20"
Foreground="{DynamicResource TextBrush}" Margin="0,4,3,0"/>
FontSize="16" <TextBlock Text="Theme:"
Margin="0,7,10,0"/> Foreground="{DynamicResource TextBrush}"
<Slider x:Name="ThemeSlider" FontSize="16"
Minimum="0" Margin="0,7,10,0"/>
Value="0" <Slider x:Name="ThemeSlider"
TickFrequency="1" Width="350"
IsSnapToTickEnabled="True" Minimum="0"
ValueChanged="ThemeSlider_ValueChanged" Value="0"
Width="350" TickFrequency="1"
Style="{StaticResource ThreePositionSlider}"/> IsSnapToTickEnabled="True"
</StackPanel> ValueChanged="ThemeSlider_ValueChanged"
</Grid> Style="{StaticResource SliderStyle}"/>
</StackPanel> </StackPanel>
</StackPanel>
<!-- Save Button --> <!-- Save Button -->
<Button x:Name="SaveButton" <Button x:Name="SaveButton"
Grid.Row="1" Content="Save"
Content="Save" Width="100"
Width="100" Height="40"
Height="40" Style="{StaticResource ButtonStyle}"
Style="{StaticResource ButtonStyle}" FontFamily="{StaticResource Orbitron}"
FontFamily="{StaticResource Orbitron}" HorizontalAlignment="Right"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
VerticalAlignment="Bottom" Margin="0,0,80,10"
Margin="0,0,80,20" Click="SaveButton_Click"/>
Click="SaveButton_Click"/>
</Grid>
</Grid> </Grid>
</UserControl> </UserControl>

View file

@ -26,8 +26,6 @@ public partial class ConfigPage : UserControl
Dictionary<string, Theme>? _themes = null; Dictionary<string, Theme>? _themes = null;
private KillStreakManager? _testKillStreakManager; private KillStreakManager? _testKillStreakManager;
private bool _isTestRunning = false;
private int _currentTestStep = 0;
public ConfigPage(MainWindow mainWindow) public ConfigPage(MainWindow mainWindow)
{ {
@ -44,6 +42,7 @@ public partial class ConfigPage : UserControl
ThemeSlider.Value = ConfigManager.Theme; ThemeSlider.Value = ConfigManager.Theme;
StreamlinkSlider.Value = ConfigManager.StreamlinkEnabled; StreamlinkSlider.Value = ConfigManager.StreamlinkEnabled;
StreamlinkDurationSlider.Value = ConfigManager.StreamlinkDuration; StreamlinkDurationSlider.Value = ConfigManager.StreamlinkDuration;
KillStreakSlider.Value = ConfigManager.KillStreakEnabled;
// Initialize Streamlink slider style // Initialize Streamlink slider style
if (StreamlinkSlider.Value == 0) if (StreamlinkSlider.Value == 0)
@ -333,6 +332,18 @@ public partial class ConfigPage : UserControl
} }
} }
private void KillStreakSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (KillStreakSlider.Value == 0)
{
KillStreakSlider.Style = (Style)Application.Current.FindResource("FalseToggleStyle");
}
else
{
KillStreakSlider.Style = (Style)Application.Current.FindResource("ToggleSliderStyle");
}
}
private void SaveButton_Click(object sender, RoutedEventArgs e) private void SaveButton_Click(object sender, RoutedEventArgs e)
{ {
ConfigManager.ApiKey = ApiKey.Password; ConfigManager.ApiKey = ApiKey.Password;
@ -343,6 +354,9 @@ public partial class ConfigPage : UserControl
ConfigManager.VideoRecord = (int)VideoRecordSlider.Value; ConfigManager.VideoRecord = (int)VideoRecordSlider.Value;
ConfigManager.OfflineMode = (int)OfflineModeSlider.Value; ConfigManager.OfflineMode = (int)OfflineModeSlider.Value;
ConfigManager.Theme = (int)ThemeSlider.Value; ConfigManager.Theme = (int)ThemeSlider.Value;
ConfigManager.StreamlinkEnabled = (int)StreamlinkSlider.Value;
ConfigManager.StreamlinkDuration = (int)StreamlinkDurationSlider.Value;
ConfigManager.KillStreakEnabled = (int)KillStreakSlider.Value;
// Save the current config values // Save the current config values
ConfigManager.SaveConfig(); ConfigManager.SaveConfig();

View file

@ -58,6 +58,7 @@ public class KillHistoryManager
catch (Exception ex) catch (Exception ex)
{ {
// If there's any error reading the file, consider it malformed // If there's any error reading the file, consider it malformed
Console.WriteLine($"Error reading CSV file: {ex.Message}");
string backupPath = Path.Combine( string backupPath = Path.Combine(
Path.GetDirectoryName(_killHistoryPath)!, Path.GetDirectoryName(_killHistoryPath)!,
"Kill-log.old" "Kill-log.old"
@ -100,8 +101,11 @@ public class KillHistoryManager
using var writer = new StreamWriter(fileStream); using var writer = new StreamWriter(fileStream);
writer.Write(csv.ToString()); writer.Write(csv.ToString());
// Trigger kill streak sound // Trigger kill streak sound only if enabled
_killStreakManager.OnKill(); if (ConfigManager.KillStreakEnabled == 1)
{
_killStreakManager.OnKill();
}
} }
catch (IOException ex) catch (IOException ex)
{ {

View file

@ -5,7 +5,7 @@ using NAudio.Wave;
namespace AutoTrackR2; namespace AutoTrackR2;
public class KillStreakManager public class KillStreakManager : IDisposable
{ {
private readonly Queue<string> _soundQueue = new(); private readonly Queue<string> _soundQueue = new();
private readonly System.Timers.Timer _killStreakTimer = new(5000); // 5 seconds between kills for streak private readonly System.Timers.Timer _killStreakTimer = new(5000); // 5 seconds between kills for streak
@ -15,6 +15,7 @@ public class KillStreakManager
private readonly object _lock = new(); private readonly object _lock = new();
private WaveOutEvent? _waveOut; private WaveOutEvent? _waveOut;
private bool _isPlaying = false; private bool _isPlaying = false;
private bool _disposed = false;
public KillStreakManager(string soundsPath) public KillStreakManager(string soundsPath)
{ {
@ -27,13 +28,15 @@ public class KillStreakManager
{ {
lock (_lock) lock (_lock)
{ {
if (_disposed) return;
_currentKills++; _currentKills++;
_totalKills++; _totalKills++;
_killStreakTimer.Stop(); _killStreakTimer.Stop();
_killStreakTimer.Start(); _killStreakTimer.Start();
// Handle multi-kill announcements // Handle multi-kill announcements
string multiKillSound = _currentKills switch string? multiKillSound = _currentKills switch
{ {
2 => "double_kill.mp3", 2 => "double_kill.mp3",
3 => "triple_kill.mp3", 3 => "triple_kill.mp3",
@ -48,7 +51,7 @@ public class KillStreakManager
}; };
// Handle spree announcements // Handle spree announcements
string spreeSound = _totalKills switch string? spreeSound = _totalKills switch
{ {
5 => "killing_spree.mp3", 5 => "killing_spree.mp3",
10 => "killing_frenzy.mp3", 10 => "killing_frenzy.mp3",
@ -88,6 +91,8 @@ public class KillStreakManager
{ {
lock (_lock) lock (_lock)
{ {
if (_disposed) return;
_totalKills = 0; _totalKills = 0;
_currentKills = 0; _currentKills = 0;
_killStreakTimer.Stop(); _killStreakTimer.Stop();
@ -99,77 +104,97 @@ public class KillStreakManager
{ {
lock (_lock) lock (_lock)
{ {
if (_disposed) return;
_currentKills = 0; _currentKills = 0;
_killStreakTimer.Stop(); _killStreakTimer.Stop();
Console.WriteLine("Kill streak reset due to timeout"); Console.WriteLine("Kill streak reset due to timeout");
} }
} }
public void Cleanup()
{
lock (_lock)
{
_killStreakTimer.Stop();
_killStreakTimer.Dispose();
_waveOut?.Dispose();
_waveOut = null;
}
}
private void PlayNextSound() private void PlayNextSound()
{ {
if (_soundQueue.Count > 0) if (_soundQueue.Count == 0 || _disposed) return;
string soundPath = _soundQueue.Dequeue();
Console.WriteLine($"Attempting to play sound: {soundPath}");
try
{ {
string soundPath = _soundQueue.Dequeue(); if (!File.Exists(soundPath))
Console.WriteLine($"Attempting to play sound: {soundPath}");
try
{ {
if (File.Exists(soundPath)) Console.WriteLine($"Sound file not found: {soundPath}");
_isPlaying = false;
if (_soundQueue.Count > 0)
{ {
// Stop any currently playing sound PlayNextSound();
_waveOut?.Stop();
_waveOut?.Dispose();
// Create a new WaveOutEvent
_waveOut = new WaveOutEvent();
// Create a new AudioFileReader for the MP3 file
using var audioFile = new AudioFileReader(soundPath);
_waveOut.Init(audioFile);
// Set up event handler for when playback finishes
_waveOut.PlaybackStopped += (sender, e) =>
{
_isPlaying = false;
if (_soundQueue.Count > 0)
{
PlayNextSound();
}
};
_isPlaying = true;
_waveOut.Play();
Console.WriteLine($"Successfully played sound: {soundPath}");
} }
else return;
}
// Stop any currently playing sound
_waveOut?.Stop();
_waveOut?.Dispose();
_waveOut = null;
// Create a new WaveOutEvent
_waveOut = new WaveOutEvent();
// Create a new AudioFileReader for the MP3 file
using var audioFile = new AudioFileReader(soundPath);
_waveOut.Init(audioFile);
// Set up event handler for when playback finishes
_waveOut.PlaybackStopped += (sender, e) =>
{
lock (_lock)
{ {
Console.WriteLine($"Sound file not found: {soundPath}"); if (_disposed) return;
_isPlaying = false; _isPlaying = false;
if (_soundQueue.Count > 0) if (_soundQueue.Count > 0)
{ {
PlayNextSound(); PlayNextSound();
} }
} }
} };
catch (Exception ex)
_isPlaying = true;
_waveOut.Play();
Console.WriteLine($"Successfully played sound: {soundPath}");
}
catch (Exception ex)
{
Console.WriteLine($"Error playing sound {soundPath}: {ex.Message}");
_isPlaying = false;
if (_soundQueue.Count > 0)
{ {
Console.WriteLine($"Error playing sound {soundPath}: {ex.Message}"); PlayNextSound();
_isPlaying = false; }
if (_soundQueue.Count > 0) }
{ }
PlayNextSound();
} public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
lock (_lock)
{
_disposed = true;
_killStreakTimer.Stop();
_killStreakTimer.Dispose();
_waveOut?.Stop();
_waveOut?.Dispose();
_waveOut = null;
} }
} }
} }

View file

@ -198,6 +198,7 @@ namespace AutoTrackR2
public static int Theme { get; set; } public static int Theme { get; set; }
public static int StreamlinkEnabled { get; set; } public static int StreamlinkEnabled { get; set; }
public static int StreamlinkDuration { get; set; } = 30; public static int StreamlinkDuration { get; set; } = 30;
public static int KillStreakEnabled { get; set; } = 1; // Default to enabled
static ConfigManager() static ConfigManager()
{ {
@ -253,6 +254,8 @@ namespace AutoTrackR2
StreamlinkEnabled = int.Parse(line.Substring("StreamlinkEnabled=".Length).Trim()); StreamlinkEnabled = int.Parse(line.Substring("StreamlinkEnabled=".Length).Trim());
else if (line.StartsWith("StreamlinkDuration=")) else if (line.StartsWith("StreamlinkDuration="))
StreamlinkDuration = int.Parse(line.Substring("StreamlinkDuration=".Length).Trim()); StreamlinkDuration = int.Parse(line.Substring("StreamlinkDuration=".Length).Trim());
else if (line.StartsWith("KillStreakEnabled="))
KillStreakEnabled = int.Parse(line.Substring("KillStreakEnabled=".Length).Trim());
} }
} }
} }
@ -286,6 +289,7 @@ namespace AutoTrackR2
writer.WriteLine($"Theme={Theme}"); writer.WriteLine($"Theme={Theme}");
writer.WriteLine($"StreamlinkEnabled={StreamlinkEnabled}"); writer.WriteLine($"StreamlinkEnabled={StreamlinkEnabled}");
writer.WriteLine($"StreamlinkDuration={StreamlinkDuration}"); writer.WriteLine($"StreamlinkDuration={StreamlinkDuration}");
writer.WriteLine($"KillStreakEnabled={KillStreakEnabled}");
} }
} }
} }

View file

@ -3,13 +3,33 @@ using System.IO;
namespace AutoTrackR2; namespace AutoTrackR2;
public class StreamlinkHandler public class StreamlinkHandler : IDisposable
{ {
private bool _disposed = false;
public StreamlinkHandler() public StreamlinkHandler()
{ {
TrackREventDispatcher.StreamlinkRecordEvent += HandleStreamlinkRecord; TrackREventDispatcher.StreamlinkRecordEvent += HandleStreamlinkRecord;
} }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
TrackREventDispatcher.StreamlinkRecordEvent -= HandleStreamlinkRecord;
}
_disposed = true;
}
public static bool IsStreamlinkInstalled() public static bool IsStreamlinkInstalled()
{ {
try try

View file

@ -144,7 +144,7 @@ public static class WebHandler
apiKillData.rsi = "-1"; apiKillData.rsi = "-1";
} }
if (!apiKillData.enlisted.Contains(",")) if (!string.IsNullOrEmpty(apiKillData.enlisted) && !apiKillData.enlisted.Contains(","))
{ {
//Get second whitespace in string //Get second whitespace in string
var index = apiKillData.enlisted.IndexOf(" ", apiKillData.enlisted.IndexOf(" ", StringComparison.Ordinal) + 1, StringComparison.Ordinal); var index = apiKillData.enlisted.IndexOf(" ", apiKillData.enlisted.IndexOf(" ", StringComparison.Ordinal) + 1, StringComparison.Ordinal);