public void Log <TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func <TState, Exception, string> formatter) { // If we're whitelisting (included prefixes), ignore anything not included. Otherwise, check the blacklist (excluded) if (!string.IsNullOrEmpty(_config.IncludedPrefixes)) { // Only log prefixes that match foreach (var prefix in _config.IncludedPrefixes.Split(',')) { if (_name.StartsWith(prefix)) { _discord.Log(state.ToString(), _name, logLevel); return; } } } else if (!string.IsNullOrEmpty(_config.ExcludedPrefixes)) { // Ignore prefixes that match foreach (var prefix in _config.ExcludedPrefixes.Split(',')) { if (_name.StartsWith(prefix)) { return; } } _discord.Log(state.ToString(), _name, logLevel); } else { // No included or excluded, so log everything. _discord.Log(state.ToString(), _name, logLevel); } }
public void Execute(IJobExecutionContext context) { _logger.Debug("** Stepping into the execution of EncounterPlayerStatistics!"); Debug.WriteLine("** Stepping into the execution of EncounterPlayerStatistics!"); var task = _taskRepository.Get("EncounterPlayerStatistics"); if (task == null) { Debug.WriteLine("Can't update EncounterPlayerStatistics - no matching task definition exists in the database."); _logger.Debug("Can't update EncounterPlayerStatistics - no matching task definition exists in the database."); _discord.Log( "Can't update EncounterPlayerStatistics - no matching task definition exists in the database.", "EncounterPlayerStatistics", LogLevel.Error); return; } // Check if enough time has passed for us to run this task again if (task.LastRun.AddMinutes(task.ScheduleMinutes) > DateTime.Now) { _logger.Debug("Not enough time has passed for this scheduled task, so it won't be executed now"); Debug.WriteLine("Not enough time has passed for this scheduled task, so it won't be executed now"); _discord.Log( "Not enough time has passed for this scheduled task, so it won't be executed now", "EncounterPlayerStatistics", LogLevel.Warning); return; } // Update the task lastrun time first, so if it takes a minute to run, we don't run it on another server at the same time //TODO: Uncomment this before we push to production //_taskRepository.UpdateTask(task.Id, DateTime.Now); Stopwatch sw = new Stopwatch(); // Don't bother saving stats for encounters that aren't valid for rankings. // There's no point saving DPS/HPS/APS for encounters that are not successful, either. var maxEncounters = 100; var encList = _encounterRepository.GetEncountersMissingPlayerStatistics(maxEncounters); if (!encList.Any()) { Debug.WriteLine("Found no encounters that require statistics updates!"); _logger.Debug("Found no encounters that require statistics updates!"); //_discord.Log( // "Found no encounters that require statistics updates!", // "EncounterPlayerStatistics", LogLevel.Warning); } else { _logger.Debug(string.Format("Found {0} encounters to save statistics for", encList.Count)); Debug.WriteLine(string.Format("Found {0} encounters to save statistics for", encList.Count)); _discord.Log( string.Format("Found {0} encounters to save statistics for (max {1})", encList.Count, maxEncounters), "EncounterPlayerStatistics", LogLevel.Information); // Optionally filter our list for a specific bossfight here for testing //encList = encList.Where(e => e.BossFightId == 41).ToList(); //_logger.Debug(string.Format("Updating stats for a filtered set of {0} encounters", encList.Count)); // Get the list of NPC names to check against when determining single target dps var bossFightSingleTargetDetails = _bossFightSingleTargetDetailRepository.GetAll(); var addPlayerStats = new List <Database.Models.EncounterPlayerStatistics>(); #region Loop through encounters that we previously identified as needing stats for (var i = 1; i <= encList.Count; i++) { sw.Reset(); sw.Start(); var enc = encList[i - 1]; if ((int)enc.Duration.TotalSeconds == 0) { _logger.Debug( string.Format( "Skipping stats for encounter {0}/{1} as it has a 0-second duration that needs to be fixed.", i, encList.Count)); _discord.Log( string.Format( "Skipping stats for encounter {0}/{1} as it has a 0-second duration that needs to be fixed.", i, encList.Count), "EncounterPlayerStatistics", LogLevel.Information); continue; } //_logger.Debug(string.Format("Calculating stats for encounter {0}/{1}", i, encList.Count)); addPlayerStats = new List <Database.Models.EncounterPlayerStatistics>(); // DPS/HPS/APS #region Damage foreach (var dmg in _encounterRepository.GetOverviewPlayerDamageDone(enc.Id)) { var thisPlayer = addPlayerStats.FirstOrDefault(p => p.PlayerId == dmg.PlayerId); if (thisPlayer == null) { addPlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = dmg.PlayerId, Deaths = 0, APS = 0, DPS = dmg.TotalToNpcs / (long)enc.Duration.TotalSeconds, HPS = 0, }); } else { thisPlayer.DPS = dmg.TotalToNpcs / (long)enc.Duration.TotalSeconds; } } #endregion #region Healing foreach (var heal in _encounterRepository.GetOverviewPlayerHealingDone(enc.Id)) { var thisPlayer = addPlayerStats.FirstOrDefault(p => p.PlayerId == heal.PlayerId); if (thisPlayer == null) { addPlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = heal.PlayerId, Deaths = 0, APS = 0, DPS = 0, HPS = (heal.TotalToOtherPlayers + heal.TotalToSelf) / (long)enc.Duration.TotalSeconds }); } else { thisPlayer.HPS = (heal.TotalToOtherPlayers + heal.TotalToSelf) / (long)enc.Duration.TotalSeconds; } } #endregion #region Shielding foreach (var shield in _encounterRepository.GetOverviewPlayerShieldingDone(enc.Id)) { var thisPlayer = addPlayerStats.FirstOrDefault(p => p.PlayerId == shield.PlayerId); if (thisPlayer == null) { addPlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = shield.PlayerId, Deaths = 0, APS = (shield.TotalToSelf + shield.TotalToOtherPlayers) / (long)enc.Duration.TotalSeconds, DPS = 0, HPS = 0 }); } else { thisPlayer.APS = (shield.TotalToOtherPlayers + shield.TotalToSelf) / (long)enc.Duration.TotalSeconds; } } #endregion #region Deaths foreach (var playerDeathCount in _encounterRepository.CountDeathsPerPlayer(enc.Id)) { var thisPlayer = addPlayerStats.FirstOrDefault(p => p.PlayerId == playerDeathCount.PlayerId); if (thisPlayer == null) { addPlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = playerDeathCount.PlayerId, Deaths = playerDeathCount.Deaths, APS = 0, DPS = 0, HPS = 0 }); } else { thisPlayer.Deaths = playerDeathCount.Deaths; } } #endregion // Top Hits #region Damage var topDmg = _encounterRepository.GetTopDamageHits(enc.Id); foreach (var dmg in topDmg) { var thisPlayer = addPlayerStats.FirstOrDefault(p => p.PlayerId == dmg.PlayerId); if (thisPlayer == null) { addPlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = dmg.PlayerId, TopDpsAbilityId = dmg.TopDpsAbilityId, TopDpsAbilityValue = dmg.TopDpsAbilityValue }); } else { thisPlayer.TopDpsAbilityId = dmg.TopDpsAbilityId; thisPlayer.TopDpsAbilityValue = dmg.TopDpsAbilityValue; } } #endregion #region Healing var topHeal = _encounterRepository.GetTopHealingHits(enc.Id); foreach (var heal in topHeal) { var thisPlayer = addPlayerStats.FirstOrDefault(p => p.PlayerId == heal.PlayerId); if (thisPlayer == null) { addPlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = heal.PlayerId, TopHpsAbilityId = heal.TopHpsAbilityId, TopHpsAbilityValue = heal.TopHpsAbilityValue }); } else { thisPlayer.TopHpsAbilityId = heal.TopHpsAbilityId; thisPlayer.TopHpsAbilityValue = heal.TopHpsAbilityValue; } } #endregion #region Shielding var topShield = _encounterRepository.GetTopShieldHits(enc.Id); foreach (var shield in topShield) { var thisPlayer = addPlayerStats.FirstOrDefault(p => p.PlayerId == shield.PlayerId); if (thisPlayer == null) { addPlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = shield.PlayerId, TopApsAbilityId = shield.TopApsAbilityId, TopApsAbilityValue = shield.TopApsAbilityValue }); } else { thisPlayer.TopApsAbilityId = shield.TopApsAbilityId; thisPlayer.TopApsAbilityValue = shield.TopApsAbilityValue; } } #endregion #region Single target DPS var bossFight = bossFightSingleTargetDetails.FirstOrDefault(b => b.BossFightId == enc.BossFightId); if (bossFight != null) { if (bossFight.TargetName.Contains(",")) { var npcsToCheck = bossFight.TargetName.Split(','); foreach (var npcName in npcsToCheck) { var dmgRecords = _encounterRepository.GetPlayerSingleTargetDamageDone(enc.Id, npcName); if (dmgRecords.Any()) { foreach (var dmg in dmgRecords) { if (dmg.PlayerId == 0) { continue; } var thisPlayer = addPlayerStats.FirstOrDefault(p => p.PlayerId == dmg.PlayerId); if (thisPlayer == null) { addPlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = dmg.PlayerId, SingleTargetDps = dmg.Damage / (long)enc.Duration.TotalSeconds }); } else { thisPlayer.SingleTargetDps = dmg.Damage / (long)enc.Duration.TotalSeconds; } } // If we've found one name that matches records, then don't bother checking the other names (might save a few queries) break; } } } else { foreach ( var dmg in _encounterRepository.GetPlayerSingleTargetDamageDone(enc.Id, bossFight.TargetName)) { if (dmg.PlayerId == 0) { continue; } var thisPlayer = addPlayerStats.FirstOrDefault(p => p.PlayerId == dmg.PlayerId); if (thisPlayer == null) { addPlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = dmg.PlayerId, SingleTargetDps = dmg.Damage / (long)enc.Duration.TotalSeconds }); } else { thisPlayer.SingleTargetDps = dmg.Damage / (long)enc.Duration.TotalSeconds; } } } } #endregion sw.Stop(); var generateTime = sw.Elapsed.ToString(); sw.Reset(); sw.Start(); // Finished generating statistics, save this encounter's stats now var result = _encounterRepository.SaveEncounterPlayerStatistics(addPlayerStats); sw.Stop(); _logger.Debug( string.Format( "Finished saving stats for encounter {0} ({3}/{4}). Stats generated in {1} and saved in {2}.", enc.Id, generateTime, sw.Elapsed, i, encList.Count)); Debug.WriteLine(string.Format( "Finished saving stats for encounter {0}. Stats generated in {1} and saved in {2}.", enc.Id, generateTime, sw.Elapsed)); _discord.Log( string.Format( "Finished saving stats for encounter {0}. Stats generated in {1} and saved in {2}.", enc.Id, generateTime, sw.Elapsed), "EncounterPlayerStatistics", LogLevel.Warning); //addPlayerStats.AddRange(addPlayerStats); } #endregion } // Now that we have added stats that didn't exist before, check if there are any single target stats that haven't been calculated at all yet var stEncList = _encounterRepository.GetEncountersMissingSingleTargetDpsStatistics(300); if (!stEncList.Any()) { _logger.Debug("Found no encounters that require ST DPS updates!"); } else { _logger.Debug(string.Format("Found {0} encounters to save ST DPS statistics for", stEncList.Count)); var updatePlayerStats = new List <Database.Models.EncounterPlayerStatistics>(); var bossFightSingleTargetDetails = _bossFightSingleTargetDetailRepository.GetAll(); for (var i = 1; i <= stEncList.Count; i++) { sw.Reset(); sw.Start(); var enc = stEncList[i - 1]; if ((int)enc.Duration.TotalSeconds == 0) { _logger.Debug(string.Format("Skipping ST DPS stats for encounter {0}/{1} as it has a 0-second duration that needs to be fixed.", i, stEncList.Count)); continue; } var bossFight = bossFightSingleTargetDetails.FirstOrDefault(b => b.BossFightId == enc.BossFightId); if (bossFight == null) { _logger.Debug(string.Format("Skipping ST DPS stats for encounter {0}/{1} as a matching bossfight couldn't be found.", i, stEncList.Count)); continue; } _logger.Debug(string.Format("Calculating ST DPS stats for encounter {0}/{1}", i, stEncList.Count)); //updatePlayerStats = new List<EncounterPlayerStatistics>(); #region Damage // Handle comma-separated names here! if (bossFight.TargetName.Contains(",")) { var npcsToCheck = bossFight.TargetName.Split(','); foreach (var npcName in npcsToCheck) { var dmgRecords = _encounterRepository.GetPlayerSingleTargetDamageDone(enc.Id, npcName); if (dmgRecords.Any()) { foreach (var dmg in dmgRecords) { if (dmg.PlayerId == 0) { continue; } var thisPlayer = updatePlayerStats.FirstOrDefault(p => p.PlayerId == dmg.PlayerId && p.EncounterId == enc.Id); if (thisPlayer == null) { updatePlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = dmg.PlayerId, SingleTargetDps = dmg.Damage / (long)enc.Duration.TotalSeconds }); } } // If we've found one name that matches records, then don't bother checking the other names (might save a few queries) break; } } } else { var stDmgRecords = _encounterRepository.GetPlayerSingleTargetDamageDone(enc.Id, bossFight.TargetName); if (stDmgRecords.Any()) { foreach (var dmg in stDmgRecords) { if (dmg.PlayerId == 0) { continue; } var thisPlayer = updatePlayerStats.FirstOrDefault(p => p.PlayerId == dmg.PlayerId && p.EncounterId == enc.Id); if (thisPlayer == null) { updatePlayerStats.Add(new Database.Models.EncounterPlayerStatistics() { EncounterId = enc.Id, PlayerId = dmg.PlayerId, SingleTargetDps = dmg.Damage / (long)enc.Duration.TotalSeconds }); } } } else { // Check if this encounter actually has any damage records at all. // If it doesn't, remove the encounter entirely. var recordCounts = _encounterRepository.CountBasicRecordsForEncounter(enc.Id); if (recordCounts.DamageCount == 0) { // Remove this encounter so we don't try and use it again _logger.Debug($"Removing encounter {enc.Id} as there are no damage records in the database."); _encounterRepository.RemoveEncounter("system", enc.Id); } } } #endregion sw.Stop(); var generateTime = sw.Elapsed.ToString(); _logger.Debug(string.Format("Finished calculating {2} ST DPS stats for encounter {0}. Stats generated in {1}.", enc.Id, generateTime, updatePlayerStats.Count)); //sw.Reset(); //sw.Start(); //// Finished generating statistics, save this encounter's stats now //var result = _encounterRepository.UpdateEncounterSingleTargetDpsStatistics(updatePlayerStats); //sw.Stop(); //_logger.Debug(string.Format("Finished updating ST DPS stats for encounter {0}. Stats generated in {1} and updated in {2}.", enc.Id, generateTime, sw.Elapsed)); //addPlayerStats.AddRange(addPlayerStats); } var result = _encounterRepository.UpdateEncounterSingleTargetDpsStatistics(updatePlayerStats); _logger.Debug(string.Format("Finished updating {0} ST DPS stats for {1} encounters.", updatePlayerStats.Count, stEncList.Count)); } }