Compare commits

...

4 commits

Author SHA1 Message Date
Heavy Bob
1e0cf4ce0d Crash Log when crashes, there can be only one. 2025-04-09 10:54:46 +10:00
Heavy Bob
daf6954e74 Changed Config Formatting
Fixed an issue with the video path looking for a file when it needed to be defined as a folder...  https://www.youtube.com/watch?v=ahKH19iN2SM
2025-04-09 08:52:17 +10:00
Heavy Bob
1f35e0a9e6 Fonts.
Actually cancer to read. Addresses the issue of the theme color not being assigned for themes correctly.
2025-04-09 06:40:14 +10:00
Heavy Bob
ab6b95a281 Added location to the homepage. 2025-04-09 05:57:19 +10:00
5 changed files with 230 additions and 102 deletions

View file

@ -1,6 +1,9 @@
using System.Configuration;
using System.Data;
using System.Windows;
using System.Threading;
using System.IO;
using System;
namespace AutoTrackR2
{
@ -9,5 +12,101 @@ namespace AutoTrackR2
/// </summary>
public partial class App : System.Windows.Application
{
private static Mutex _mutex = null;
private static bool _mutexOwned = false;
private static readonly string CrashLogPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"AutoTrackR2",
"crash.log"
);
protected override void OnStartup(StartupEventArgs e)
{
// Ensure crash log directory exists
Directory.CreateDirectory(Path.GetDirectoryName(CrashLogPath));
// Set up unhandled exception handlers
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
DispatcherUnhandledException += App_DispatcherUnhandledException;
const string appName = "AutoTrackR2";
bool createdNew;
_mutex = new Mutex(true, appName, out createdNew);
_mutexOwned = createdNew;
if (!createdNew)
{
// App is already running, silently exit
Current.Shutdown();
return;
}
base.OnStartup(e);
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
LogCrash(e.ExceptionObject as Exception);
}
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
LogCrash(e.Exception);
e.Handled = true; // Prevent the application from crashing
}
private void LogCrash(Exception? ex)
{
try
{
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var logMessage = $"[{timestamp}] CRASH: {ex?.Message}\n" +
$"Stack Trace:\n{ex?.StackTrace}\n" +
$"Source: {ex?.Source}\n" +
$"Target Site: {ex?.TargetSite}\n" +
"----------------------------------------\n";
File.AppendAllText(CrashLogPath, logMessage);
// Show error message to user
MessageBox.Show(
"AutoTrackR2 has encountered an error. A crash log has been created.\n" +
$"Location: {CrashLogPath}",
"AutoTrackR2 Error",
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
catch (Exception logEx)
{
// If logging fails, at least show a basic error message
MessageBox.Show(
"AutoTrackR2 has encountered an error and failed to create a crash log.\n" +
$"Error: {ex?.Message}",
"AutoTrackR2 Error",
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
}
protected override void OnExit(ExitEventArgs e)
{
if (_mutex != null && _mutexOwned)
{
try
{
_mutex.ReleaseMutex();
}
catch (Exception ex)
{
// Log the mutex release error but don't throw
LogCrash(ex);
}
_mutex.Dispose();
}
base.OnExit(e);
}
}
}

View file

@ -26,7 +26,7 @@
VerticalAlignment="Center"
Height="389">
<!-- Log File -->
<StackPanel Margin="0,10,0,15"
<StackPanel Margin="0,5,0,10"
Orientation="Horizontal">
<TextBlock Text="ⓘ"
ToolTip="Set this to the Game.log file in your StarCitizen\LIVE directory."
@ -55,7 +55,7 @@
</StackPanel>
<!-- API URL -->
<StackPanel Margin="0,0,0,15"
<StackPanel Margin="0,0,0,10"
Orientation="Horizontal">
<TextBlock Text="ⓘ"
ToolTip="Need a URL? No idea what to do? Contact heavy_bob on Discord!"
@ -83,7 +83,7 @@
</StackPanel>
<!-- API Key -->
<StackPanel Margin="0,0,0,15"
<StackPanel Margin="0,0,0,10"
Orientation="Horizontal">
<TextBlock Text="ⓘ"
ToolTip="Need a key? No idea what to do? Contact heavy_bob on Discord!"
@ -102,7 +102,7 @@
</StackPanel>
<!-- Video Path -->
<StackPanel Margin="0,0,0,15"
<StackPanel Margin="0,0,0,10"
Orientation="Horizontal">
<TextBlock Text="ⓘ"
ToolTip="The directory where your clipping software saves kills. Check the README."
@ -129,18 +129,31 @@
</StackPanel>
</StackPanel>
<!-- Visor Wipe Toggle Slider -->
<StackPanel Margin="0,0,0,15"
Orientation="Horizontal">
<!-- 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"/>
</Grid.RowDefinitions>
<!-- Visor Wipe Toggle -->
<StackPanel Grid.Column="0" Grid.Row="0"
Orientation="Horizontal"
Margin="0,0,5,5">
<TextBlock Text="ⓘ"
ToolTip="Perform a Visor Wipe animation on player kill. Requires AHKv2."
Foreground="{DynamicResource TextBrush}"
FontSize="20"
Margin="0,4,3,5"/>
Margin="0,4,3,0"/>
<TextBlock Text="Visor Wipe:"
Foreground="{DynamicResource TextBrush}"
FontSize="16"
Margin="0,7,0,5"/>
Margin="0,7,0,0"/>
<Slider Name="VisorWipeSlider"
Minimum="0"
Maximum="1"
@ -148,22 +161,24 @@
IsSnapToTickEnabled="True"
Value="0"
Style="{StaticResource ToggleSliderStyle}"
Margin="27,-4,0,0"
Margin="10,-4,0,0"
Width="100"
ValueChanged="VisorWipeSlider_ValueChanged"/>
</StackPanel>
</StackPanel>
<!-- Video Record Toggle Slider -->
<StackPanel Margin="0,0,0,15"
Orientation="Horizontal">
<!-- Video Record Toggle -->
<StackPanel Grid.Column="1" Grid.Row="0"
Orientation="Horizontal"
Margin="5,0,0,5">
<TextBlock Text="ⓘ"
ToolTip="Automatically clip your last kill. Check the README for more info."
Foreground="{DynamicResource TextBrush}"
FontSize="20"
Margin="0,4,3,5"/>
Margin="0,4,3,0"/>
<TextBlock Text="Video Record:"
Foreground="{DynamicResource TextBrush}"
FontSize="16"
Margin="0,7,0,5"/>
Margin="0,7,0,0"/>
<Slider Name="VideoRecordSlider"
Minimum="0"
Maximum="1"
@ -172,21 +187,23 @@
Value="0"
Style="{StaticResource ToggleSliderStyle}"
Margin="10,-4,0,0"
Width="100"
ValueChanged="VideoRecordSlider_ValueChanged"/>
</StackPanel>
</StackPanel>
<!-- Offline Mode Toggle Slider -->
<StackPanel Margin="0,0,0,10"
Orientation="Horizontal">
<!-- Offline Mode Toggle -->
<StackPanel Grid.Column="0" Grid.Row="1"
Orientation="Horizontal"
Margin="0,0,5,5">
<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"/>
Margin="0,4,3,0"/>
<TextBlock Text="Offline Mode:"
Foreground="{DynamicResource TextBrush}"
FontSize="16"
Margin="0,7,0,5"/>
Margin="0,7,0,0"/>
<Slider Name="OfflineModeSlider"
Minimum="0"
Maximum="1"
@ -194,17 +211,20 @@
IsSnapToTickEnabled="True"
Value="0"
Style="{StaticResource ToggleSliderStyle}"
Margin="12,-4,0,0"
Margin="10,-4,0,0"
Width="100"
ValueChanged="OfflineModeSlider_ValueChanged"/>
</StackPanel>
</StackPanel>
<!-- 3-Position Toggle Slider -->
<StackPanel Margin="0,0,0,15"
Orientation="Horizontal">
<!-- Theme Slider -->
<StackPanel Grid.Column="0" Grid.Row="2"
Grid.ColumnSpan="2"
Orientation="Horizontal"
Margin="0,0,0,5">
<TextBlock Text="Theme:"
Foreground="{DynamicResource TextBrush}"
FontSize="16"
Margin="0,7,0,5"/>
Margin="0,7,10,0"/>
<Slider x:Name="ThemeSlider"
Minimum="0"
Value="0"
@ -213,8 +233,8 @@
ValueChanged="ThemeSlider_ValueChanged"
Width="447"
Style="{StaticResource ThreePositionSlider}"/>
</StackPanel>
</StackPanel>
</Grid>
</StackPanel>
<!-- Save Button -->

View file

@ -39,7 +39,6 @@ public partial class ConfigPage : UserControl
ApplyToggleModeStyle(OfflineModeSlider.Value, VisorWipeSlider.Value, VideoRecordSlider.Value);
const string themeJsonPath = "themes.json";
var themeJson = File.ReadAllText(themeJsonPath);
_themes = JsonSerializer.Deserialize<Dictionary<string, Theme>>(themeJson);
@ -78,23 +77,19 @@ public partial class ConfigPage : UserControl
}
// This method will set the loaded config values to the UI controls
public void SetConfigValues(string logFile, string apiUrl, string apiKey, string videoPath,
int visorWipe, int videoRecord, int offlineMode, int theme)
public void SetConfigValues(string logFile, string apiUrl, string apiKey, string videoPath, int visorWipe, int videoRecord, int offlineMode, int theme)
{
// Set the textboxes with the loaded values
LogFilePath.Text = logFile;
ApiUrl.Text = apiUrl;
ApiKey.Password = apiKey;
VideoPath.Text = videoPath;
// Set the sliders with the loaded values
VideoRecordSlider.Value = videoRecord;
VisorWipeSlider.Value = visorWipe;
VideoRecordSlider.Value = videoRecord;
OfflineModeSlider.Value = offlineMode;
ThemeSlider.Value = theme;
// Handle themes
ApplyTheme(theme);
}
private void ApplyToggleModeStyle(double offlineModeValue, double visorWipeValue, double videoRecordValue)
@ -202,18 +197,18 @@ public partial class ConfigPage : UserControl
// Video Path Browse Button Handler
private void VideoPathBrowseButton_Click(object sender, RoutedEventArgs e)
{
var dialog = new OpenFileDialog();
dialog.CheckFileExists = false;
var dialog = new Microsoft.Win32.OpenFileDialog();
dialog.ValidateNames = false;
dialog.Filter = "All files|*.*";
if (dialog.ShowDialog() == true && dialog.FileName != null)
dialog.CheckFileExists = false;
dialog.CheckPathExists = true;
dialog.FileName = "Folder Selection";
if (dialog.ShowDialog() == true)
{
// Extract only the directory path from the file
string? selectedFolder = Path.GetDirectoryName(dialog.FileName);
if (selectedFolder != null)
{
VideoPath.Text = selectedFolder; // Set the folder path
VideoPath.Text = selectedFolder;
}
}
}
@ -320,10 +315,8 @@ public partial class ConfigPage : UserControl
}
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
ConfigManager.ApiKey = ApiKey.Password;
ConfigManager.ApiUrl = ApiUrl.Text;
ConfigManager.LogFile = LogFilePath.Text;
@ -332,6 +325,7 @@ public partial class ConfigPage : UserControl
ConfigManager.VideoRecord = (int)VideoRecordSlider.Value;
ConfigManager.OfflineMode = (int)OfflineModeSlider.Value;
ConfigManager.Theme = (int)ThemeSlider.Value;
// Save the current config values
ConfigManager.SaveConfig();
// Start the flashing effect
@ -404,10 +398,11 @@ public partial class ConfigPage : UserControl
{
// Set headers
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.DefaultRequestHeaders.UserAgent.ParseAdd("AutoTrackR");
client.DefaultRequestHeaders.UserAgent.ParseAdd("AutoTrackR2");
// Empty JSON body
var content = new StringContent("{}", Encoding.UTF8, "application/json");
// Create JSON body with version
var jsonBody = new { version = "2.10" };
var content = new StringContent(JsonSerializer.Serialize(jsonBody), Encoding.UTF8, "application/json");
// Send POST
var response = await client.PostAsync(modifiedUrl, content);

View file

@ -42,19 +42,20 @@
</ScrollViewer>
</Border>
<!-- StackPanel for Start and Stop buttons -->
<!-- Border and StackPanel for player info -->
<Border Background="{DynamicResource BackgroundDarkBrush}"
BorderBrush="{DynamicResource AccentBrush}"
Grid.Row="0"
Grid.Column="1"
BorderThickness="2"
CornerRadius="5"
Margin="0,0,0,82"/>
<StackPanel Grid.Column="1"
VerticalAlignment="Center"
Margin="0,0,0,10"
VerticalAlignment="Top">
<StackPanel
VerticalAlignment="Top"
HorizontalAlignment="Center"
Height="269"
Width="152">
Width="152"
Margin="10,5,10,5">
<TextBlock Name="PilotNameTitle"
Text="Pilot"
Width="152"
@ -112,6 +113,25 @@
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"
@ -141,7 +161,8 @@
FontSize="8"
BorderThickness="0"
Margin="0,9,0,0"/>
</StackPanel>
</StackPanel>
</Border>
<StackPanel Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Center"

View file

@ -77,9 +77,11 @@ public partial class HomePage : UserControl
GameModeTextBox.Text = "Unknown";
PlayerShipTextBox.Text = "Unknown";
PilotNameTextBox.Text = "Unknown";
LocationTextBox.Text = "Unknown";
LocalPlayerData.CurrentGameMode = GameMode.Unknown;
LocalPlayerData.PlayerShip = string.Empty;
LocalPlayerData.Username = string.Empty;
LocalPlayerData.LastSeenVehicleLocation = "Unknown";
// Stop log monitoring if it's running
if (_isLogHandlerRunning)
@ -139,6 +141,8 @@ public partial class HomePage : UserControl
AdjustFontSize(PlayerShipTextBox);
LocalPlayerData.PlayerShip = data.ShipName;
LocalPlayerData.LastSeenVehicleLocation = data.Location;
LocationTextBox.Text = data.Location;
AdjustFontSize(LocationTextBox);
});
};
@ -216,7 +220,12 @@ public partial class HomePage : UserControl
// Vehicle Destruction
TrackREventDispatcher.VehicleDestructionEvent += (data) =>
{
LocalPlayerData.LastSeenVehicleLocation = data.VehicleZone;
Dispatcher.Invoke(() =>
{
LocalPlayerData.LastSeenVehicleLocation = data.VehicleZone;
LocationTextBox.Text = data.VehicleZone;
AdjustFontSize(LocationTextBox);
});
};
_UIEventsRegistered = true;
@ -224,67 +233,51 @@ public partial class HomePage : UserControl
private void AddKillToScreen(KillData killData)
{
// Fetch the dynamic resource for AltTextColor
var altTextColorBrush = new SolidColorBrush((Color)Application.Current.Resources["AltTextColor"]);
var accentColorBrush = new SolidColorBrush((Color)Application.Current.Resources["AccentColor"]);
// Fetch the Orbitron FontFamily from resources
var orbitronFontFamily = (FontFamily)Application.Current.Resources["Orbitron"];
var gemunuFontFamily = (FontFamily)Application.Current.Resources["Gemunu"];
// Create a new TextBlock for each kill
// Use resource references instead of creating new brushes
var killTextBlock = new TextBlock
{
Margin = new Thickness(0, 10, 0, 10),
Style = (Style)Application.Current.Resources["RoundedTextBlock"], // Apply style for text
Style = (Style)Application.Current.Resources["RoundedTextBlock"],
FontSize = 14,
FontWeight = FontWeights.Bold,
FontFamily = gemunuFontFamily,
FontFamily = (FontFamily)Application.Current.Resources["Gemunu"],
};
// Add styled content using Run elements
killTextBlock.Inlines.Add(new Run("Victim Name: ")
{
Foreground = altTextColorBrush,
FontFamily = orbitronFontFamily,
});
// Add styled content using Run elements with resource references
var titleRun = new Run("Victim Name: ");
titleRun.SetResourceReference(TextElement.ForegroundProperty, "AltTextBrush");
titleRun.FontFamily = (FontFamily)Application.Current.Resources["Orbitron"];
killTextBlock.Inlines.Add(titleRun);
killTextBlock.Inlines.Add(new Run($"{killData.EnemyPilot}\n"));
// Repeat for other lines
killTextBlock.Inlines.Add(new Run("Victim Ship: ")
{
Foreground = altTextColorBrush,
FontFamily = orbitronFontFamily,
});
titleRun = new Run("Victim Ship: ");
titleRun.SetResourceReference(TextElement.ForegroundProperty, "AltTextBrush");
titleRun.FontFamily = (FontFamily)Application.Current.Resources["Orbitron"];
killTextBlock.Inlines.Add(titleRun);
killTextBlock.Inlines.Add(new Run($"{killData.EnemyShip}\n"));
killTextBlock.Inlines.Add(new Run("Victim Org: ")
{
Foreground = altTextColorBrush,
FontFamily = orbitronFontFamily,
});
titleRun = new Run("Victim Org: ");
titleRun.SetResourceReference(TextElement.ForegroundProperty, "AltTextBrush");
titleRun.FontFamily = (FontFamily)Application.Current.Resources["Orbitron"];
killTextBlock.Inlines.Add(titleRun);
killTextBlock.Inlines.Add(new Run($"{killData.OrgAffiliation}\n"));
killTextBlock.Inlines.Add(new Run("Join Date: ")
{
Foreground = altTextColorBrush,
FontFamily = orbitronFontFamily,
});
titleRun = new Run("Join Date: ");
titleRun.SetResourceReference(TextElement.ForegroundProperty, "AltTextBrush");
titleRun.FontFamily = (FontFamily)Application.Current.Resources["Orbitron"];
killTextBlock.Inlines.Add(titleRun);
killTextBlock.Inlines.Add(new Run($"{killData.Enlisted}\n"));
killTextBlock.Inlines.Add(new Run("UEE Record: ")
{
Foreground = altTextColorBrush,
FontFamily = orbitronFontFamily,
});
titleRun = new Run("UEE Record: ");
titleRun.SetResourceReference(TextElement.ForegroundProperty, "AltTextBrush");
titleRun.FontFamily = (FontFamily)Application.Current.Resources["Orbitron"];
killTextBlock.Inlines.Add(titleRun);
killTextBlock.Inlines.Add(new Run($"{killData.RecordNumber}\n"));
killTextBlock.Inlines.Add(new Run("Kill Time: ")
{
Foreground = altTextColorBrush,
FontFamily = orbitronFontFamily,
});
titleRun = new Run("Kill Time: ");
titleRun.SetResourceReference(TextElement.ForegroundProperty, "AltTextBrush");
titleRun.FontFamily = (FontFamily)Application.Current.Resources["Orbitron"];
killTextBlock.Inlines.Add(titleRun);
killTextBlock.Inlines.Add(new Run($"{killData.KillTime}"));
// Create a Border and apply the RoundedTextBlockWithBorder style