mirror of
https://github.com/BubbaGumpShrump/AutoTrackR2.git
synced 2025-06-18 20:59:05 +00:00
Initial refactor to C# from PS
This commit is contained in:
parent
77a3c936fd
commit
a08de59a73
15 changed files with 855 additions and 300 deletions
.gitignore
AutoTrackR2
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -360,4 +360,7 @@ MigrationBackup/
|
||||||
.ionide/
|
.ionide/
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
### Rider ###
|
||||||
|
.idea/
|
|
@ -3,309 +3,313 @@ using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Effects;
|
using System.Windows.Media.Effects;
|
||||||
using System.IO;
|
|
||||||
using System.Windows.Documents;
|
using System.Windows.Documents;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
using AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
namespace AutoTrackR2
|
namespace AutoTrackR2;
|
||||||
|
|
||||||
|
public struct PlayerData
|
||||||
{
|
{
|
||||||
public partial class HomePage : UserControl
|
public string? PFPURL;
|
||||||
|
public string? UEERecord;
|
||||||
|
public string? OrgURL;
|
||||||
|
public string? OrgName;
|
||||||
|
public string? JoinDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class HomePage : UserControl
|
||||||
|
{
|
||||||
|
public HomePage()
|
||||||
{
|
{
|
||||||
public HomePage()
|
InitializeComponent();
|
||||||
|
|
||||||
|
// Get the current month
|
||||||
|
string currentMonth = DateTime.Now.ToString("MMMM", CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
// Set the TextBlock text
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var accentColor = (Color)Application.Current.Resources["AccentColor"];
|
||||||
|
|
||||||
|
if (isRunning)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
// Set Start button to "Running..." and apply glow effect
|
||||||
|
StartButton.Content = "Running...";
|
||||||
|
StartButton.IsEnabled = false; // Disable Start button
|
||||||
|
StartButton.Style = (Style)FindResource("DisabledButtonStyle");
|
||||||
|
|
||||||
// Get the current month
|
// Add glow effect to the Start button
|
||||||
string currentMonth = DateTime.Now.ToString("MMMM", CultureInfo.InvariantCulture);
|
StartButton.Effect = new DropShadowEffect
|
||||||
|
|
||||||
// Set the TextBlock text
|
|
||||||
KillTallyTitle.Text = $"Kill Tally - {currentMonth}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private Process runningProcess; // Field to store the running process
|
|
||||||
|
|
||||||
// Update Start/Stop button states based on the isRunning flag
|
|
||||||
public void UpdateButtonState(bool isRunning)
|
|
||||||
{
|
|
||||||
var accentColor = (Color)Application.Current.Resources["AccentColor"];
|
|
||||||
|
|
||||||
if (isRunning)
|
|
||||||
{
|
{
|
||||||
// Set Start button to "Running..." and apply glow effect
|
Color = accentColor,
|
||||||
StartButton.Content = "Running...";
|
BlurRadius = 30, // Adjust blur radius for desired glow intensity
|
||||||
StartButton.IsEnabled = false; // Disable Start button
|
ShadowDepth = 0, // Set shadow depth to 0 for a pure glow effect
|
||||||
StartButton.Style = (Style)FindResource("DisabledButtonStyle");
|
Opacity = 1, // Set opacity for glow visibility
|
||||||
|
Direction = 0 // Direction doesn't matter for glow
|
||||||
|
};
|
||||||
|
|
||||||
// Add glow effect to the Start button
|
StopButton.Style = (Style)FindResource("ButtonStyle");
|
||||||
StartButton.Effect = new DropShadowEffect
|
StopButton.IsEnabled = true; // Enable Stop button
|
||||||
{
|
|
||||||
Color = accentColor,
|
|
||||||
BlurRadius = 30, // Adjust blur radius for desired glow intensity
|
|
||||||
ShadowDepth = 0, // Set shadow depth to 0 for a pure glow effect
|
|
||||||
Opacity = 1, // Set opacity for glow visibility
|
|
||||||
Direction = 0 // Direction doesn't matter for glow
|
|
||||||
};
|
|
||||||
|
|
||||||
StopButton.Style = (Style)FindResource("ButtonStyle");
|
|
||||||
StopButton.IsEnabled = true; // Enable Stop button
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Reset Start button back to its original state
|
|
||||||
StartButton.Content = "Start";
|
|
||||||
StartButton.IsEnabled = true; // Enable Start button
|
|
||||||
|
|
||||||
// Remove the glow effect from Start button
|
|
||||||
StartButton.Effect = null;
|
|
||||||
|
|
||||||
StopButton.Style = (Style)FindResource("DisabledButtonStyle");
|
|
||||||
StartButton.Style = (Style)FindResource("ButtonStyle");
|
|
||||||
StopButton.IsEnabled = false; // Disable Stop button
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public void StartButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
{
|
||||||
UpdateButtonState(true);
|
// Reset Start button back to its original state
|
||||||
string scriptPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "KillTrackR_MainScript.ps1");
|
StartButton.Content = "Start";
|
||||||
TailFileAsync(scriptPath);
|
StartButton.IsEnabled = true; // Enable Start button
|
||||||
|
|
||||||
|
// Remove the glow effect from Start button
|
||||||
|
StartButton.Effect = null;
|
||||||
|
|
||||||
|
StopButton.Style = (Style)FindResource("DisabledButtonStyle");
|
||||||
|
StartButton.Style = (Style)FindResource("ButtonStyle");
|
||||||
|
StopButton.IsEnabled = false; // Disable Stop button
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RegisterUIEventHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
private async void TailFileAsync(string scriptPath)
|
public void StartButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await Task.Run(() =>
|
UpdateButtonState(true);
|
||||||
|
//string scriptPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "KillTrackR_MainScript.ps1");
|
||||||
|
// TailFileAsync(scriptPath);
|
||||||
|
|
||||||
|
// _logHandler = new LogHandler(@"U:\\StarCitizen\\StarCitizen\\LIVE\\Game.log");
|
||||||
|
_logHandler = new LogHandler(ConfigManager.LogFile);
|
||||||
|
_logHandler.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterUIEventHandlers()
|
||||||
|
{
|
||||||
|
if (_UIEventsRegistered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Username
|
||||||
|
TrackREventDispatcher.PlayerLoginEvent += (username) => {
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
{
|
{
|
||||||
try
|
PilotNameTextBox.Text = username;
|
||||||
{
|
AdjustFontSize(PilotNameTextBox);
|
||||||
ProcessStartInfo psi = new ProcessStartInfo
|
LocalPlayerData.Username = username;
|
||||||
{
|
|
||||||
FileName = "powershell.exe",
|
|
||||||
Arguments = $"-NoProfile -ExecutionPolicy Bypass -File \"{scriptPath}\"",
|
|
||||||
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory,
|
|
||||||
RedirectStandardOutput = true,
|
|
||||||
RedirectStandardError = true,
|
|
||||||
UseShellExecute = false,
|
|
||||||
CreateNoWindow = true
|
|
||||||
};
|
|
||||||
|
|
||||||
runningProcess = new Process { StartInfo = psi }; // Store the process in the field
|
|
||||||
|
|
||||||
runningProcess.OutputDataReceived += (s, e) =>
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(e.Data))
|
|
||||||
{
|
|
||||||
Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
// Parse and display key-value pairs in the OutputTextBox
|
|
||||||
if (e.Data.Contains("PlayerName="))
|
|
||||||
{
|
|
||||||
string pilotName = e.Data.Split('=')[1].Trim();
|
|
||||||
PilotNameTextBox.Text = pilotName; // Update the Button's Content
|
|
||||||
AdjustFontSize(PilotNameTextBox);
|
|
||||||
}
|
|
||||||
else if (e.Data.Contains("PlayerShip="))
|
|
||||||
{
|
|
||||||
string playerShip = e.Data.Split('=')[1].Trim();
|
|
||||||
PlayerShipTextBox.Text = playerShip;
|
|
||||||
AdjustFontSize(PlayerShipTextBox);
|
|
||||||
}
|
|
||||||
else if (e.Data.Contains("GameMode="))
|
|
||||||
{
|
|
||||||
string gameMode = e.Data.Split('=')[1].Trim();
|
|
||||||
GameModeTextBox.Text = gameMode;
|
|
||||||
AdjustFontSize(GameModeTextBox);
|
|
||||||
}
|
|
||||||
else if (e.Data.Contains("KillTally="))
|
|
||||||
{
|
|
||||||
string killTally = e.Data.Split('=')[1].Trim();
|
|
||||||
KillTallyTextBox.Text = killTally;
|
|
||||||
AdjustFontSize(KillTallyTextBox);
|
|
||||||
}
|
|
||||||
else if (e.Data.Contains("NewKill="))
|
|
||||||
{
|
|
||||||
// Parse the kill data
|
|
||||||
var killData = e.Data.Split('=')[1].Trim(); // Assume the kill data follows after "NewKill="
|
|
||||||
var killParts = killData.Split(',');
|
|
||||||
|
|
||||||
// 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
|
|
||||||
var killTextBlock = new TextBlock
|
|
||||||
{
|
|
||||||
Margin = new Thickness(0, 10, 0, 10),
|
|
||||||
Style = (Style)Application.Current.Resources["RoundedTextBlock"], // Apply style for text
|
|
||||||
FontSize = 14,
|
|
||||||
FontWeight = FontWeights.Bold,
|
|
||||||
FontFamily = gemunuFontFamily,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add styled content using Run elements
|
|
||||||
killTextBlock.Inlines.Add(new Run("Victim Name: ")
|
|
||||||
{
|
|
||||||
Foreground = altTextColorBrush,
|
|
||||||
FontFamily = orbitronFontFamily,
|
|
||||||
});
|
|
||||||
killTextBlock.Inlines.Add(new Run($"{killParts[1]}\n"));
|
|
||||||
|
|
||||||
// Repeat for other lines
|
|
||||||
killTextBlock.Inlines.Add(new Run("Victim Ship: ")
|
|
||||||
{
|
|
||||||
Foreground = altTextColorBrush,
|
|
||||||
FontFamily = orbitronFontFamily,
|
|
||||||
});
|
|
||||||
killTextBlock.Inlines.Add(new Run($"{killParts[2]}\n"));
|
|
||||||
|
|
||||||
killTextBlock.Inlines.Add(new Run("Victim Org: ")
|
|
||||||
{
|
|
||||||
Foreground = altTextColorBrush,
|
|
||||||
FontFamily = orbitronFontFamily,
|
|
||||||
});
|
|
||||||
killTextBlock.Inlines.Add(new Run($"{killParts[3]}\n"));
|
|
||||||
|
|
||||||
killTextBlock.Inlines.Add(new Run("Join Date: ")
|
|
||||||
{
|
|
||||||
Foreground = altTextColorBrush,
|
|
||||||
FontFamily = orbitronFontFamily,
|
|
||||||
});
|
|
||||||
killTextBlock.Inlines.Add(new Run($"{killParts[4]}\n"));
|
|
||||||
|
|
||||||
killTextBlock.Inlines.Add(new Run("UEE Record: ")
|
|
||||||
{
|
|
||||||
Foreground = altTextColorBrush,
|
|
||||||
FontFamily = orbitronFontFamily,
|
|
||||||
});
|
|
||||||
killTextBlock.Inlines.Add(new Run($"{killParts[5]}\n"));
|
|
||||||
|
|
||||||
killTextBlock.Inlines.Add(new Run("Kill Time: ")
|
|
||||||
{
|
|
||||||
Foreground = altTextColorBrush,
|
|
||||||
FontFamily = orbitronFontFamily,
|
|
||||||
});
|
|
||||||
killTextBlock.Inlines.Add(new Run($"{killParts[6]}"));
|
|
||||||
|
|
||||||
// Create a Border and apply the RoundedTextBlockWithBorder style
|
|
||||||
var killBorder = new Border
|
|
||||||
{
|
|
||||||
Style = (Style)Application.Current.Resources["RoundedTextBlockWithBorder"], // Apply border style
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a Grid to hold the TextBlock and the Image
|
|
||||||
var killGrid = new Grid
|
|
||||||
{
|
|
||||||
Width = 400, // Adjust the width of the Grid
|
|
||||||
Height = 130, // Adjust the height as needed
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define two columns in the Grid: one for the text and one for the image
|
|
||||||
killGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(3, GridUnitType.Star) }); // Text column
|
|
||||||
killGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) }); // Image column
|
|
||||||
|
|
||||||
// Add the TextBlock to the first column of the Grid
|
|
||||||
Grid.SetColumn(killTextBlock, 0);
|
|
||||||
killGrid.Children.Add(killTextBlock);
|
|
||||||
|
|
||||||
// Create the Image for the profile
|
|
||||||
var profileImage = new Image
|
|
||||||
{
|
|
||||||
Source = new BitmapImage(new Uri(killParts[7])), // Assuming the 8th part contains the profile image URL
|
|
||||||
Width = 90,
|
|
||||||
Height = 90,
|
|
||||||
Stretch = Stretch.Fill, // Adjust how the image fits
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a Border around the Image
|
|
||||||
var imageBorder = new Border
|
|
||||||
{
|
|
||||||
BorderBrush = accentColorBrush, // Set the border color
|
|
||||||
BorderThickness = new Thickness(2), // Set the border thickness
|
|
||||||
Padding = new Thickness(0), // Optional padding inside the border
|
|
||||||
CornerRadius = new CornerRadius(5),
|
|
||||||
Margin = new Thickness(10,18,15,18),
|
|
||||||
Child = profileImage // Set the Image as the content of the Border
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the Border (with the image inside) to the Grid
|
|
||||||
Grid.SetColumn(imageBorder, 1);
|
|
||||||
killGrid.Children.Add(imageBorder);
|
|
||||||
|
|
||||||
// Set the Grid as the child of the Border
|
|
||||||
killBorder.Child = killGrid;
|
|
||||||
|
|
||||||
// Add the new Border to the StackPanel inside the Border
|
|
||||||
KillFeedStackPanel.Children.Insert(0, killBorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DebugPanel.AppendText(e.Data + Environment.NewLine);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
runningProcess.ErrorDataReceived += (s, e) =>
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(e.Data))
|
|
||||||
{
|
|
||||||
Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
DebugPanel.AppendText(e.Data + Environment.NewLine);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
runningProcess.Start();
|
|
||||||
runningProcess.BeginOutputReadLine();
|
|
||||||
runningProcess.BeginErrorReadLine();
|
|
||||||
|
|
||||||
runningProcess.WaitForExit();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
MessageBox.Show($"Error running script: {ex.Message}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
public void StopButton_Click(object sender, RoutedEventArgs e)
|
// Ship
|
||||||
{
|
TrackREventDispatcher.InstancedInteriorEvent += (data) => {
|
||||||
if (runningProcess != null && !runningProcess.HasExited)
|
if (data.OwnerGEID == LocalPlayerData.Username && data.Ship != null)
|
||||||
{
|
{
|
||||||
// Kill the running process
|
Dispatcher.Invoke(() =>
|
||||||
runningProcess.Kill();
|
{
|
||||||
runningProcess = null; // Clear the reference to the process
|
PlayerShipTextBox.Text = data.Ship;
|
||||||
|
AdjustFontSize(PlayerShipTextBox);
|
||||||
|
LocalPlayerData.PlayerShip = data.Ship;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Game Mode
|
||||||
|
TrackREventDispatcher.PlayerChangedGameModeEvent += (mode) => {
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
GameModeTextBox.Text = mode.ToString();
|
||||||
|
AdjustFontSize(GameModeTextBox);
|
||||||
|
LocalPlayerData.CurrentGameMode = mode;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Game Version
|
||||||
|
TrackREventDispatcher.GameVersionEvent += (version) => {
|
||||||
|
LocalPlayerData.GameVersion = version;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Actor Death
|
||||||
|
TrackREventDispatcher.ActorDeathEvent += async (data) => {
|
||||||
|
if (data.VictimPilot != LocalPlayerData.Username)
|
||||||
|
{
|
||||||
|
var playerData = await WebHandler.GetPlayerData(data.VictimPilot);
|
||||||
|
|
||||||
// Clear the text boxes
|
if (playerData != null)
|
||||||
System.Threading.Thread.Sleep(200);
|
{
|
||||||
PilotNameTextBox.Text = string.Empty;
|
Dispatcher.Invoke(() => { AddKillToScreen(data, playerData); });
|
||||||
PlayerShipTextBox.Text = string.Empty;
|
await WebHandler.SubmitKill(data, playerData);
|
||||||
GameModeTextBox.Text = string.Empty;
|
}
|
||||||
KillTallyTextBox.Text = string.Empty;
|
}
|
||||||
KillFeedStackPanel.Children.Clear();
|
};
|
||||||
}
|
|
||||||
|
_UIEventsRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void AdjustFontSize(TextBlock textBlock)
|
private void AddKillToScreen(ActorDeathData deathData, PlayerData? playerData)
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
var killTextBlock = new TextBlock
|
||||||
{
|
{
|
||||||
// Set a starting font size
|
Margin = new Thickness(0, 10, 0, 10),
|
||||||
double fontSize = 14;
|
Style = (Style)Application.Current.Resources["RoundedTextBlock"], // Apply style for text
|
||||||
double maxWidth = textBlock.Width;
|
FontSize = 14,
|
||||||
|
FontWeight = FontWeights.Bold,
|
||||||
|
FontFamily = gemunuFontFamily,
|
||||||
|
};
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(textBlock.Text) || double.IsNaN(maxWidth))
|
// Add styled content using Run elements
|
||||||
return;
|
killTextBlock.Inlines.Add(new Run("Victim Name: ")
|
||||||
|
{
|
||||||
|
Foreground = altTextColorBrush,
|
||||||
|
FontFamily = orbitronFontFamily,
|
||||||
|
});
|
||||||
|
killTextBlock.Inlines.Add(new Run($"{deathData.VictimPilot}\n"));
|
||||||
|
|
||||||
// Measure the rendered width of the text
|
// Repeat for other lines
|
||||||
FormattedText formattedText = new FormattedText(
|
killTextBlock.Inlines.Add(new Run("Victim Ship: ")
|
||||||
|
{
|
||||||
|
Foreground = altTextColorBrush,
|
||||||
|
FontFamily = orbitronFontFamily,
|
||||||
|
});
|
||||||
|
killTextBlock.Inlines.Add(new Run($"{deathData.VictimShip}\n"));
|
||||||
|
|
||||||
|
killTextBlock.Inlines.Add(new Run("Victim Org: ")
|
||||||
|
{
|
||||||
|
Foreground = altTextColorBrush,
|
||||||
|
FontFamily = orbitronFontFamily,
|
||||||
|
});
|
||||||
|
killTextBlock.Inlines.Add(new Run($"{playerData?.OrgName}\n"));
|
||||||
|
|
||||||
|
killTextBlock.Inlines.Add(new Run("Join Date: ")
|
||||||
|
{
|
||||||
|
Foreground = altTextColorBrush,
|
||||||
|
FontFamily = orbitronFontFamily,
|
||||||
|
});
|
||||||
|
killTextBlock.Inlines.Add(new Run($"{playerData?.JoinDate}\n"));
|
||||||
|
|
||||||
|
killTextBlock.Inlines.Add(new Run("UEE Record: ")
|
||||||
|
{
|
||||||
|
Foreground = altTextColorBrush,
|
||||||
|
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($"{currentTime}"));
|
||||||
|
|
||||||
|
// Create a Border and apply the RoundedTextBlockWithBorder style
|
||||||
|
var killBorder = new Border
|
||||||
|
{
|
||||||
|
Style = (Style)Application.Current.Resources["RoundedTextBlockWithBorder"], // Apply border style
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a Grid to hold the TextBlock and the Image
|
||||||
|
var killGrid = new Grid
|
||||||
|
{
|
||||||
|
Width = 400, // Adjust the width of the Grid
|
||||||
|
Height = 130, // Adjust the height as needed
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define two columns in the Grid: one for the text and one for the image
|
||||||
|
killGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(3, GridUnitType.Star) }); // Text column
|
||||||
|
killGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Auto) }); // Image column
|
||||||
|
|
||||||
|
// Add the TextBlock to the first column of the Grid
|
||||||
|
Grid.SetColumn(killTextBlock, 0);
|
||||||
|
killGrid.Children.Add(killTextBlock);
|
||||||
|
|
||||||
|
// Create the Image for the profile
|
||||||
|
var profileImage = new Image
|
||||||
|
{
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a Border around the Image
|
||||||
|
var imageBorder = new Border
|
||||||
|
{
|
||||||
|
BorderBrush = accentColorBrush, // Set the border color
|
||||||
|
BorderThickness = new Thickness(2), // Set the border thickness
|
||||||
|
Padding = new Thickness(0), // Optional padding inside the border
|
||||||
|
CornerRadius = new CornerRadius(5),
|
||||||
|
Margin = new Thickness(10,18,15,18),
|
||||||
|
Child = profileImage // Set the Image as the content of the Border
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the Border (with the image inside) to the Grid
|
||||||
|
Grid.SetColumn(imageBorder, 1);
|
||||||
|
killGrid.Children.Add(imageBorder);
|
||||||
|
|
||||||
|
// Set the Grid as the child of the Border
|
||||||
|
killBorder.Child = killGrid;
|
||||||
|
|
||||||
|
// Add the new Border to the StackPanel inside the Border
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
KillFeedStackPanel.Children.Insert(0, killBorder);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AdjustFontSize(TextBlock textBlock)
|
||||||
|
{
|
||||||
|
// Set a starting font size
|
||||||
|
double fontSize = 14;
|
||||||
|
double maxWidth = textBlock.Width;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(textBlock.Text) || double.IsNaN(maxWidth))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Measure the rendered width of the text
|
||||||
|
FormattedText formattedText = new FormattedText(
|
||||||
|
textBlock.Text,
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
FlowDirection.LeftToRight,
|
||||||
|
new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch),
|
||||||
|
fontSize,
|
||||||
|
textBlock.Foreground,
|
||||||
|
VisualTreeHelper.GetDpi(this).PixelsPerDip
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reduce font size until text fits within the width
|
||||||
|
while (formattedText.Width > maxWidth && fontSize > 6)
|
||||||
|
{
|
||||||
|
fontSize -= 0.5;
|
||||||
|
formattedText = new FormattedText(
|
||||||
textBlock.Text,
|
textBlock.Text,
|
||||||
CultureInfo.CurrentCulture,
|
CultureInfo.CurrentCulture,
|
||||||
FlowDirection.LeftToRight,
|
FlowDirection.LeftToRight,
|
||||||
|
@ -314,24 +318,9 @@ namespace AutoTrackR2
|
||||||
textBlock.Foreground,
|
textBlock.Foreground,
|
||||||
VisualTreeHelper.GetDpi(this).PixelsPerDip
|
VisualTreeHelper.GetDpi(this).PixelsPerDip
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reduce font size until text fits within the width
|
|
||||||
while (formattedText.Width > maxWidth && fontSize > 6)
|
|
||||||
{
|
|
||||||
fontSize -= 0.5;
|
|
||||||
formattedText = new FormattedText(
|
|
||||||
textBlock.Text,
|
|
||||||
CultureInfo.CurrentCulture,
|
|
||||||
FlowDirection.LeftToRight,
|
|
||||||
new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch),
|
|
||||||
fontSize,
|
|
||||||
textBlock.Foreground,
|
|
||||||
VisualTreeHelper.GetDpi(this).PixelsPerDip
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the adjusted font size
|
|
||||||
textBlock.FontSize = fontSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply the adjusted font size
|
||||||
|
textBlock.FontSize = fontSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
AutoTrackR2/LocalPlayerData.cs
Normal file
17
AutoTrackR2/LocalPlayerData.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
namespace AutoTrackR2;
|
||||||
|
|
||||||
|
|
||||||
|
public enum GameMode
|
||||||
|
{
|
||||||
|
ArenaCommander,
|
||||||
|
PersistentUniverse
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LocalPlayerData
|
||||||
|
{
|
||||||
|
public static string? Username;
|
||||||
|
public static string? PlayerShip;
|
||||||
|
public static string? GameVersion;
|
||||||
|
public static GameMode CurrentGameMode;
|
||||||
|
public static string? LastSeenVehicleLocation;
|
||||||
|
}
|
59
AutoTrackR2/LogEventHandlers/ActorDeathEvent.cs
Normal file
59
AutoTrackR2/LogEventHandlers/ActorDeathEvent.cs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
public struct ActorDeathData
|
||||||
|
{
|
||||||
|
public string VictimPilot;
|
||||||
|
public string VictimShip;
|
||||||
|
public string Player;
|
||||||
|
public string Weapon;
|
||||||
|
public string Class;
|
||||||
|
public string DamageType;
|
||||||
|
public string Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActorDeathEvent : ILogEventHandler
|
||||||
|
{
|
||||||
|
public Regex Pattern { get; }
|
||||||
|
public ActorDeathEvent()
|
||||||
|
{
|
||||||
|
Pattern = new Regex(@"<Actor Death> CActor::Kill: '(?<EnemyPilot>[^']+)' \[\d+\] in zone '(?<EnemyShip>[^']+)' killed by '(?<Player>[^']+)' \[[^']+\] using '(?<Weapon>[^']+)' \[Class (?<Class>[^\]]+)\] with damage type '(?<DamageType>[^']+)");
|
||||||
|
}
|
||||||
|
|
||||||
|
Regex cleanUpPattern = new Regex(@"^(.+?)_\d+$");
|
||||||
|
|
||||||
|
public void Handle(LogEntry entry)
|
||||||
|
{
|
||||||
|
if (entry.Message is null) return;
|
||||||
|
|
||||||
|
var match = Pattern.Match(entry.Message);
|
||||||
|
if (!match.Success) return;
|
||||||
|
|
||||||
|
var data = new ActorDeathData {
|
||||||
|
VictimPilot = match.Groups["EnemyPilot"].Value,
|
||||||
|
VictimShip = match.Groups["EnemyShip"].Value,
|
||||||
|
Player = match.Groups["Player"].Value,
|
||||||
|
Weapon = match.Groups["Weapon"].Value,
|
||||||
|
Class = match.Groups["Class"].Value,
|
||||||
|
DamageType = match.Groups["DamageType"].Value,
|
||||||
|
Timestamp = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss")
|
||||||
|
};
|
||||||
|
|
||||||
|
if (cleanUpPattern.IsMatch(data.VictimShip))
|
||||||
|
{
|
||||||
|
data.VictimShip = cleanUpPattern.Match(data.VictimShip).Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanUpPattern.IsMatch(data.Weapon))
|
||||||
|
{
|
||||||
|
data.Weapon = cleanUpPattern.Match(data.Weapon).Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TrackREventDispatcher.OnActorDeathEvent(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
21
AutoTrackR2/LogEventHandlers/GameVersionEvent.cs
Normal file
21
AutoTrackR2/LogEventHandlers/GameVersionEvent.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
public class GameVersionEvent : ILogEventHandler
|
||||||
|
{
|
||||||
|
public Regex Pattern { get; }
|
||||||
|
|
||||||
|
public GameVersionEvent()
|
||||||
|
{
|
||||||
|
Pattern = new Regex(@"--system-trace-env-id='pub-sc-alpha-(?<GameVersion>\d{3,4}-\d{7})'");
|
||||||
|
}
|
||||||
|
public void Handle(LogEntry entry)
|
||||||
|
{
|
||||||
|
if (entry.Message is null) return;
|
||||||
|
var match = Pattern.Match(entry.Message);
|
||||||
|
if (!match.Success) return;
|
||||||
|
|
||||||
|
TrackREventDispatcher.OnGameVersionEvent(match.Groups["GameVersion"].Value);
|
||||||
|
}
|
||||||
|
}
|
10
AutoTrackR2/LogEventHandlers/ILogEventHandler.cs
Normal file
10
AutoTrackR2/LogEventHandlers/ILogEventHandler.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
public interface ILogEventHandler
|
||||||
|
{
|
||||||
|
Regex Pattern { get; }
|
||||||
|
void Handle(LogEntry entry);
|
||||||
|
|
||||||
|
}
|
22
AutoTrackR2/LogEventHandlers/InArenaCommanderEvent.cs
Normal file
22
AutoTrackR2/LogEventHandlers/InArenaCommanderEvent.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
public class InArenaCommanderEvent : ILogEventHandler
|
||||||
|
{
|
||||||
|
public Regex Pattern { get; }
|
||||||
|
|
||||||
|
public InArenaCommanderEvent()
|
||||||
|
{
|
||||||
|
Pattern = new Regex("Requesting Mode Change");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(LogEntry entry)
|
||||||
|
{
|
||||||
|
if (entry.Message is null) return;
|
||||||
|
var match = Pattern.Match(entry.Message);
|
||||||
|
if (!match.Success) return;
|
||||||
|
|
||||||
|
TrackREventDispatcher.OnPlayerChangedGameModeEvent(GameMode.ArenaCommander);
|
||||||
|
}
|
||||||
|
}
|
22
AutoTrackR2/LogEventHandlers/InPersistentUniverseEvent.cs
Normal file
22
AutoTrackR2/LogEventHandlers/InPersistentUniverseEvent.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
public class InPersistentUniverseEvent : ILogEventHandler
|
||||||
|
{
|
||||||
|
public Regex Pattern { get; }
|
||||||
|
|
||||||
|
public InPersistentUniverseEvent()
|
||||||
|
{
|
||||||
|
Pattern = new Regex(@"<\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\]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(LogEntry entry)
|
||||||
|
{
|
||||||
|
if (entry.Message is null) return;
|
||||||
|
var match = Pattern.Match(entry.Message);
|
||||||
|
if (!match.Success) return;
|
||||||
|
|
||||||
|
TrackREventDispatcher.OnPlayerChangedGameModeEvent(GameMode.PersistentUniverse);
|
||||||
|
}
|
||||||
|
}
|
77
AutoTrackR2/LogEventHandlers/InstancedInteriorEvent.cs
Normal file
77
AutoTrackR2/LogEventHandlers/InstancedInteriorEvent.cs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
public struct InstancedInteriorData
|
||||||
|
{
|
||||||
|
public string Entity;
|
||||||
|
public string OwnerGEID;
|
||||||
|
public string ManagerGEID;
|
||||||
|
public string InstancedInterior;
|
||||||
|
public string? Ship;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ship loadout has been changed
|
||||||
|
public class InstancedInteriorEvent : ILogEventHandler
|
||||||
|
{
|
||||||
|
public Regex Pattern { get; }
|
||||||
|
|
||||||
|
private Regex _shipManufacturerPattern;
|
||||||
|
private Regex _cleanUpPattern = new Regex(@"(.+?)_\d+$");
|
||||||
|
|
||||||
|
private List<string> _shipManufacturers = new List<string>
|
||||||
|
{
|
||||||
|
"ORIG",
|
||||||
|
"CRUS",
|
||||||
|
"RSI",
|
||||||
|
"AEGS",
|
||||||
|
"VNCL",
|
||||||
|
"DRAK",
|
||||||
|
"ANVL",
|
||||||
|
"BANU",
|
||||||
|
"MISC",
|
||||||
|
"CNOU",
|
||||||
|
"XIAN",
|
||||||
|
"GAMA",
|
||||||
|
"TMBL",
|
||||||
|
"ESPR",
|
||||||
|
"KRIG",
|
||||||
|
"GRIN",
|
||||||
|
"XNAA",
|
||||||
|
"MRAI"
|
||||||
|
};
|
||||||
|
|
||||||
|
public InstancedInteriorEvent()
|
||||||
|
{
|
||||||
|
Pattern = new Regex(@"\[InstancedInterior\] OnEntityLeaveZone - InstancedInterior \[(?<InstancedInterior>[^\]]+)\] \[\d+\] -> Entity \[(?<Entity>[^\]]+)\] \[\d+\] -- m_openDoors\[\d+\], m_managerGEID\[(?<ManagerGEID>\d+)\], m_ownerGEID\[(?<OwnerGEID>[^\[]+)\]");
|
||||||
|
_shipManufacturerPattern = new Regex($"^({string.Join("|", _shipManufacturers)})");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(LogEntry entry)
|
||||||
|
{
|
||||||
|
if (entry.Message is null) return;
|
||||||
|
var match = Pattern.Match(entry.Message);
|
||||||
|
if (!match.Success) return;
|
||||||
|
|
||||||
|
var data = new InstancedInteriorData {
|
||||||
|
Entity = match.Groups["Entity"].Value,
|
||||||
|
OwnerGEID = match.Groups["OwnerGEID"].Value,
|
||||||
|
ManagerGEID = match.Groups["ManagerGEID"].Value,
|
||||||
|
InstancedInterior = match.Groups["InstancedInterior"].Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
match = _shipManufacturerPattern.Match(data.Entity);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
match = _cleanUpPattern.Match(data.Entity);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
data.Ship = match.Groups[1].Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackREventDispatcher.OnInstancedInteriorEvent(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
AutoTrackR2/LogEventHandlers/LoginEvent.cs
Normal file
24
AutoTrackR2/LogEventHandlers/LoginEvent.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
// Local player has logged in
|
||||||
|
public class LoginEvent : ILogEventHandler
|
||||||
|
{
|
||||||
|
public Regex Pattern { get; }
|
||||||
|
|
||||||
|
public LoginEvent()
|
||||||
|
{
|
||||||
|
Pattern = new Regex(@"\[Notice\] <Legacy login response> \[CIG-net\] User Login Success - Handle\[(?<Player>[A-Za-z0-9_-]+)\]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(LogEntry entry)
|
||||||
|
{
|
||||||
|
if (entry.Message is null) return;
|
||||||
|
|
||||||
|
var match = Pattern.Match(entry.Message);
|
||||||
|
if (!match.Success) return;
|
||||||
|
|
||||||
|
TrackREventDispatcher.OnPlayerLoginEvent(match.Groups["Player"].Value);
|
||||||
|
}
|
||||||
|
}
|
59
AutoTrackR2/LogEventHandlers/VehicleDestructionEvent.cs
Normal file
59
AutoTrackR2/LogEventHandlers/VehicleDestructionEvent.cs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
public struct VehicleDestructionData
|
||||||
|
{
|
||||||
|
public string Vehicle { get; set; }
|
||||||
|
public string VehicleZone { get; set; }
|
||||||
|
public float PosX { get; set; }
|
||||||
|
public float PosY { get; set; }
|
||||||
|
public float PosZ { get; set; }
|
||||||
|
public string Driver { get; set; }
|
||||||
|
public int DestroyLevelFrom { get; set; }
|
||||||
|
public int DestroyLevelTo { get; set; }
|
||||||
|
public string CausedBy { get; set; }
|
||||||
|
public string DamageType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VehicleDestructionEvent : ILogEventHandler
|
||||||
|
{
|
||||||
|
public Regex Pattern { get; }
|
||||||
|
|
||||||
|
public VehicleDestructionEvent()
|
||||||
|
{
|
||||||
|
Pattern = new Regex("""
|
||||||
|
"<(?<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>[^']+)'"
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(LogEntry entry)
|
||||||
|
{
|
||||||
|
var match = Pattern.Match(entry.Message);
|
||||||
|
if (!match.Success)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = new VehicleDestructionData
|
||||||
|
{
|
||||||
|
Vehicle = match.Groups["vehicle"].Value,
|
||||||
|
VehicleZone = match.Groups["vehicle_zone"].Value,
|
||||||
|
PosX = float.Parse(match.Groups["pos_x"].Value),
|
||||||
|
PosY = float.Parse(match.Groups["pos_y"].Value),
|
||||||
|
PosZ = float.Parse(match.Groups["pos_z"].Value),
|
||||||
|
Driver = match.Groups["driver"].Value,
|
||||||
|
DestroyLevelFrom = int.Parse(match.Groups["destroy_level_from"].Value),
|
||||||
|
DestroyLevelTo = int.Parse(match.Groups["destroy_level_to"].Value),
|
||||||
|
CausedBy = match.Groups["caused_by"].Value,
|
||||||
|
DamageType = match.Groups["damage_type"].Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
TrackREventDispatcher.OnVehicleDestructionEvent(data);
|
||||||
|
}
|
||||||
|
}
|
101
AutoTrackR2/LogHandler.cs
Normal file
101
AutoTrackR2/LogHandler.cs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
namespace AutoTrackR2;
|
||||||
|
|
||||||
|
|
||||||
|
// Represents a single log entry
|
||||||
|
// This is the object that will be passed to each handler, mostly for convenience
|
||||||
|
public class LogEntry
|
||||||
|
{
|
||||||
|
public DateTime Timestamp { get; set; }
|
||||||
|
public required string? Message { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
public class LogHandler(string logPath)
|
||||||
|
{
|
||||||
|
private readonly string? _logPath = logPath;
|
||||||
|
private FileStream? _fileStream;
|
||||||
|
private StreamReader? _reader;
|
||||||
|
|
||||||
|
private CancellationTokenSource cancellationToken = new CancellationTokenSource();
|
||||||
|
Thread? monitorThread;
|
||||||
|
|
||||||
|
// Handlers that should be run on every log entry
|
||||||
|
// Overlap with _startupEventHandlers is fine
|
||||||
|
private readonly List<ILogEventHandler> _eventHandlers = [
|
||||||
|
new LoginEvent(),
|
||||||
|
new InstancedInteriorEvent(),
|
||||||
|
new InArenaCommanderEvent(),
|
||||||
|
new InPersistentUniverseEvent(),
|
||||||
|
new GameVersionEvent(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize the LogHandler and run all startup handlers
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
if (!File.Exists(_logPath))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("Log file not found", _logPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileStream = new FileStream(_logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
_reader = new StreamReader(_fileStream);
|
||||||
|
|
||||||
|
while (_reader.ReadLine() is { } line)
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
|
||||||
|
monitorThread = new Thread(() => MonitorLog(cancellationToken.Token));
|
||||||
|
monitorThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
// Stop the monitor thread
|
||||||
|
cancellationToken?.Cancel();
|
||||||
|
_reader?.Close();
|
||||||
|
_fileStream?.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a single line of the log file and run matching handlers
|
||||||
|
private void HandleLogEntry(string line)
|
||||||
|
{
|
||||||
|
foreach (var handler in _eventHandlers)
|
||||||
|
{
|
||||||
|
var match = handler.Pattern.Match(line);
|
||||||
|
if (!match.Success) continue;
|
||||||
|
|
||||||
|
var entry = new LogEntry
|
||||||
|
{
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Message = line
|
||||||
|
};
|
||||||
|
handler.Handle(entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MonitorLog(CancellationToken token)
|
||||||
|
{
|
||||||
|
while (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (_reader?.ReadLine() is { } line)
|
||||||
|
{
|
||||||
|
HandleLogEntry(line);
|
||||||
|
Console.WriteLine(line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Wait for new lines to be written to the log file
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine("Monitor thread stopped");
|
||||||
|
}
|
||||||
|
}
|
49
AutoTrackR2/TrackREventDispatcher.cs
Normal file
49
AutoTrackR2/TrackREventDispatcher.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
namespace AutoTrackR2;
|
||||||
|
|
||||||
|
public static class TrackREventDispatcher
|
||||||
|
{
|
||||||
|
// Local Player Login
|
||||||
|
public static event Action<string>? PlayerLoginEvent;
|
||||||
|
public static void OnPlayerLoginEvent(string playerName)
|
||||||
|
{
|
||||||
|
PlayerLoginEvent?.Invoke(playerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// An instanced interior has changed
|
||||||
|
// Example: Player enters/leaves a ship
|
||||||
|
public static event Action<InstancedInteriorData>? InstancedInteriorEvent;
|
||||||
|
public static void OnInstancedInteriorEvent(InstancedInteriorData data)
|
||||||
|
{
|
||||||
|
InstancedInteriorEvent?.Invoke(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player changed GameMode (AC or PU)
|
||||||
|
public static event Action<GameMode>? PlayerChangedGameModeEvent;
|
||||||
|
public static void OnPlayerChangedGameModeEvent(GameMode mode)
|
||||||
|
{
|
||||||
|
PlayerChangedGameModeEvent?.Invoke(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game version has been detected
|
||||||
|
public static event Action<string>? GameVersionEvent;
|
||||||
|
public static void OnGameVersionEvent(string value)
|
||||||
|
{
|
||||||
|
GameVersionEvent?.Invoke(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actor has died
|
||||||
|
public static event Action<ActorDeathData>? ActorDeathEvent;
|
||||||
|
public static void OnActorDeathEvent(ActorDeathData data)
|
||||||
|
{
|
||||||
|
ActorDeathEvent?.Invoke(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vehicle has been destroyed
|
||||||
|
public static event Action<VehicleDestructionData>? VehicleDestructionEvent;
|
||||||
|
public static void OnVehicleDestructionEvent(VehicleDestructionData data)
|
||||||
|
{
|
||||||
|
VehicleDestructionEvent?.Invoke(data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ namespace AutoTrackR2
|
||||||
{
|
{
|
||||||
public partial class UpdatePage : UserControl
|
public partial class UpdatePage : UserControl
|
||||||
{
|
{
|
||||||
private string currentVersion = "v2.07";
|
public static string currentVersion = "v2.08";
|
||||||
private string latestVersion;
|
private string latestVersion;
|
||||||
|
|
||||||
public UpdatePage()
|
public UpdatePage()
|
||||||
|
|
102
AutoTrackR2/WebHandler.cs
Normal file
102
AutoTrackR2/WebHandler.cs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using AutoTrackR2.LogEventHandlers;
|
||||||
|
|
||||||
|
namespace AutoTrackR2;
|
||||||
|
|
||||||
|
public static class WebHandler
|
||||||
|
{
|
||||||
|
class APIKillData
|
||||||
|
{
|
||||||
|
public string? victim_ship { get; set; }
|
||||||
|
public string? victim{ get; set; }
|
||||||
|
public string? enlisted{ get; set; }
|
||||||
|
public string? rsi{ get; set; }
|
||||||
|
public string? weapon{ get; set; }
|
||||||
|
public string? method{ get; set; }
|
||||||
|
public string? loadout_ship{ get; set; }
|
||||||
|
public string? game_version{ get; set; }
|
||||||
|
public string? gamemode{ get; set; }
|
||||||
|
public string? trackr_version{ get; set; }
|
||||||
|
public string? location{ get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<PlayerData?> GetPlayerData(string enemyPilot)
|
||||||
|
{
|
||||||
|
var joinDataPattern = new Regex("<span class=\"label\">Enlisted</span>\\s*<strong class=\"value\">([^<]+)</strong>");
|
||||||
|
var ueePattern = new Regex("<p class=\"entry citizen-record\">\\s*<span class=\"label\">UEE Citizen Record<\\/span>\\s*<strong class=\"value\">#?(n\\/a|\\d+)<\\/strong>\\s*<\\/p>");
|
||||||
|
var orgPattern = new Regex("\\/orgs\\/(?<OrgURL>[A-z0-9]+)\" .*\\>(?<OrgName>.*)<");
|
||||||
|
var pfpPattern = new Regex("/media/(.*)\"");
|
||||||
|
|
||||||
|
// Make web request to check player data
|
||||||
|
var playerData = new PlayerData();
|
||||||
|
var httpClient = new HttpClient();
|
||||||
|
var response = await httpClient.GetAsync($"https://robertsspaceindustries.com/en/citizens/{enemyPilot}");
|
||||||
|
|
||||||
|
if (response.StatusCode != HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
var joinDataMatch = joinDataPattern.Match(content);
|
||||||
|
if (joinDataMatch.Success)
|
||||||
|
{
|
||||||
|
playerData.JoinDate = joinDataMatch.Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ueeMatch = ueePattern.Match(content);
|
||||||
|
if (ueeMatch.Success)
|
||||||
|
{
|
||||||
|
playerData.UEERecord = ueeMatch.Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgMatch = orgPattern.Match(content);
|
||||||
|
if (orgMatch.Success)
|
||||||
|
{
|
||||||
|
playerData.OrgName = orgMatch.Groups["OrgName"].Value;
|
||||||
|
playerData.OrgURL = "https://robertsspaceindustries.com/en/orgs/" + orgMatch.Groups["OrgURL"].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pfpMatch = pfpPattern.Match(content);
|
||||||
|
if (pfpMatch.Success)
|
||||||
|
{
|
||||||
|
var match = pfpMatch.Groups[1].Value;
|
||||||
|
if (match.Contains("heap_thumb"))
|
||||||
|
{
|
||||||
|
playerData.PFPURL = "https://cdn.robertsspaceindustries.com/static/images/account/avatar_default_big.jpg";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
playerData.PFPURL = "https://robertsspaceindustries.com/media/" + pfpMatch.Groups[1].Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return playerData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task SubmitKill(ActorDeathData deathData, PlayerData? enemyPlayerData)
|
||||||
|
{
|
||||||
|
var killData = new APIKillData
|
||||||
|
{
|
||||||
|
victim_ship = deathData.VictimShip,
|
||||||
|
victim = deathData.VictimPilot,
|
||||||
|
enlisted = enemyPlayerData?.JoinDate,
|
||||||
|
rsi = enemyPlayerData?.UEERecord,
|
||||||
|
weapon = deathData.Weapon,
|
||||||
|
method = deathData.DamageType,
|
||||||
|
loadout_ship = LocalPlayerData.PlayerShip ?? "Unknown",
|
||||||
|
game_version = LocalPlayerData.GameVersion ?? "Unknown",
|
||||||
|
gamemode = LocalPlayerData.CurrentGameMode.ToString() ?? "Unknown",
|
||||||
|
trackr_version = UpdatePage.currentVersion ?? "Unknown",
|
||||||
|
location = LocalPlayerData.LastSeenVehicleLocation ?? "Unknown"
|
||||||
|
};
|
||||||
|
|
||||||
|
var httpClient = new HttpClient();
|
||||||
|
string jsonData = JsonSerializer.Serialize(killData);
|
||||||
|
await httpClient.PostAsync(ConfigManager.ApiUrl + "/register-kill", new StringContent(jsonData, Encoding.UTF8, "application/json"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue