- @if (context.Error != null)
+ @if (_loading)
+ {
+
+
+
+ }
+ else if (context.Error != null)
{
@context.Error
}
else
{
- var group = _reports.Where(x => TableFilter(x)).GroupBy(x => x.Group).First(x => x.Any(i => i.FileName == context.FileName)).OrderBy(x => x.MatchStart).ToList();
+ var group = _filteredReports.GroupBy(x => x.Group).First(x => x.Any(i => i.FileName == context.FileName)).OrderBy(x => x.MatchStart).ToList();
var index = group.FindIndex(x => x.FileName == context.FileName) + 1;
Team ownTeam = context.GetOwnTeam(SettingService.Settings);
@@ -180,7 +186,9 @@
var percentage_color = winPercentage > 50 ? "Win" : winPercentage < 40 ? "Lose" : "Draw";
}
- Totaal @_filteredReports.Count gevechten @winPercentage.ToString("0.00")%
+ Totaal @_filteredReports.Count() gevechten @winPercentage.ToString("0.00")%
+
+
@wins Gewonnen
@@ -193,7 +201,12 @@
-
+
+ @if (_loadingTime != TimeSpan.Zero)
+ {
+ @_loadingTime.ToString(@"hh\:mm\:ss\.fff")
+ }
+
@@ -203,11 +216,12 @@
private bool _loading = true;
private string searchString = "";
private bool _hideNormalMatchMaking = true;
+ private TimeSpan _loadingTime = TimeSpan.Zero;
private bool _showOnlyUnkown = false;
private List _reports = new List();
- private List _filteredReports => _reports.Where(x => TableFilter(x)).ToList();
+ private IEnumerable _filteredReports => _reports.Where(x => TableFilter(x));
private TableGroupDefinition _groupDefinition = new()
{
Indentation = false,
@@ -217,32 +231,48 @@
protected override async Task OnInitializedAsync()
{
+ long timestamp = 0;
+
BattleResultService.LoadingBattleReportsStarted += async (s, e) =>
{
await InvokeAsync(() =>
{
_reports = new List();
_loading = true;
+ timestamp = Stopwatch.GetTimestamp();
StateHasChanged();
});
};
- BattleResultService.BattleReportAdded += (s, e) =>
+ BattleResultService.BattleReportAdded += async (s, e) =>
{
- _reports.Add(e.Report);
+ await InvokeAsync(() =>
+ {
+ _reports.Add(e.Report);
+ if (_loading)
+ {
+ _loadingTime = Stopwatch.GetElapsedTime(timestamp);
+ }
+ StateHasChanged();
+ });
};
- BattleResultService.BattleReportRemoved += (s, e) =>
+ BattleResultService.BattleReportRemoved += async (s, e) =>
{
- _reports.Remove(e.Report);
+ await InvokeAsync(() =>
+ {
+ _reports.Remove(e.Report);
+ StateHasChanged();
+ });
};
BattleResultService.LoadingBattleReportsFinished += async (s, e) =>
{
await InvokeAsync(() =>
{
- _reports = BattleResultService.BattleReports.OrderByDescending(x => x.Value.MatchStart).Select(x => x.Value).ToList();
+ _reports = BattleResultService.BattleReports.Select(x => x.Value).OrderByDescending(x => x.MatchStart).ToList();
_loading = false;
+ _loadingTime = Stopwatch.GetElapsedTime(timestamp);
StateHasChanged();
});
};
diff --git a/NED.WoT.BattleResults.Client/Components/Shared/ClanMembersDialog.razor b/NED.WoT.BattleResults.Client/Components/Shared/ClanMembersDialog.razor
index 476815f..4580ae1 100644
--- a/NED.WoT.BattleResults.Client/Components/Shared/ClanMembersDialog.razor
+++ b/NED.WoT.BattleResults.Client/Components/Shared/ClanMembersDialog.razor
@@ -21,7 +21,7 @@
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
- [Parameter] public List Reports { get; set; } = [];
+ [Parameter] public IEnumerable Reports { get; set; } = [];
private IEnumerable GetClanMembers()
diff --git a/NED.WoT.BattleResults.Client/Components/Shared/SettingsDialog.razor b/NED.WoT.BattleResults.Client/Components/Shared/SettingsDialog.razor
index 398be91..9cfff14 100644
--- a/NED.WoT.BattleResults.Client/Components/Shared/SettingsDialog.razor
+++ b/NED.WoT.BattleResults.Client/Components/Shared/SettingsDialog.razor
@@ -29,6 +29,9 @@
+
+
Ga naar app settings
@@ -59,7 +62,8 @@
ClanAbbreviation = SettingService.Settings.ClanAbbreviation,
PlayerName = SettingService.Settings.PlayerName,
SingleBattleResultOpenedOnly = SettingService.Settings.SingleBattleResultOpenedOnly,
- OnlyHighlistOwnMatches = SettingService.Settings.OnlyHighlistOwnMatches
+ OnlyHighlistOwnMatches = SettingService.Settings.OnlyHighlistOwnMatches,
+ StartupUpdateOnEveryReport = SettingService.Settings.StartupUpdateOnEveryReport
};
}
@@ -77,6 +81,7 @@
settings.PlayerName = modal.PlayerName;
settings.SingleBattleResultOpenedOnly = modal.SingleBattleResultOpenedOnly;
settings.OnlyHighlistOwnMatches = modal.OnlyHighlistOwnMatches;
+ settings.StartupUpdateOnEveryReport = modal.StartupUpdateOnEveryReport;
SettingService.Save(settings);
@@ -103,5 +108,6 @@
public bool OnlyHighlistOwnMatches { get; set; }
+ public bool StartupUpdateOnEveryReport { get; set; }
}
}
diff --git a/NED.WoT.BattleResults.Client/MauiProgram.cs b/NED.WoT.BattleResults.Client/MauiProgram.cs
index 29807bf..c7ae3c6 100644
--- a/NED.WoT.BattleResults.Client/MauiProgram.cs
+++ b/NED.WoT.BattleResults.Client/MauiProgram.cs
@@ -10,7 +10,7 @@ public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
- var builder = MauiApp.CreateBuilder();
+ MauiAppBuilder builder = MauiApp.CreateBuilder();
builder
.UseMauiApp()
.ConfigureFonts(fonts =>
diff --git a/NED.WoT.BattleResults.Client/Models/BattleReport.cs b/NED.WoT.BattleResults.Client/Models/BattleReport.cs
index 4640941..27306e7 100644
--- a/NED.WoT.BattleResults.Client/Models/BattleReport.cs
+++ b/NED.WoT.BattleResults.Client/Models/BattleReport.cs
@@ -31,13 +31,13 @@ public MarkupString MatchDurationDisplay
{
get
{
- var duration = string.Empty;
+ string duration = string.Empty;
if (MatchDuration.HasValue)
{
- var time = new TimeSpan(MatchDuration.Value * TimeSpan.TicksPerSecond);
- var minutes = time.Minutes.ToString();
+ TimeSpan time = new TimeSpan(MatchDuration.Value * TimeSpan.TicksPerSecond);
+ string minutes = time.Minutes.ToString();
if (minutes.Length == 1) minutes = " " + minutes;
- var seconds = time.Seconds.ToString();
+ string seconds = time.Seconds.ToString();
if (seconds.Length == 1) seconds = " " + seconds;
return (MarkupString)$"{minutes}m {seconds}s";
@@ -58,7 +58,7 @@ public string Group
{
get
{
- var daysAgo = (DateTime.Now.Date - MatchStart.Date).Days;
+ int daysAgo = (DateTime.Now.Date - MatchStart.Date).Days;
if (MatchStart.Hour <= 2)
{
daysAgo += 1;
@@ -157,7 +157,7 @@ public bool IsOwnTeam(Settings settings)
public string GetResult(string map)
{
- var lines = Players.Where(x => x.Name != null).OrderByDescending(x => x.ExperienceEarned).Select(x => x.Name).ToList();
+ List lines = Players.Where(x => x.Name != null).OrderByDescending(x => x.ExperienceEarned).Select(x => x.Name).ToList();
lines.Insert(0, $"{map} {(Number == 1 ? "I" : "II")}");
lines.Insert(1, ResultDisplay);
diff --git a/NED.WoT.BattleResults.Client/Models/Settings.cs b/NED.WoT.BattleResults.Client/Models/Settings.cs
index 1979414..c3797d2 100644
--- a/NED.WoT.BattleResults.Client/Models/Settings.cs
+++ b/NED.WoT.BattleResults.Client/Models/Settings.cs
@@ -9,5 +9,6 @@ public class Settings
public bool ShowCopyNamesOnlyWhenClanMatches { get; set; }
public bool SingleBattleResultOpenedOnly { get; set; }
public bool OnlyHighlistOwnMatches { get; set; }
+ public bool StartupUpdateOnEveryReport { get; set; }
}
}
diff --git a/NED.WoT.BattleResults.Client/Platforms/Windows/Package.appxmanifest b/NED.WoT.BattleResults.Client/Platforms/Windows/Package.appxmanifest
index f8b85e0..2fa3d35 100644
--- a/NED.WoT.BattleResults.Client/Platforms/Windows/Package.appxmanifest
+++ b/NED.WoT.BattleResults.Client/Platforms/Windows/Package.appxmanifest
@@ -6,7 +6,7 @@
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
-
+
diff --git a/NED.WoT.BattleResults.Client/Services/BattleReportMapper.cs b/NED.WoT.BattleResults.Client/Services/BattleReportMapper.cs
index a83f73d..4bffc36 100644
--- a/NED.WoT.BattleResults.Client/Services/BattleReportMapper.cs
+++ b/NED.WoT.BattleResults.Client/Services/BattleReportMapper.cs
@@ -1,154 +1,170 @@
-using NED.WoT.BattleResults.Client.Models;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Text.Json.Nodes;
+
+using NED.WoT.BattleResults.Client.Models;
using NED.WoT.BattleResults.Client.Services;
-using System.Globalization;
-using System.Text.Json.Nodes;
+namespace NED.WoT.BattleResults.Client.Data;
-namespace NED.WoT.BattleResults.Client.Data
+public class BattleReportMapper
{
- public class BattleReportMapper
- {
- protected BattleReportMapper() { }
+ protected BattleReportMapper() { }
- private static CultureInfo CultureInfo => CultureInfo.InvariantCulture;
+ private static CultureInfo CultureInfo => CultureInfo.InvariantCulture;
- public static BattleReport Map(JsonObject replay, JsonArray stats, Settings settings)
+ public static BattleReport Map(JsonObject replay, JsonArray stats, Settings settings)
+ {
+ if (!DateTime.TryParseExact(replay["dateTime"].GetValue(), "dd.MM.yyyy HH:mm:ss", CultureInfo, DateTimeStyles.None, out DateTime matchStart))
{
- var datetime = replay["dateTime"].GetValue();
- if (!DateTime.TryParseExact(datetime, "dd.MM.yyyy HH:mm:ss", CultureInfo, DateTimeStyles.None, out DateTime matchStart))
- {
- throw new Exception($"Could not parse match datetime '{datetime}' with culture '{CultureInfo.Name}'. Current culture is '{CultureInfo.CurrentCulture.Name}'.");
- }
+ throw new Exception($"Could not parse match datetime '{replay["dateTime"].GetValue()}' with culture '{CultureInfo.Name}'. Current culture is '{CultureInfo.CurrentCulture.Name}'.");
+ }
- var game = new BattleReport
- {
- MapName = MapNameResolver.GetMapName(replay["mapDisplayName"].GetValue()),
- MatchStart = matchStart,
- Team1 = new Team()
- {
- Number = 1
- },
- Team2 = new Team()
- {
- Number = 2
- }
- };
-
- if (stats != null)
- {
- game.FinishReason = stats[0]["common"]["finishReason"].GetValue();
- game.MatchDuration = stats[0]["common"]["duration"].GetValue();
- game.Team1.Health = stats[0]["common"]["teamHealth"]["1"].GetValue();
- game.Team1.IsWinner = stats[0]["common"]["winnerTeam"].GetValue() == game.Team1.Number;
- game.Team2.Health = stats[0]["common"]["teamHealth"]["2"].GetValue();
- game.Team2.IsWinner = stats[0]["common"]["winnerTeam"].GetValue() == game.Team2.Number;
-
- foreach (var vehicle in stats[0]["vehicles"].AsObject())
- {
- var vehicleData = vehicle.Value.AsArray()[0].AsObject();
- var playerData = stats[1][vehicle.Key].AsObject();
- MapPlayerData(game, playerData, vehicleData, settings);
- }
- }
- else
+ BattleReport game = new()
+ {
+ MapName = MapNameResolver.GetMapName(replay["mapDisplayName"].GetValue()),
+ MatchStart = matchStart,
+ Team1 = new Team { Number = 1 },
+ Team2 = new Team { Number = 2 }
+ };
+
+ if (stats != null)
+ {
+ JsonNode commonStats = stats[0]["common"];
+ game.FinishReason = commonStats["finishReason"].GetValue();
+ game.MatchDuration = commonStats["duration"].GetValue();
+ game.Team1.Health = commonStats["teamHealth"]["1"].GetValue();
+ game.Team1.IsWinner = commonStats["winnerTeam"].GetValue() == game.Team1.Number;
+ game.Team2.Health = commonStats["teamHealth"]["2"].GetValue();
+ game.Team2.IsWinner = commonStats["winnerTeam"].GetValue() == game.Team2.Number;
+
+ foreach (KeyValuePair vehicle in stats[0]["vehicles"].AsObject())
{
- foreach (var vehicle in replay["vehicles"].AsObject())
- {
- var playerData = vehicle.Value.AsObject();
- var vehicleData = stats?[0]?["vehicles"]?.AsObject()?[vehicle.Key]?[0]?.AsObject();
- MapPlayerData(game, playerData, vehicleData, settings);
- }
+ MapPlayerData(game, stats[1][vehicle.Key].AsObject(), vehicle.Value.AsArray()[0].AsObject(), settings);
}
-
- var mostMentionedClanTeam1 = game.Team1.Players.Where(x => !string.IsNullOrEmpty(x.Clan)).Select(x => x.Clan).GroupBy(x => x).MaxBy(x => x.Count());
- if (mostMentionedClanTeam1 != null)
+ }
+ else
+ {
+ foreach (KeyValuePair vehicle in replay["vehicles"].AsObject())
{
- game.Team1.Abbreviation = mostMentionedClanTeam1.Count() >= Math.Max(game.Team1.Players.Count / 2, 4) ? mostMentionedClanTeam1.Key : "?";
+ MapPlayerData(game, vehicle.Value.AsObject(), stats?[0]?["vehicles"]?.AsObject()?[vehicle.Key]?[0]?.AsObject(), settings);
}
+ }
- var mostMentionedClanTeam2 = game.Team2.Players.Where(x => !string.IsNullOrEmpty(x.Clan)).Select(x => x.Clan).GroupBy(x => x).MaxBy(x => x.Count());
- if (mostMentionedClanTeam2 != null)
- {
- game.Team2.Abbreviation = mostMentionedClanTeam2.Count() >= Math.Max(game.Team2.Players.Count / 2, 4) ? mostMentionedClanTeam2.Key : "?";
- }
+ game.Team1.Abbreviation = GetMostMentionedClanAbbreviation(game.Team1);
+ game.Team2.Abbreviation = GetMostMentionedClanAbbreviation(game.Team2);
- game.Team1.Result = GetResult(game.Team1.IsWinner, game.Team2.IsWinner);
- game.Team2.Result = GetResult(game.Team2.IsWinner, game.Team1.IsWinner);
+ game.Team1.Result = GetResult(game.Team1.IsWinner, game.Team2.IsWinner);
+ game.Team2.Result = GetResult(game.Team2.IsWinner, game.Team1.IsWinner);
- int playerCount = Math.Max(game.Team1.Players.Count, game.Team2.Players.Count);
- EnrichAndSortPlayers(game.Team1, playerCount);
- EnrichAndSortPlayers(game.Team2, playerCount);
+ int playerCount = Math.Max(game.Team1.Players.Count, game.Team2.Players.Count);
+ EnrichAndSortPlayers(game.Team1, playerCount);
+ EnrichAndSortPlayers(game.Team2, playerCount);
- return game;
+ return game;
+ }
+
+ private static string GetMostMentionedClanAbbreviation(Team team)
+ {
+ if (team.Players == null || team.Players.Count == 0)
+ {
+ return "?";
}
- private static void EnrichAndSortPlayers(Team team, int playerCount)
+ Dictionary clanCounts = [];
+ string mostMentionedClan = "?";
+ int maxMentions = 0;
+ int mentionThreshold = Math.Max(team.Players.Count / 2, 4);
+
+ foreach (var player in team.Players)
{
- var currentTeamPlayerCount = team.Players.Count;
- for (int i = 0; i < playerCount - currentTeamPlayerCount; i++)
+ if (string.IsNullOrEmpty(player.Clan))
{
- team.Players.Add(new Player());
+ continue;
}
- team.Players = [.. team.Players.OrderByDescending(p => p.ExperienceEarned)];
- for (int i = 0; i < team.Players.Count; i++)
+ int count = clanCounts.GetValueOrDefault(player.Clan) + 1;
+ clanCounts[player.Clan] = count;
+
+ if (count > maxMentions)
{
- team.Players[i].Number = i + 1;
+ mostMentionedClan = player.Clan;
+ maxMentions = count;
}
+ }
+
+ return maxMentions >= mentionThreshold ? mostMentionedClan : "?";
+ }
+
+ private static void EnrichAndSortPlayers(Team team, int playerCount)
+ {
+ int playersToAdd = playerCount - team.Players.Count;
+ if (playersToAdd > 0)
+ {
+ team.Players.AddRange(Enumerable.Repeat(new Player(), playersToAdd));
}
- private static void MapPlayerData(BattleReport game, JsonObject playerData, JsonObject vehicleData, Settings settings)
+ Span playersSpan = CollectionsMarshal.AsSpan(team.Players);
+ playersSpan.Sort((p1, p2) => p2.ExperienceEarned.GetValueOrDefault().CompareTo(p1.ExperienceEarned.GetValueOrDefault()));
+
+ for (int i = 0; i < playersSpan.Length; i++)
{
- var clan = playerData["clanAbbrev"].GetValue();
+ playersSpan[i].Number = i + 1;
+ }
+ }
- var team = playerData["team"].GetValue() == game.Team1.Number ? game.Team1 : game.Team2;
+ private static void MapPlayerData(BattleReport game, JsonObject playerData, JsonObject vehicleData, Settings settings)
+ {
+ string clan = playerData["clanAbbrev"].GetValue();
- var player = new Player()
- {
- Name = playerData["name"].GetValue(),
- Clan = clan,
- Vehicle = TankNameResolver.GetTankName(playerData["vehicleType"].GetValue()),
- IsClanMember = settings.ClanAbbreviation?.ToLower() == clan?.ToLower()
- };
+ Team team = playerData["team"].GetValue() == game.Team1.Number ? game.Team1 : game.Team2;
- if (vehicleData != null)
- {
- player.DamageDealt = vehicleData["damageDealt"]?.GetValue();
- player.DamageReceived = vehicleData["damageReceived"]?.GetValue();
- player.DamageBlocked = vehicleData["damageBlockedByArmor"]?.GetValue();
- player.Piercings = vehicleData["piercings"]?.GetValue();
- player.ExperienceEarned = vehicleData["xp"]?.GetValue();
- player.CreditsEarned = vehicleData["credits"]?.GetValue();
- player.Shots = vehicleData["shots"]?.GetValue();
- player.Kills = vehicleData["kills"]?.GetValue();
- player.IsTeamKiller = vehicleData["isTeamKiller"]?.ToString() == "1";
- player.CapturePoints = vehicleData["flagCapture"]?.GetValue();
- player.Health = vehicleData["health"]?.GetValue();
- player.DirectHits = vehicleData["directHits"]?.GetValue();
- player.Spotted = vehicleData["spotted"]?.GetValue();
- player.LifeTime = vehicleData["lifeTime"]?.GetValue();
- player.MaxHealth = vehicleData["maxHealth"]?.GetValue();
- player.DeathReason = vehicleData["deathReason"]?.GetValue();
- }
+ Player player = new()
+ {
+ Name = playerData["name"].GetValue(),
+ Clan = clan,
+ Vehicle = TankNameResolver.GetTankName(playerData["vehicleType"].GetValue()),
+ IsClanMember = settings.ClanAbbreviation?.ToLower() == clan?.ToLower()
+ };
- team.Players.Add(player);
+ if (vehicleData != null)
+ {
+ player.DamageDealt = vehicleData["damageDealt"]?.GetValue();
+ player.DamageReceived = vehicleData["damageReceived"]?.GetValue();
+ player.DamageBlocked = vehicleData["damageBlockedByArmor"]?.GetValue();
+ player.Piercings = vehicleData["piercings"]?.GetValue();
+ player.ExperienceEarned = vehicleData["xp"]?.GetValue();
+ player.CreditsEarned = vehicleData["credits"]?.GetValue();
+ player.Shots = vehicleData["shots"]?.GetValue();
+ player.Kills = vehicleData["kills"]?.GetValue();
+ player.IsTeamKiller = vehicleData["isTeamKiller"]?.ToString() == "1";
+ player.CapturePoints = vehicleData["flagCapture"]?.GetValue();
+ player.Health = vehicleData["health"]?.GetValue();
+ player.DirectHits = vehicleData["directHits"]?.GetValue();
+ player.Spotted = vehicleData["spotted"]?.GetValue();
+ player.LifeTime = vehicleData["lifeTime"]?.GetValue();
+ player.MaxHealth = vehicleData["maxHealth"]?.GetValue();
+ player.DeathReason = vehicleData["deathReason"]?.GetValue();
}
- private static Result GetResult(bool? team1IsWinner, bool? team2IsWinner)
+ team.Players.Add(player);
+ }
+
+ private static Result GetResult(bool? team1IsWinner, bool? team2IsWinner)
+ {
+ if (team1IsWinner == true)
{
- if (team1IsWinner == true)
- {
- return Result.Win;
- }
- else if (team2IsWinner == true)
- {
- return Result.Lose;
- }
- else if (team1IsWinner == false && team2IsWinner == false)
- {
- return Result.Draw;
- }
- return Result.Unkown;
+ return Result.Win;
+ }
+ else if (team2IsWinner == true)
+ {
+ return Result.Lose;
}
+ else if (team1IsWinner == false && team2IsWinner == false)
+ {
+ return Result.Draw;
+ }
+
+ return Result.Unkown;
}
}
diff --git a/NED.WoT.BattleResults.Client/Services/BattleReportService.cs b/NED.WoT.BattleResults.Client/Services/BattleReportService.cs
index 346b36b..1477d38 100644
--- a/NED.WoT.BattleResults.Client/Services/BattleReportService.cs
+++ b/NED.WoT.BattleResults.Client/Services/BattleReportService.cs
@@ -1,14 +1,13 @@
-using MudBlazor;
-
-using NED.WoT.BattleResults.Client.Data;
-using NED.WoT.BattleResults.Client.Models;
-
-using System.Collections;
using System.Collections.Concurrent;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
+using MudBlazor;
+
+using NED.WoT.BattleResults.Client.Data;
+using NED.WoT.BattleResults.Client.Models;
+
namespace NED.WoT.BattleResults.Client.Services;
public class BattleReportService
@@ -19,6 +18,9 @@ public class BattleReportService
private readonly SettingService _settingService;
private FileSystemWatcher _watcher;
+ private static ReadOnlySpan ReplayStart => "{\"clientVersionFromXml\""u8;
+ private static ReadOnlySpan StatsStart => "[{\"personal\""u8;
+
public ConcurrentDictionary BattleReports { get; set; } = new ConcurrentDictionary();
public event EventHandler LoadingBattleReportsStarted;
@@ -31,7 +33,6 @@ public class BattleReportService
PropertyNameCaseInsensitive = true
};
-
public BattleReportService(ISnackbar snackbar, SettingService settingService)
{
_snackbar = snackbar;
@@ -48,16 +49,29 @@ public void LoadBattleReports()
LoadingBattleReportsStarted?.Invoke(this, new EventArgs());
- var directoryInfo = new DirectoryInfo(_settingService.Settings.WotReplayDirectory);
- var files = directoryInfo.GetFiles(SEARCH_PATTERN)
- .Where(x => x.LastWriteTime > _settingService.Settings.LoadBattlesSince)
- .Where(x => !IsTempFile(x.Name))
- .OrderByDescending(x => x.CreationTime);
+ DirectoryInfo directoryInfo = new(_settingService.Settings.WotReplayDirectory);
+ List files = directoryInfo
+ .EnumerateFiles(SEARCH_PATTERN)
+ .Where(x => x.LastWriteTime > _settingService.Settings.LoadBattlesSince && !IsTempFile(x.Name))
+ .OrderByDescending(x => x.CreationTime)
+ .ToList();
- Parallel.ForEach(files, (file) =>
+ OrderablePartitioner partitioner = Partitioner.Create(files);
+ Parallel.ForEach(partitioner, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, file =>
{
- var report = GetBattleReport(file);
- AddBattleReport(file.Name, report);
+ try
+ {
+ BattleReport report = GetBattleReport(file);
+
+ AddBattleReport(file.Name, report, _settingService.Settings.StartupUpdateOnEveryReport);
+ }
+ catch (Exception ex)
+ {
+ BattleReport report = new()
+ {
+ Error = $"Failed to process file {file.Name}: {ex.Message}"
+ };
+ }
});
LoadingBattleReportsFinished?.Invoke(this, new EventArgs());
@@ -105,7 +119,7 @@ private void OnCreated(object sender, FileSystemEventArgs e)
LoadingBattleReportsStarted?.Invoke(this, new EventArgs());
- var report = GetBattleReport(new FileInfo(e.FullPath));
+ BattleReport report = GetBattleReport(new FileInfo(e.FullPath));
if (report != null)
{
AddBattleReport(e.Name, report);
@@ -119,10 +133,9 @@ private void OnCreated(object sender, FileSystemEventArgs e)
LoadingBattleReportsFinished?.Invoke(this, new EventArgs());
}
-
private void OnDeleted(object sender, FileSystemEventArgs e)
{
- if (BattleReports.Remove(e.Name, out var report))
+ if (BattleReports.Remove(e.Name, out BattleReport report))
{
_snackbar.Add($"Replay bestand '{e.Name}' is verwijderd", Severity.Warning);
BattleReportRemoved?.Invoke(this, new BattleReportRemovedEventArgs(report));
@@ -140,19 +153,19 @@ private BattleReport GetBattleReport(FileInfo file)
try
{
- byte[] fileData = File.ReadAllBytes(file.FullName);
-
- int startIndex = FindSequenceIndex(fileData, [(byte)'{', (byte)'"']);
- var replay = GetJsonFromFile(fileData, '{', '}', ref startIndex);
- var stats = GetJsonFromFile(fileData, '[', ']', ref startIndex);
+ ReadOnlySpan fileData = File.ReadAllBytes(file.FullName);
+ int replayStartIndex = fileData.IndexOf(ReplayStart);
+ JsonObject replay = GetJsonFromFile(fileData, '{', '}', replayStartIndex);
if (replay?["dateTime"] == null)
{
report.Error = $"Could not parse file: {file.Name}";
}
else
{
- report = BattleReportMapper.Map(replay, stats, _settingService.Settings);
+ int statsStartIndex = fileData.IndexOf(StatsStart);
+ JsonArray stats = GetJsonFromFile(fileData, '[', ']', statsStartIndex);
+ report = BattleReportMapper.Map(replay, stats, _settingService.Settings);
}
}
catch (Exception ex)
@@ -165,22 +178,24 @@ private BattleReport GetBattleReport(FileInfo file)
return report;
}
- private static T GetJsonFromFile(byte[] fileData, char start, char end, ref int startIndex)
+ private static T GetJsonFromFile(ReadOnlySpan fileData, char start, char end, int startIndex)
{
+ if (startIndex == -1)
+ {
+ return default;
+ }
+
try
{
int jsonStarts = 0;
int jsonEnds = 0;
+ int length = fileData.Length;
- for (int i = startIndex; i < fileData.Length; i++)
+ for (int i = startIndex; i < length; i++)
{
if (fileData[i] == start)
{
jsonStarts++;
- if (jsonStarts == 1)
- {
- startIndex = i;
- }
}
else if (fileData[i] == end && jsonStarts > jsonEnds)
{
@@ -189,16 +204,12 @@ private static T GetJsonFromFile(byte[] fileData, char start, char end, ref i
if (jsonStarts > 0 && jsonStarts == jsonEnds)
{
- string json = Encoding.UTF8.GetString(fileData, startIndex, i - startIndex + 1);
-
- // Update startindex for next json search
- startIndex = i;
-
- return JsonSerializer.Deserialize(json, _options);
+ ReadOnlySpan jsonSpan = fileData.Slice(startIndex, i - startIndex + 1);
+ return JsonSerializer.Deserialize(jsonSpan, _options);
}
}
}
- catch (Exception)
+ catch
{
return default;
}
@@ -211,27 +222,16 @@ private static bool IsTempFile(string fileName)
return fileName.Contains("temp");
}
- private void AddBattleReport(string name, BattleReport report)
+ private void AddBattleReport(string name, BattleReport report, bool notify = true)
{
if (!BattleReports.TryAdd(name, report))
{
_snackbar.Add($"Kan bestand '{name}' niet toevoegen", Severity.Error);
}
- BattleReportAdded?.Invoke(this, new BattleReportAddedEventArgs(report));
- }
-
- private static int FindSequenceIndex(byte[] byteArray, byte[] sequence)
- {
- for (int i = 0; i <= byteArray.Length - sequence.Length; i++)
+ if (notify)
{
- if (byteArray.Skip(i).Take(sequence.Length).SequenceEqual(sequence))
- {
- return i;
- }
+ BattleReportAdded?.Invoke(this, new BattleReportAddedEventArgs(report));
}
- return 0;
}
-
-}
-
+}
\ No newline at end of file
diff --git a/NED.WoT.BattleResults.Client/Services/MapNameResolver.cs b/NED.WoT.BattleResults.Client/Services/MapNameResolver.cs
index b32f81d..97f3afa 100644
--- a/NED.WoT.BattleResults.Client/Services/MapNameResolver.cs
+++ b/NED.WoT.BattleResults.Client/Services/MapNameResolver.cs
@@ -1,94 +1,103 @@
-namespace NED.WoT.BattleResults.Client.Services
+namespace NED.WoT.BattleResults.Client.Services;
+
+public static class MapNameResolver
{
- public static class MapNameResolver
+ public static HashSet UndefinedMapNames = [];
+
+ private static readonly Dictionary _mapNames = new()
+ {
+ {"Abdij","Abbey"},
+ {"Vliegveld","Airfield"},
+ {"Klif","Cliff"},
+ {"Rijksgrens","Empire's Border"},
+ {"Vissersbaai","Fisherman's Bay"},
+ {"Fjorden","Fjords"},
+ {"Spookstad","Ghost Town"},
+ {"Gletsjer","Glacier"},
+ {"Snelweg","Highway"},
+ {"Karelië","Karelia"},
+ {"Mannerheim Linie","Mannerheim Line"},
+ {"Mijngebied","Mines"},
+ {"Bergpas","Mountain Pass"},
+ {"Buitenpost","Outpost"},
+ {"Oesterbaai","Oyster Bay"},
+ {"Parelrivier","Pearl River"},
+ {"Parijs","Paris"},
+ {"Provincie","Province"},
+ {"Veilige haven","Safe Haven"},
+ {"Zandrivier","Sand River"},
+ {"Serene kust","Serene Coast"},
+ {"Siegfriedlinie","Siegfried Line"},
+ {"Steppe","Steppes"},
+ {"Toendra","Tundra"},
+ {"Stadspark","Widepark"},
+
+ {"Karelia", "Karelia"},
+ {"Lakeville", "Lakeville"},
+ {"Ghost Town", "Ghost Town"},
+ {"Sand River", "Sand River"},
+ {"El Halluf", "El Halluf"},
+ {"Malinovka", "Malinovka"},
+ {"Himmelsdorf", "Himmelsdorf"},
+ {"Paris", "Paris"},
+ {"Siegfried Line", "Siegfried Line"},
+ {"Ruinberg", "Ruinberg"},
+ {"Steppes", "Steppes"},
+ {"Prokhorovka", "Prokhorovka"},
+ {"Province", "Province"},
+ {"Mines", "Mines"},
+ {"Outpost", "Outpost"},
+ {"Cliff", "Cliff"},
+ {"Abbey", "Abbey"},
+ {"Pearl River", "Pearl River"},
+ {"Berlin", "Berlin"},
+ {"Pilsen", "Pilsen"},
+ {"Tundra", "Tundra"},
+ {"Live Oaks", "Live Oaks"},
+ {"Mannerheim Line", "Mannerheim Line"},
+ {"Fisherman's Bay", "Fisherman's Bay"},
+ {"Mountain Pass", "Mountain Pass"},
+ {"Widepark", "Widepark"},
+ {"Glacier", "Glacier"},
+ {"Murovanka", "Murovanka"},
+ {"Erlenberg", "Erlenberg"},
+ {"Redshire", "Redshire"},
+ {"Westfield", "Westfield"},
+ {"Overlord", "Overlord"},
+ {"Fjords", "Fjords"},
+ {"Ensk", "Ensk"},
+ {"Airfield", "Airfield"},
+ {"Highway", "Highway"},
+ {"Safe Haven", "Safe Haven"},
+ {"Empire's Border", "Empire's Border"}
+ };
+
+ public static string GetMapName(string key)
{
- public static HashSet UndefinedMapNames = [];
+ if (string.IsNullOrEmpty(key))
+ {
+ return key;
+ }
- private static readonly Dictionary _mapNames = new()
+ if (_mapNames.TryGetValue(key, out string name))
{
- {"Abdij","Abbey"},
- {"Vliegveld","Airfield"},
- {"Klif","Cliff"},
- {"Rijksgrens","Empire's Border"},
- {"Vissersbaai","Fisherman's Bay"},
- {"Fjorden","Fjords"},
- {"Spookstad","Ghost Town"},
- {"Gletsjer","Glacier"},
- {"Snelweg","Highway"},
- {"Karelië","Karelia"},
- {"Mannerheim Linie","Mannerheim Line"},
- {"Mijngebied","Mines"},
- {"Bergpas","Mountain Pass"},
- {"Buitenpost","Outpost"},
- {"Oesterbaai","Oyster Bay"},
- {"Parelrivier","Pearl River"},
- {"Parijs","Paris"},
- {"Provincie","Province"},
- {"Veilige haven","Safe Haven"},
- {"Zandrivier","Sand River"},
- {"Serene kust","Serene Coast"},
- {"Siegfriedlinie","Siegfried Line"},
- {"Steppe","Steppes"},
- {"Toendra","Tundra"},
- {"Stadspark","Widepark"},
+ return name;
+ }
- {"Karelia", "Karelia"},
- {"Lakeville", "Lakeville"},
- {"Ghost Town", "Ghost Town"},
- {"Sand River", "Sand River"},
- {"El Halluf", "El Halluf"},
- {"Malinovka", "Malinovka"},
- {"Himmelsdorf", "Himmelsdorf"},
- {"Paris", "Paris"},
- {"Siegfried Line", "Siegfried Line"},
- {"Ruinberg", "Ruinberg"},
- {"Steppes", "Steppes"},
- {"Prokhorovka", "Prokhorovka"},
- {"Province", "Province"},
- {"Mines", "Mines"},
- {"Outpost", "Outpost"},
- {"Cliff", "Cliff"},
- {"Abbey", "Abbey"},
- {"Pearl River", "Pearl River"},
- {"Berlin", "Berlin"},
- {"Pilsen", "Pilsen"},
- {"Tundra", "Tundra"},
- {"Live Oaks", "Live Oaks"},
- {"Mannerheim Line", "Mannerheim Line"},
- {"Fisherman's Bay", "Fisherman's Bay"},
- {"Mountain Pass", "Mountain Pass"},
- {"Widepark", "Widepark"},
- {"Glacier", "Glacier"},
- {"Murovanka", "Murovanka"},
- {"Erlenberg", "Erlenberg"},
- {"Redshire", "Redshire"},
- {"Westfield", "Westfield"},
- {"Overlord", "Overlord"},
- {"Fjords", "Fjords"},
- {"Ensk", "Ensk"},
- {"Airfield", "Airfield"},
- {"Highway", "Highway"},
- {"Safe Haven", "Safe Haven"},
- {"Empire's Border", "Empire's Border"}
- };
+ ReadOnlySpan keySpan = key.AsSpan();
- public static string GetMapName(string key)
+ int semiColonIndex = keySpan.IndexOf(':');
+ if (semiColonIndex != -1)
{
- string strippedKey = key[(key.IndexOf(':') + 1)..];
- if (_mapNames.TryGetValue(strippedKey, out var name))
+ ReadOnlySpan strippedKey = keySpan[(semiColonIndex + 1)..];
+ if (_mapNames.TryGetValue(strippedKey.ToString(), out name))
{
return name;
}
+ }
- if (!string.IsNullOrWhiteSpace(key))
- {
- UndefinedMapNames.Add(key);
- }
+ UndefinedMapNames.Add(key);
- return key;
- }
+ return key;
}
-}
-
-
-
+}
\ No newline at end of file
diff --git a/NED.WoT.BattleResults.Client/Services/SettingService.cs b/NED.WoT.BattleResults.Client/Services/SettingService.cs
index 87b1706..92eef42 100644
--- a/NED.WoT.BattleResults.Client/Services/SettingService.cs
+++ b/NED.WoT.BattleResults.Client/Services/SettingService.cs
@@ -16,6 +16,7 @@ public SettingService()
PlayerName = Preferences.Default.Get(nameof(Settings.PlayerName), string.Empty),
SingleBattleResultOpenedOnly = Preferences.Default.Get(nameof(Settings.SingleBattleResultOpenedOnly), false),
OnlyHighlistOwnMatches = Preferences.Default.Get(nameof(Settings.OnlyHighlistOwnMatches), false),
+ StartupUpdateOnEveryReport = Preferences.Default.Get(nameof(Settings.StartupUpdateOnEveryReport), false),
};
}
public void Save(Settings settings)
@@ -28,6 +29,7 @@ public void Save(Settings settings)
Preferences.Default.Set(nameof(Settings.PlayerName), Settings.PlayerName);
Preferences.Default.Set(nameof(Settings.SingleBattleResultOpenedOnly), Settings.SingleBattleResultOpenedOnly);
Preferences.Default.Set(nameof(Settings.OnlyHighlistOwnMatches), Settings.OnlyHighlistOwnMatches);
+ Preferences.Default.Set(nameof(Settings.StartupUpdateOnEveryReport), Settings.StartupUpdateOnEveryReport);
}
}
}
diff --git a/NED.WoT.BattleResults.Client/Services/TankNameResolver.cs b/NED.WoT.BattleResults.Client/Services/TankNameResolver.cs
index 8536c31..042f88f 100644
--- a/NED.WoT.BattleResults.Client/Services/TankNameResolver.cs
+++ b/NED.WoT.BattleResults.Client/Services/TankNameResolver.cs
@@ -1465,29 +1465,41 @@ public static class TankNameResolver
public static string GetTankName(string key)
{
- if (string.IsNullOrWhiteSpace(key))
+ if (string.IsNullOrEmpty(key))
{
return key;
}
- string strippedKey = key[(key.IndexOf(':') + 1)..];
- if (_tankNames.TryGetValue(strippedKey, out var name))
+ if (_tankNames.TryGetValue(key, out string name))
{
return name;
}
- strippedKey = strippedKey[..strippedKey.LastIndexOf('_')];
- if (_tankNames.TryGetValue(strippedKey, out name))
+ ReadOnlySpan keySpan = key.AsSpan();
+
+ int semiColonIndex = keySpan.IndexOf(':');
+ if (semiColonIndex != -1)
{
- return name;
+ keySpan = keySpan[(semiColonIndex + 1)..];
+ if (_tankNames.TryGetValue(keySpan.ToString(), out name))
+ {
+ return name;
+ }
+ }
+
+ int underscoreIndex = keySpan.LastIndexOf('_');
+ if (underscoreIndex != -1)
+ {
+ keySpan = keySpan[..underscoreIndex];
+ if (_tankNames.TryGetValue(keySpan.ToString(), out name))
+ {
+ return name;
+ }
}
-
- UndefinedTankNames.Add(key);
+
+ UndefinedTankNames.Add(key);
return key;
}
}
-}
-
-
-
+}
\ No newline at end of file