private static void MeasureTimes(string filename, EVTCParser parser, LogProcessor processor, GW2ApiData apiData, TextWriter outputWriter) { var stopwatch = Stopwatch.StartNew(); var log = parser.ParseLog(filename); var parsedTime = stopwatch.Elapsed; stopwatch.Restart(); var processedLog = processor.ProcessLog(log); var processedTime = stopwatch.Elapsed; stopwatch.Restart(); var analyzer = new LogAnalyzer(processedLog, apiData); var result = analyzer.GetResult(); var duration = analyzer.GetEncounterDuration(); var mode = analyzer.GetMode(); var statisticsTime = stopwatch.Elapsed; var totalTime = parsedTime + processedTime + statisticsTime; outputWriter.WriteLine( $"{filename},{parsedTime.TotalMilliseconds},{processedTime.TotalMilliseconds},{statisticsTime.TotalMilliseconds},{totalTime.TotalMilliseconds}"); outputWriter.Flush(); }
protected override Log GetLog() { if (processedLog == null) { var parser = new EVTCParser(); var processor = new LogProcessor(); var parsedLog = parser.ParseLog(filename); processedLog = processor.ProcessLog(parsedLog); } return(processedLog); }
static void Main() { string filename = "example.zevtc"; var parser = new EVTCParser(); // Used to read a log file and get raw data out of it var processor = new LogProcessor(); // Used to process the raw data // The parsed log contains raw data from the EVTC file ParsedLog parsedLog = parser.ParseLog(filename); // The log after processing the raw data into structured events and agents. Log log = processor.ProcessLog(parsedLog); // At this point, we can do anything with the processed data, and use the LogAnalyzer // for easy access to most common results with caching. var analyzer = new LogAnalyzer(log); Encounter encounter = analyzer.GetEncounter(); // Encounter names are available for some languages, we use the target name if it's not. if (EncounterNames.TryGetEncounterNameForLanguage(GameLanguage.English, encounter, out string name)) { Console.WriteLine($"Encounter: {name}"); } else { Console.WriteLine($"Encounter: {log.MainTarget?.Name ?? "unknown target"}"); } Console.WriteLine($"Result: {analyzer.GetResult()}"); Console.WriteLine($"Mode: {analyzer.GetMode()}"); Console.WriteLine($"Duration: {analyzer.GetEncounterDuration()}"); // The processed log allows easy access to data about agents foreach (var player in log.Agents.OfType <Player>()) { Console.WriteLine($"{player.Name} - {player.AccountName} - {player.Profession} - {player.EliteSpecialization}"); } // Events may be accessed as well foreach (var deadEvent in log.Events.OfType <AgentDeadEvent>()) { if (deadEvent.Agent is Player player) { Console.WriteLine($"{player.Name} died at {deadEvent.Time}."); } } }
protected override Log GetLog() { byte[] bytes; using (var memoryStream = new MemoryStream()) { stream.CopyTo(memoryStream); bytes = memoryStream.ToArray(); } if (processedLog == null) { var parser = new EVTCParser(); var processor = new LogProcessor(); var parsedLog = parser.ParseLog(bytes); processedLog = processor.ProcessLog(parsedLog); } return(processedLog); }
private static void MeasureTimes(string filename, EVTCParser parser, LogProcessor processor, LogAnalyser analyser, GW2ApiData apiData, TextWriter outputWriter) { var stopwatch = Stopwatch.StartNew(); var log = parser.ParseLog(filename); var parsedTime = stopwatch.Elapsed; stopwatch.Restart(); var processedLog = processor.GetProcessedLog(log); var processedTime = stopwatch.Elapsed; stopwatch.Restart(); var statistics = analyser.GetStatistics(processedLog, apiData); var statisticsTime = stopwatch.Elapsed; var totalTime = parsedTime + processedTime + statisticsTime; outputWriter.WriteLine( $"{filename},{parsedTime.TotalMilliseconds},{processedTime.TotalMilliseconds},{statisticsTime.TotalMilliseconds},{totalTime.TotalMilliseconds}"); outputWriter.Flush(); }
public LogAnalytics(EVTCParser parser, LogProcessor processor, Func <Log, LogAnalyzer> analyzerFactory) { Parser = parser ?? throw new ArgumentNullException(nameof(parser)); Processor = processor ?? throw new ArgumentNullException(nameof(processor)); AnalyzerFactory = analyzerFactory ?? throw new ArgumentNullException(nameof(analyzerFactory)); }
public CheckResult CheckLog(string filename) { try { var parser = new EVTCParser(); var processor = new LogProcessor(); var bytes = ReadLogFileBytes(filename); var parsedLog = parser.ParseLog(bytes); var log = processor.ProcessLog(parsedLog); var analyzer = new LogAnalyzer(log); var encounter = log.EncounterData.Encounter; var mode = analyzer.GetMode(); var result = analyzer.GetResult(); var players = analyzer.GetPlayers() .Select(p => new LogPlayer { CharacterName = p.Name, AccountName = p.AccountName, Profession = p.Profession, EliteSpecialization = p.EliteSpecialization, Subgroup = p.Subgroup }).ToList(); var duration = analyzer.GetEncounterDuration(); // This combination of builds resulted in logs that did not contain NPCs other than the main target. // There is not much of a point in checking these. // This outdated version of arcdps was commonly used for extended periods of time due to it being // the last version that had working arcdps build templates. if (log.EvtcVersion == "EVTC20191001" && (log.GameBuild ?? 0) >= 100565) { return(new CheckResult { Ignored = true, Correct = false, ProcessingFailed = false, Encounter = Result <Encounter> .UncheckedResult(encounter), Mode = Result <EncounterMode> .UncheckedResult(mode), Result = Result <EncounterResult> .UncheckedResult(result), Players = Result <List <LogPlayer> > .UncheckedResult(players), Duration = Result <TimeSpan> .UncheckedResult(duration) }); } var eiSettings = new EvtcParserSettings(false, false, true, false, false, 0); var eiParser = new EvtcParser(eiSettings, eiApiController); var eiLog = eiParser.ParseLog(new EIController(), new MemoryStream(bytes), out var eiFailureReason); if (eiLog == null) { eiFailureReason.Throw(); } var eiDuration = TimeSpan.FromMilliseconds(eiLog.FightData.FightEnd - eiLog.FightData.FightStart); var eiResult = eiLog.FightData.Success ? EncounterResult.Success : EncounterResult.Failure; var eiPlayers = eiLog.PlayerList .Where(p => p.Prof != "Sword") .Select(p => { Profession profession; if (Enum.TryParse(p.Prof, out EliteSpecialization specialization)) { profession = GameData.Characters.GetProfession(specialization); } else { specialization = EliteSpecialization.None; if (!Enum.TryParse(p.Prof, out profession)) { throw new Exception($"Unknown profession {p.Prof} found in Elite Insights data."); } } return(new LogPlayer { CharacterName = p.Character, // EI strips the leading : in account names, so we re-add it AccountName = $":{p.Account}", Profession = profession, EliteSpecialization = specialization, Subgroup = p.Group }); }).ToList(); var eiMode = eiLog.FightData.IsCM ? EncounterMode.Challenge : EncounterMode.Normal; // There is no reasonable way to compare EI and Analytics encounters var encounterResult = Result <Encounter> .UncheckedResult(encounter); var resultResult = CheckResult ? Result <EncounterResult> .CheckedResult(eiResult, result) : Result <EncounterResult> .UncheckedResult(result); var modeResult = CheckMode ? Result <EncounterMode> .CheckedResult(eiMode, mode) : Result <EncounterMode> .UncheckedResult(mode); var playerResult = CheckPlayers ? players.ToHashSet().SetEquals(eiPlayers) ? Result <List <LogPlayer> > .CorrectResult(players) : Result <List <LogPlayer> > .IncorrectResult(eiPlayers, players) : Result <List <LogPlayer> > .UncheckedResult(players); var durationResult = CheckDuration ? (eiDuration - duration) < DurationEpsilon ? Result <TimeSpan> .CorrectResult(duration) : Result <TimeSpan> .IncorrectResult(eiDuration, duration) : Result <TimeSpan> .UncheckedResult(duration); bool correct = encounterResult.Correct && resultResult.Correct && modeResult.Correct && playerResult.Correct && durationResult.Correct; return(new CheckResult { Correct = correct, ProcessingFailed = false, Encounter = encounterResult, Mode = modeResult, Result = resultResult, Players = playerResult, Duration = durationResult }); } catch (TooShortException) { return(new CheckResult { Ignored = true, Correct = false }); } catch (Exception e) { return(new CheckResult { Correct = false, ProcessingFailed = true, ProcessingException = e }); } }
public CheckResult CheckLog(LogDefinition definition) { try { var parser = new EVTCParser(); var processor = new LogProcessor(); var parsedLog = parser.ParseLog(definition.Filename); var log = processor.ProcessLog(parsedLog); var analyzer = new LogAnalyzer(log); var encounter = log.EncounterData.Encounter; var mode = analyzer.GetMode(); var result = analyzer.GetResult(); var players = analyzer.GetPlayers() .Select(p => new LogPlayer { CharacterName = p.Name, AccountName = p.AccountName, Profession = p.Profession, EliteSpecialization = p.EliteSpecialization, Subgroup = p.Subgroup }).ToList(); var encounterResult = definition.Encounter.HasValue ? Result <Encounter> .CheckedResult(definition.Encounter.Value, encounter) : Result <Encounter> .UncheckedResult(encounter); var resultResult = definition.Result.HasValue ? Result <EncounterResult> .CheckedResult(definition.Result.Value, result) : Result <EncounterResult> .UncheckedResult(result); var modeResult = definition.Mode.HasValue ? Result <EncounterMode> .CheckedResult(definition.Mode.Value, mode) : Result <EncounterMode> .UncheckedResult(mode); var playerResult = definition.Players != null ? players.ToHashSet().SetEquals(definition.Players) ? Result <List <LogPlayer> > .CorrectResult(players) : Result <List <LogPlayer> > .IncorrectResult(definition.Players, players) : Result <List <LogPlayer> > .UncheckedResult(players); bool correct = encounterResult.Correct && resultResult.Correct && modeResult.Correct && playerResult.Correct; return(new CheckResult { Correct = correct, ProcessingFailed = false, Encounter = encounterResult, Mode = modeResult, Result = resultResult, Players = playerResult }); } catch (Exception e) { return(new CheckResult { Correct = false, ProcessingFailed = true, ProcessingException = e }); } }
public LogAnalytics(EVTCParser parser, LogProcessor processor, LogAnalyser analyser) { Parser = parser ?? throw new ArgumentNullException(nameof(parser)); Processor = processor ?? throw new ArgumentNullException(nameof(processor)); Analyser = analyser ?? throw new ArgumentNullException(nameof(analyser)); }
} = null; // TODO: Add support public MakerForm() { Title = "Scratch HTML log maker"; ClientSize = new Size(800, 600); var formLayout = new DynamicLayout(); Content = formLayout; var openFileDialog = new OpenFileDialog { MultiSelect = true }; openFileDialog.Filters.Add(new FileFilter("EVTC logs", ".evtc", ".evtc.zip", ".zevtc")); var openFilesButton = new Button { Text = "Select EVTC logs" }; var processButton = new Button { Text = "Create HTML logs" }; var notDoneListBox = new ListBox(); var doneListBox = new ListBox(); doneListBox.DataStore = finishedFileNames.ToArray(); var splitter = new Splitter { Panel1 = notDoneListBox, Panel2 = doneListBox, Position = 400 }; formLayout.BeginVertical(spacing: new Size(10, 10)); formLayout.AddRow(openFilesButton, processButton); formLayout.EndVertical(); formLayout.BeginVertical(); formLayout.Add(splitter); formLayout.EndVertical(); openFilesButton.Click += (sender, args) => { if (openFileDialog.ShowDialog((Control)sender) == DialogResult.Ok) { foreach (var file in openFileDialog.Filenames) { notFinishedFileNames.Enqueue(file); } notDoneListBox.DataStore = notFinishedFileNames.Select(Path.GetFileName).ToArray(); } }; processButton.Click += (sender, args) => { Task.Run(() => { var times = new List <(string taskName, double milliseconds)>(); var totalTimeStopwatch = Stopwatch.StartNew(); var parser = new EVTCParser(); var processor = new LogProcessor(); var generator = new HtmlGenerator(ApiData); int finishedTaskCount = 0; while (notFinishedFileNames.Count > 0) { var taskStopwatch = Stopwatch.StartNew(); string filename = ""; filename = notFinishedFileNames.Dequeue(); var fileDirectory = Path.GetDirectoryName(filename); var newName = Path.GetFileNameWithoutExtension(filename); if (newName.EndsWith(".evtc")) { newName = newName.Substring(0, newName.Length - 5); } var resultFilename = Path.Combine(fileDirectory, ResultFilePrefix + newName + ".html"); try { var lastElapsed = taskStopwatch.Elapsed; var parsedLog = parser.ParseLog(filename); times.Add(("parsing", (taskStopwatch.Elapsed - lastElapsed).TotalMilliseconds)); lastElapsed = taskStopwatch.Elapsed; var processedLog = processor.ProcessLog(parsedLog); times.Add(("processing", (taskStopwatch.Elapsed - lastElapsed).TotalMilliseconds)); lastElapsed = taskStopwatch.Elapsed; var analysis = new LogAnalyzer(processedLog, ApiData); var stats = analysis.GetStatistics(); times.Add(("stats", (taskStopwatch.Elapsed - lastElapsed).TotalMilliseconds)); lastElapsed = taskStopwatch.Elapsed; using (var htmlStringWriter = new StreamWriter(resultFilename)) { generator.WriteHtml(htmlStringWriter, stats); } times.Add(("html", (taskStopwatch.Elapsed - lastElapsed).TotalMilliseconds)); lastElapsed = taskStopwatch.Elapsed; finishedFileNames.Add(resultFilename); Application.Instance.Invoke(() => { notDoneListBox.DataStore = notFinishedFileNames.Select(Path.GetFileName).ToArray(); doneListBox.DataStore = finishedFileNames.Select(Path.GetFileName).ToArray(); }); } catch (Exception e) { finishedFileNames.Add($"FAILED: {resultFilename} ({e.Message})"); Application.Instance.Invoke(() => { notDoneListBox.DataStore = notFinishedFileNames.Select(Path.GetFileName).ToArray(); doneListBox.DataStore = finishedFileNames.Select(Path.GetFileName).ToArray(); }); } Console.WriteLine($"{newName} done, time {taskStopwatch.Elapsed}"); finishedTaskCount++; } Console.WriteLine($"All done, total time {totalTimeStopwatch.Elapsed}"); foreach ((string taskName, double totalMs) in times.GroupBy(x => x.taskName).Select(x => (x.Key, x.Sum(y => y.milliseconds))).OrderByDescending(x => x.Item2)) { Console.WriteLine($"{taskName}, total {totalMs}ms, average {totalMs/Math.Max(finishedTaskCount, 1)}ms"); } }); }; }
public void SelectLog(string logFilename) { var statusStringBuilder = new StringBuilder(); var parser = new EVTCParser(); var processor = new LogProcessor() { IgnoreUnknownEvents = false }; // Parsing var sw = Stopwatch.StartNew(); ParsedLog parsedLog = null; try { parsedLog = parser.ParseLog(logFilename); var parseTime = sw.Elapsed; statusStringBuilder.AppendLine($"Parsed in {parseTime}"); Application.Instance.Invoke(() => { parsedAgents.Clear(); parsedAgents.AddRange(parsedLog.ParsedAgents); parsedAgents.Refresh(); parsedSkills.Clear(); parsedSkills.AddRange(parsedLog.ParsedSkills); parsedSkills.Refresh(); parsedCombatItems.Clear(); parsedCombatItems.AddRange(parsedLog.ParsedCombatItems); parsedCombatItems.Refresh(); }); } catch (Exception ex) { statusStringBuilder.AppendLine($"Parsing failed: {ex.Message}\n{ex.StackTrace}"); } // Processing Log processedLog = null; try { sw.Restart(); processedLog = processor.ProcessLog(parsedLog); var processTime = sw.Elapsed; statusStringBuilder.AppendLine($"Processed in {processTime}"); Application.Instance.Invoke(() => { eventList.Clear(); eventList.AddRange(processedLog.Events); eventListControl.Events = eventList; eventListControl.Agents = processedLog.Agents.ToArray(); agents.Clear(); agents.AddRange(new FilterCollection <Agent>(processedLog.Agents)); agents.Refresh(); agentControl.Events = processedLog.Events.ToArray(); }); } catch (Exception ex) { statusStringBuilder.AppendLine($"Processing failed: {ex.Message}\n{ex.StackTrace}"); } // Statistics Statistics stats = null; sw.Restart(); try { var analyzer = new LogAnalyzer(processedLog); stats = new Statistics(processedLog.StartTime.LocalTime, processedLog.PointOfView, analyzer.GetResult(), analyzer.GetMode(), analyzer.GetEncounter(), processedLog.EvtcVersion, analyzer.GetEncounterDuration()); var statsTime = sw.Elapsed; statusStringBuilder.AppendLine($"Statistics generated in {statsTime}"); Application.Instance.Invoke(() => { statisticsJsonControl.Object = stats; }); } catch (Exception ex) { statusStringBuilder.AppendLine($"Statistics generation failed: {ex.Message}\n{ex.StackTrace}"); } Application.Instance.Invoke(() => { statusStringBuilder.AppendLine( $"Build version: {parsedLog?.LogVersion?.BuildVersion}, revision {parsedLog?.LogVersion?.Revision}"); statusStringBuilder.AppendLine( $"Parsed: {parsedLog?.ParsedAgents?.Count} agents, {parsedLog?.ParsedSkills?.Count} skills, {parsedLog?.ParsedCombatItems?.Count} combat items."); statusStringBuilder.AppendLine( $"Processed: {processedLog?.Events?.Count} events, {processedLog?.Agents?.Count} agents."); parsedStateLabel.Text = statusStringBuilder.ToString(); }); }
static void Main(string[] args) { var command = new RootCommand { new Option <bool>( "--anonymize", getDefaultValue: () => false, description: "Anonymize the character and account names of players in the log"), new Option <bool>( "--remove-rewards", getDefaultValue: () => false, description: "Remove all reward events from the log"), new Option <bool>( "--output-zevtc", getDefaultValue: () => true, description: "Compress output as a .zevtc file"), new Option <string>( "--input", description: "The path to the input file") { Required = true, }, new Option <string>( "--output", description: "The name of the output file. Replaces file if it exists.") { Required = true, } }; command.Description = "Edit arcdps EVTC logs"; command.Handler = CommandHandler.Create <bool, bool, bool, string, string>( (anonymize, removeRewards, outputZevtc, input, output) => { var parser = new EVTCParser(); var editor = new ParsedLogEditor(); var writer = new EVTCWriter(); var log = parser.ParseLog(input); if (anonymize) { Console.WriteLine("Anonymizing..."); editor.AnonymizePlayers(log); } if (removeRewards) { editor.RemoveStateChanges(log, StateChange.Reward); Console.Write("Removing rewards..."); } if (outputZevtc) { using var zip = ZipFile.Open(output, ZipArchiveMode.Create); var entry = zip.CreateEntry("1"); using var binaryWriter = new BinaryWriter(entry.Open()); writer.WriteLog(log, binaryWriter); } else { using var file = File.OpenWrite(output); using var binaryWriter = new BinaryWriter(file); writer.WriteLog(log, binaryWriter); } } ); command.Invoke(args); }