diff --git a/CLI/DownloadCommand.cs b/CLI/DownloadCommand.cs index 70d8b00ac..8434b7a4a 100644 --- a/CLI/DownloadCommand.cs +++ b/CLI/DownloadCommand.cs @@ -183,7 +183,7 @@ private async Task HandleBoilerExitCode(int exitCode) Console.WriteLine(@"Invalid arguments"); break; case (int)BoilerExitCode.CommunicationFailure: - Console.WriteLine(@"CSGO Game coordinator communication failure"); ; + Console.WriteLine(@"CSGO Game coordinator communication failure"); break; case (int)BoilerExitCode.AlreadyConnectedToGC: Console.WriteLine(@"Already connected to CSGO GC, make sure to close CSGO and retry"); diff --git a/CLI/JsonCommand.cs b/CLI/JsonCommand.cs index 8111605c2..4ff324e7d 100644 --- a/CLI/JsonCommand.cs +++ b/CLI/JsonCommand.cs @@ -55,20 +55,46 @@ public override async Task Run(string[] args) try { + CacheService cacheService = new CacheService(); + int currentDemoNumber = 0; foreach (string demoPath in _demoPaths) { - Console.WriteLine($@"Analyzing demo {demoPath}"); + Console.WriteLine($@"Retrieving demo {++currentDemoNumber}/{_demoPaths.Count} {demoPath}"); Demo demo = DemoAnalyzer.ParseDemoHeader(demoPath); + if (demo == null) + { + Console.WriteLine($@"Invalid demo {demoPath}"); + continue; + } + if (_source != null) { demo.Source = _source; } - DemoAnalyzer analyzer = DemoAnalyzer.Factory(demo); - demo = await analyzer.AnalyzeDemoAsync(new CancellationTokenSource().Token); - Console.WriteLine(@"Generating JSON file"); + if (cacheService.HasDemoInCache(demo.Id)) + { + demo = await cacheService.GetDemoDataFromCache(demo.Id); + demo.WeaponFired = await cacheService.GetDemoWeaponFiredAsync(demo); + demo.PlayerBlinded = await cacheService.GetDemoPlayerBlindedAsync(demo); + } + else + { + try + { + Console.WriteLine($@"Analyzing demo {demoPath}"); + DemoAnalyzer analyzer = DemoAnalyzer.Factory(demo); + demo = await analyzer.AnalyzeDemoAsync(new CancellationTokenSource().Token); + await cacheService.WriteDemoDataCache(demo); + } + catch (Exception) + { + Console.WriteLine($@"Error while analyzing demo {demoPath}"); + continue; + } + } + string outputFolderPath = BuildOutputFolderPathFromDemoPath(demoPath); - CacheService cacheService = new CacheService(); string jsonFilePath = await cacheService.GenerateJsonAsync(demo, outputFolderPath); Console.WriteLine($@"JSON file generated at {jsonFilePath}"); } diff --git a/CLI/XlsxCommand.cs b/CLI/XlsxCommand.cs index 6b1a74732..627d62483 100644 --- a/CLI/XlsxCommand.cs +++ b/CLI/XlsxCommand.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Core.Models; +using Services.Concrete; using Services.Concrete.Analyzer; using Services.Concrete.Excel; @@ -67,19 +68,46 @@ public override async Task Run(string[] args) try { ExcelService excelService = new ExcelService(); + CacheService cacheService = new CacheService(); List demos = new List(); + int currentDemoNumber = 0; foreach (string demoPath in _demoPaths) { - Console.WriteLine($@"Analyzing demo {demoPath}"); + Console.WriteLine($@"Retrieving demo {++currentDemoNumber}/{_demoPaths.Count} {demoPath}"); Demo demo = DemoAnalyzer.ParseDemoHeader(demoPath); + if (demo == null) + { + Console.WriteLine($@"Invalid demo {demoPath}"); + continue; + } + if (_source != null) { demo.Source = _source; } - DemoAnalyzer analyzer = DemoAnalyzer.Factory(demo); - demo = await analyzer.AnalyzeDemoAsync(new CancellationTokenSource().Token); + if (cacheService.HasDemoInCache(demo.Id)) + { + demo = await cacheService.GetDemoDataFromCache(demo.Id); + demo.WeaponFired = await cacheService.GetDemoWeaponFiredAsync(demo); + demo.PlayerBlinded = await cacheService.GetDemoPlayerBlindedAsync(demo); + } + else + { + try + { + Console.WriteLine($@"Analyzing demo {demoPath}"); + DemoAnalyzer analyzer = DemoAnalyzer.Factory(demo); + demo = await analyzer.AnalyzeDemoAsync(new CancellationTokenSource().Token); + await cacheService.WriteDemoDataCache(demo); + } + catch (Exception) + { + Console.WriteLine($@"Error while analyzing demo {demoPath}"); + continue; + } + } if (_exportIntoSingleFile) { diff --git a/Core/Models/Events/KillEvent.cs b/Core/Models/Events/KillEvent.cs index 57a8b793f..9180dd51c 100644 --- a/Core/Models/Events/KillEvent.cs +++ b/Core/Models/Events/KillEvent.cs @@ -74,6 +74,12 @@ public class KillEvent : BaseEvent [JsonIgnore] public override string Message => KillerName + " killed " + KilledName + " with " + Weapon.Name; + [JsonIgnore] + public bool IsKillerBot => KillerSteamId == 0; + + [JsonIgnore] + public bool IsVictimBot => KilledSteamId == 0; + public KillEvent(int tick, float seconds) : base(tick, seconds) { } diff --git a/Core/Models/Events/PlayerBlindedEvent.cs b/Core/Models/Events/PlayerBlindedEvent.cs index 2f9d33b4c..84f1aeddd 100644 --- a/Core/Models/Events/PlayerBlindedEvent.cs +++ b/Core/Models/Events/PlayerBlindedEvent.cs @@ -25,6 +25,12 @@ public class PlayerBlindedEvent : BaseEvent [JsonProperty("duration")] public float Duration { get; set; } + [JsonIgnore] + public bool IsThrowerBot => ThrowerSteamId == 0; + + [JsonIgnore] + public bool IsVictimBot => VictimSteamId == 0; + public PlayerBlindedEvent(int tick, float seconds) : base(tick, seconds) { } diff --git a/Core/Models/Player.cs b/Core/Models/Player.cs index 0c019e46e..bc8c165e9 100644 --- a/Core/Models/Player.cs +++ b/Core/Models/Player.cs @@ -1041,7 +1041,11 @@ public bool HasEntryHoldKill set { Set(() => HasEntryHoldKill, ref _hasEntryHoldKill, value); } } - [JsonIgnore] public int MatchCount { get; set; } = 1; + [JsonIgnore] + public int MatchCount { get; set; } = 1; + + [JsonIgnore] + public bool IsBot => SteamId == 0; #endregion @@ -1383,7 +1387,7 @@ private float ComputeKast(Demo demo) } int eventCount = 0; - foreach(Round round in demo.Rounds) + foreach (Round round in demo.Rounds) { bool hasKill = Kills.Any(kill => kill.RoundNumber == round.Number); bool hasAssist = Assists.Any(assist => assist.RoundNumber == round.Number); diff --git a/Manager/ViewModel/Demos/DemoListViewModel.cs b/Manager/ViewModel/Demos/DemoListViewModel.cs index abbdcb1bb..30690fdde 100644 --- a/Manager/ViewModel/Demos/DemoListViewModel.cs +++ b/Manager/ViewModel/Demos/DemoListViewModel.cs @@ -570,6 +570,11 @@ public RelayCommand> ExportExcelCommand { demo.WeaponFired = await _cacheService.GetDemoWeaponFiredAsync(demo); } + + if (demo.PlayerBlinded.Count == 0) + { + demo.PlayerBlinded = await _cacheService.GetDemoPlayerBlindedAsync(demo); + } } await _excelService.GenerateXls(demos.ToList(), saveExportFileDialog.FileName, @@ -622,6 +627,11 @@ await _dialogService.ShowErrorAsync(Properties.Resources.DialogErrorWhileExporti demo.WeaponFired = await _cacheService.GetDemoWeaponFiredAsync(demo); } + if (demo.PlayerBlinded.Count == 0) + { + demo.PlayerBlinded = await _cacheService.GetDemoPlayerBlindedAsync(demo); + } + string exportFilePath = $"{directoryPath}{Path.DirectorySeparatorChar}{demo.Name.Substring(0, demo.Name.Length - 4)}-export.xlsx"; await _excelService.GenerateXls(demo, exportFilePath); diff --git a/Services/Concrete/Excel/MultipleExport.cs b/Services/Concrete/Excel/MultipleExport.cs index 29ad10d8a..8f4f3040d 100644 --- a/Services/Concrete/Excel/MultipleExport.cs +++ b/Services/Concrete/Excel/MultipleExport.cs @@ -40,12 +40,6 @@ public MultipleExport(List demos, long selectedStatsAccountSteamId = 0) public override async Task Generate() { - CacheService cacheService = new CacheService(); - foreach (Demo demo in _demos) - { - demo.WeaponFired = await cacheService.GetDemoWeaponFiredAsync(demo); - } - _generalSheet = new GeneralSheet(Workbook, _demos); await _generalSheet.Generate(); _playersSheet = new PlayersSheet(Workbook, _demos); diff --git a/Services/Concrete/Excel/Sheets/Multiple/FlashMatrixPlayersSheet.cs b/Services/Concrete/Excel/Sheets/Multiple/FlashMatrixPlayersSheet.cs index 215e8783c..c957bd7d5 100644 --- a/Services/Concrete/Excel/Sheets/Multiple/FlashMatrixPlayersSheet.cs +++ b/Services/Concrete/Excel/Sheets/Multiple/FlashMatrixPlayersSheet.cs @@ -3,12 +3,24 @@ using System.Linq; using System.Threading.Tasks; using Core.Models; +using Core.Models.Events; using NPOI.SS.UserModel; namespace Services.Concrete.Excel.Sheets.Multiple { + internal class PlayerFlashEntry + { + public long SteamId { get; set; } + public string Name { get; set; } + + public Dictionary Durations { get; set; } + } + public class FlashMatrixPlayersSheet : AbstractMultipleSheet { + private readonly List _playerEntries = new List(); + private Dictionary _playerNamePerSteamId = new Dictionary(); + public FlashMatrixPlayersSheet(IWorkbook workbook, List demos) { Headers = new Dictionary { { string.Empty, CellType.String } }; @@ -16,71 +28,115 @@ public FlashMatrixPlayersSheet(IWorkbook workbook, List demos) Sheet = workbook.CreateSheet("Flash matrix players"); } - public override async Task GenerateContent() + public override Task GenerateContent() { - // store players row and columns index - Dictionary playersRow = new Dictionary(); - Dictionary playersColumn = new Dictionary(); - - // first row containing victims name - IRow firstRow = Sheet.CreateRow(0); - SetCellValue(firstRow, 0, CellType.String, "Flasher\\Flashed"); + ComputePlayerNamePerSteamId(); + InitializePlayerEntries(); + PopulatePlayerEntries(); + GenerateSheet(); - int columnCount = 1; - int rowCount = 1; + return Task.CompletedTask; + } - // current row - IRow row; + private void ComputePlayerNamePerSteamId() + { foreach (Demo demo in Demos) { - CacheService cacheService = new CacheService(); - demo.PlayerBlinded = await cacheService.GetDemoPlayerBlindedAsync(demo); - - // create rows and columns with only players name foreach (Player player in demo.Players) { - if (!playersRow.ContainsKey(player.SteamId)) + if (player.IsBot || _playerNamePerSteamId.ContainsKey(player.SteamId)) + { + continue; + } + + _playerNamePerSteamId.Add(player.SteamId, player.Name); + + if (IsMaxPlayerLimitReached()) { - // add a column for this player in the first row - SetCellValue(firstRow, columnCount, CellType.String, player.Name); - playersRow.Add(player.SteamId, rowCount); - // create a row for this player - row = Sheet.CreateRow(rowCount++); - SetCellValue(row, 0, CellType.String, player.Name); - playersColumn.Add(player.SteamId, columnCount++); + break; } } - foreach (Player player in demo.Players) + if (IsMaxPlayerLimitReached()) + { + break; + } + } + + _playerNamePerSteamId = _playerNamePerSteamId.OrderBy(k => k.Value).ToDictionary(x => x.Key, x => x.Value); + } + + private void InitializePlayerEntries() + { + foreach (var playerName in _playerNamePerSteamId) + { + var durations = new Dictionary(); + foreach (var sortedPlayerName in _playerNamePerSteamId) { - if (playersRow.ContainsKey(player.SteamId)) + durations.Add(sortedPlayerName.Key, 0); + } + + _playerEntries.Add(new PlayerFlashEntry() + { + SteamId = playerName.Key, + Name = playerName.Value, + Durations = durations, + }); + } + } + + private void PopulatePlayerEntries() + { + foreach (Demo demo in Demos) + { + foreach (PlayerBlindedEvent blindEvent in demo.PlayerBlinded) + { + if (blindEvent.IsThrowerBot || blindEvent.IsVictimBot) + { + continue; + } + + var throwerEntry = _playerEntries.Find(entry => entry.SteamId == blindEvent.ThrowerSteamId); + if (throwerEntry == null) { - int rowIndex = playersRow[player.SteamId]; - foreach (Player pl in demo.Players) - { - if (playersColumn.ContainsKey(pl.SteamId)) - { - int columnIndex = playersColumn[pl.SteamId]; - float duration = demo.PlayerBlinded.Where(e => e.ThrowerSteamId == player.SteamId && e.VictimSteamId == pl.SteamId) - .Sum(e => e.Duration); - row = Sheet.GetRow(rowIndex); - ICell cell = row.GetCell(columnIndex); - if (cell == null) - { - SetCellValue(row, columnIndex, CellType.Numeric, Math.Round(duration, 2)); - } - else - { - cell.SetCellValue(cell.NumericCellValue + Math.Round(duration, 2)); - } - } - } + continue; } + + var victimEntry = _playerEntries.Find(entry => entry.SteamId == blindEvent.VictimSteamId); + if (victimEntry == null) + { + continue; + } + + throwerEntry.Durations[victimEntry.SteamId] += blindEvent.Duration; } + } + } + + private void GenerateSheet() + { + IRow firstRow = Sheet.CreateRow(0); + SetCellValue(firstRow, 0, CellType.String, "Flasher\\Flashed"); + + for (int playerIndex = 0; playerIndex < _playerEntries.Count; playerIndex++) + { + var playerEntry = _playerEntries[playerIndex]; + SetCellValue(firstRow, playerIndex + 1, CellType.String, playerEntry.Name); - FillEmptyCells(rowCount, columnCount); - demo.PlayerBlinded.Clear(); + IRow row = Sheet.CreateRow(playerIndex + 1); + SetCellValue(row, 0, CellType.String, playerEntry.Name); + + for (int durationIndex = 0; durationIndex < playerEntry.Durations.Count; durationIndex++) + { + var duration = playerEntry.Durations.ElementAt(durationIndex); + SetCellValue(row, durationIndex + 1, CellType.Numeric, Math.Round(duration.Value, 2)); + } } } + + private bool IsMaxPlayerLimitReached() + { + return _playerNamePerSteamId.Count >= 256; + } } } diff --git a/Services/Concrete/Excel/Sheets/Multiple/FlashMatrixTeamsSheet.cs b/Services/Concrete/Excel/Sheets/Multiple/FlashMatrixTeamsSheet.cs index 24b4da114..a025fb841 100644 --- a/Services/Concrete/Excel/Sheets/Multiple/FlashMatrixTeamsSheet.cs +++ b/Services/Concrete/Excel/Sheets/Multiple/FlashMatrixTeamsSheet.cs @@ -46,7 +46,7 @@ public FlashMatrixTeamsSheet(IWorkbook workbook, List demos) Sheet = workbook.CreateSheet("Flash matrix teams"); } - public override async Task GenerateContent() + public override Task GenerateContent() { // first row containing victims name _firstRow = Sheet.CreateRow(0); @@ -54,9 +54,6 @@ public override async Task GenerateContent() foreach (Demo demo in Demos) { - CacheService cacheService = new CacheService(); - demo.PlayerBlinded = await cacheService.GetDemoPlayerBlindedAsync(demo); - // create rows and columns with only demo's teams name if this team is not if (!_teamsRow.ContainsKey(demo.TeamCT.Name)) { @@ -101,6 +98,8 @@ public override async Task GenerateContent() FillEmptyCells(_rowCount, _columnCount); demo.PlayerBlinded.Clear(); } + + return Task.CompletedTask; } private void CreateRowAndColumnForTeam(string teamName) diff --git a/Services/Concrete/Excel/Sheets/Multiple/KillMatrixSheet.cs b/Services/Concrete/Excel/Sheets/Multiple/KillMatrixSheet.cs index 465af2093..d258200ee 100644 --- a/Services/Concrete/Excel/Sheets/Multiple/KillMatrixSheet.cs +++ b/Services/Concrete/Excel/Sheets/Multiple/KillMatrixSheet.cs @@ -2,86 +2,142 @@ using System.Linq; using System.Threading.Tasks; using Core.Models; +using Core.Models.Events; using NPOI.SS.UserModel; +using static System.String; +using CellType = NPOI.SS.UserModel.CellType; namespace Services.Concrete.Excel.Sheets.Multiple { + internal class PlayerKillEntry + { + public long SteamId { get; set; } + public string Name { get; set; } + + public Dictionary Kills { get; set; } + } + public class KillMatrixSheet : AbstractMultipleSheet { + private readonly List _playerEntries = new List(); + private Dictionary _playerNamePerSteamId = new Dictionary(); + public KillMatrixSheet(IWorkbook workbook, List demos) { - Headers = new Dictionary { { string.Empty, CellType.String } }; + Headers = new Dictionary { { Empty, CellType.String } }; Demos = demos; Sheet = workbook.CreateSheet("Kill matrix"); } - public override async Task GenerateContent() + public override Task GenerateContent() { - await Task.Factory.StartNew(() => + ComputePlayerNamePerSteamId(); + InitializePlayerEntries(); + PopulatePlayerEntries(); + GenerateSheet(); + + return Task.CompletedTask; + } + + private void ComputePlayerNamePerSteamId() + { + foreach (Demo demo in Demos) { - // store players row and columns index - Dictionary playersRow = new Dictionary(); - Dictionary playersColumn = new Dictionary(); + foreach (Player player in demo.Players) + { + if (player.IsBot || _playerNamePerSteamId.ContainsKey(player.SteamId)) + { + continue; + } + + _playerNamePerSteamId.Add(player.SteamId, player.Name); - // first row containing victims name - IRow firstRow = Sheet.CreateRow(0); - SetCellValue(firstRow, 0, CellType.String, "Killer\\Victim"); + if (IsMaxPlayerLimitReached()) + { + break; + } + } - int columnCount = 1; - int rowCount = 1; - foreach (Demo demo in Demos) + if (IsMaxPlayerLimitReached()) { - // create rows and columns with only players name - foreach (Player player in demo.Players) + break; + } + } + + _playerNamePerSteamId = _playerNamePerSteamId.OrderBy(k => k.Value).ToDictionary(x => x.Key, x => x.Value); + } + + private void InitializePlayerEntries() + { + foreach (var playerName in _playerNamePerSteamId) + { + var kills = new Dictionary(); + foreach (var sortedPlayerName in _playerNamePerSteamId) + { + kills.Add(sortedPlayerName.Key, 0); + } + + _playerEntries.Add(new PlayerKillEntry + { + SteamId = playerName.Key, + Name = playerName.Value, + Kills = kills, + }); + } + } + + private void PopulatePlayerEntries() + { + foreach (Demo demo in Demos) + { + foreach (KillEvent kill in demo.Kills) + { + if (kill.IsKillerBot || kill.IsVictimBot) { - if (!playersRow.ContainsKey(player.SteamId)) - { - // add a column for this player in the first row - SetCellValue(firstRow, columnCount, CellType.String, player.Name); - playersRow.Add(player.SteamId, rowCount); - // create a row for this player - IRow row = Sheet.CreateRow(rowCount++); - SetCellValue(row, 0, CellType.String, player.Name); - playersColumn.Add(player.SteamId, columnCount++); - } + continue; } - // insert kills value - foreach (Player player in demo.Players) + PlayerKillEntry killerEntry = _playerEntries.Find(entry => entry.SteamId == kill.KillerSteamId); + if (killerEntry == null) { - if (playersRow.ContainsKey(player.SteamId)) - { - int rowIndex = playersRow[player.SteamId]; - foreach (Player pl in demo.Players) - { - if (playersColumn.ContainsKey(pl.SteamId)) - { - int columnIndex = playersColumn[pl.SteamId]; - int killCount = demo.Kills.Count(e => e.KillerSteamId == player.SteamId && e.KilledSteamId == pl.SteamId); - IRow row = Sheet.GetRow(rowIndex); - SetCellValue(row, columnIndex, CellType.Numeric, killCount); - } - } - } + continue; } - // fill empty cells with 0 - for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) + PlayerKillEntry victimEntry = _playerEntries.Find(entry => entry.SteamId == kill.KilledSteamId); + if (victimEntry == null) { - for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) - { - IRow row = Sheet.GetRow(rowIndex); - ICell cell = row.GetCell(columnIndex); - if (cell == null) - { - SetCellValue(row, columnIndex, CellType.Numeric, 0); - } - } + continue; } + + killerEntry.Kills[victimEntry.SteamId]++; } + } + } - FillEmptyCells(rowCount, columnCount); - }); + private void GenerateSheet() + { + IRow firstRow = Sheet.CreateRow(0); + SetCellValue(firstRow, 0, CellType.String, "Killer\\Victim"); + + for (int playerIndex = 0; playerIndex < _playerEntries.Count; playerIndex++) + { + PlayerKillEntry playerEntry = _playerEntries[playerIndex]; + SetCellValue(firstRow, playerIndex + 1, CellType.String, playerEntry.Name); + + IRow row = Sheet.CreateRow(playerIndex + 1); + SetCellValue(row, 0, CellType.String, playerEntry.Name); + + for (int killIndex = 0; killIndex < playerEntry.Kills.Count; killIndex++) + { + var killEntry = playerEntry.Kills.ElementAt(killIndex); + SetCellValue(row, killIndex + 1, CellType.Numeric, killEntry.Value); + } + } + } + + private bool IsMaxPlayerLimitReached() + { + return _playerNamePerSteamId.Count >= 256; } } } diff --git a/Services/Concrete/Excel/Sheets/Multiple/RoundsSheet.cs b/Services/Concrete/Excel/Sheets/Multiple/RoundsSheet.cs index 2fe1e25d9..b0ed74197 100644 --- a/Services/Concrete/Excel/Sheets/Multiple/RoundsSheet.cs +++ b/Services/Concrete/Excel/Sheets/Multiple/RoundsSheet.cs @@ -53,12 +53,6 @@ public RoundsSheet(IWorkbook workbook, List demos) public override async Task GenerateContent() { - foreach (Demo demo in Demos) - { - CacheService cacheService = new CacheService(); - demo.WeaponFired = await cacheService.GetDemoWeaponFiredAsync(demo); - } - await Task.Factory.StartNew(() => { var rowNumber = 1; diff --git a/Services/Concrete/Excel/SingleExport.cs b/Services/Concrete/Excel/SingleExport.cs index 8b1ed4246..7584a223b 100644 --- a/Services/Concrete/Excel/SingleExport.cs +++ b/Services/Concrete/Excel/SingleExport.cs @@ -42,8 +42,6 @@ public SingleExport(Demo demo) public override async Task Generate() { - CacheService cacheService = new CacheService(); - _demo.WeaponFired = await cacheService.GetDemoWeaponFiredAsync(_demo); _generalSheet = new GeneralSheet(Workbook, _demo); await _generalSheet.Generate(); _playersSheet = new PlayersSheet(Workbook, _demo); diff --git a/Services/Concrete/FlashbangService.cs b/Services/Concrete/FlashbangService.cs index 682e46ec4..91c6823e5 100644 --- a/Services/Concrete/FlashbangService.cs +++ b/Services/Concrete/FlashbangService.cs @@ -124,7 +124,7 @@ await Task.Factory.StartNew(() => foreach (Player player in Demo.Players) { float totalDuration = Demo.PlayerBlinded - .Where(e => e.ThrowerSteamId == player.SteamId) + .Where(e => e.ThrowerSteamId == player.SteamId && !e.IsVictimBot) .Sum(e => e.Duration); if (!playerFlashStats.ContainsKey(player))