public void SumHits() { var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; chars.GetOrAdd(PLAYER2).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Amount = 100 }); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Amount = 200 }); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER2, Target = "Mob1", Amount = 350 }); //tracker.HandleEvent(new LogDeathEvent { Timestamp = DateTime.Now.AddSeconds(1), Name = "Mob1" }); var f = tracker.ActiveFights[0]; Assert.Equal(2, f.Participants.Count); Assert.Equal("Mob1", f.Target.Name); Assert.Equal(3, f.Target.InboundHitCount); Assert.Equal(650, f.Target.InboundHitSum); Assert.Equal(PLAYER1, f.Participants[0].Name); Assert.Equal(2, f.Participants[0].OutboundHitCount); Assert.Equal(300, f.Participants[0].OutboundHitSum); Assert.Equal(PLAYER2, f.Participants[1].Name); Assert.Equal(1, f.Participants[1].OutboundHitCount); Assert.Equal(350, f.Participants[1].OutboundHitSum); }
public void Two_Fights_Concurrent() { var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); Assert.Empty(tracker.ActiveFights); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Type = "slash", Amount = 100 }); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER2, Target = "Mob1", Type = "slash", Amount = 100 }); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = "Mob1", Target = PLAYER1, Type = "slash", Amount = 100 }); Assert.Equal("Mob1", tracker.ActiveFights[0].Target.Name); Assert.Single(tracker.ActiveFights); tracker.HandleEvent(new LogMissEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob2", Type = "dodge" }); Assert.Equal("Mob2", tracker.ActiveFights[1].Target.Name); Assert.Equal(2, tracker.ActiveFights.Count); }
public void Fight_Has_Correct_Timestamps() { var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); var t1 = DateTime.Now; tracker.HandleEvent(new LogHitEvent { Timestamp = t1, Source = PLAYER1, Target = "Mob1", Type = "slash", Amount = 100 }); var t2 = t1.AddSeconds(3); tracker.HandleEvent(new LogMissEvent { Timestamp = t2, Source = PLAYER1, Target = "Mob1", Type = "dodge" }); var f = tracker.ActiveFights[0]; Assert.Equal(t1, f.StartedOn); Assert.Equal(t2, f.UpdatedOn); var t3 = t2.AddSeconds(3); tracker.HandleEvent(new LogDeathEvent { Timestamp = t3, Name = "Mob1" }); Assert.Equal(FightStatus.Killed, f.Status); Assert.Equal(t3, f.UpdatedOn); }
public void Raid_Joined() { var tracker = new FightTracker(new SpellParser(), new CharTracker()); tracker.HandleEvent(new LogOpenEvent { Player = PLAYER1 }); tracker.HandleEvent(new LogPartyEvent { Status = PartyStatus.RaidJoined, Name = PLAYER2 }); Assert.Equal("Raid", tracker.Party); }
public void Accepts_Nulls() { // some events have null references // make sure these don't throw an exception var tracker = new FightTracker(new SpellParser(), new CharTracker()); tracker.HandleEvent(new LogTauntEvent() { Source = PLAYER1, Target = null }); tracker.HandleEvent(new LogDeathEvent() { Name = PLAYER1, KillShot = null }); }
public void Raid_Left_Self() { var tracker = new FightTracker(new SpellParser(), new CharTracker()); tracker.HandleEvent(new LogOpenEvent { Player = PLAYER1 }); tracker.HandleEvent(new LogPartyEvent { Status = PartyStatus.RaidJoined, Name = PLAYER2 }); tracker.HandleEvent(new LogPartyEvent { Status = PartyStatus.RaidLeft, Name = PLAYER1 }); // since player1 is the log owner, we must now drop the raid Assert.Equal("Group", tracker.Party); }
public void Raid_Left_Other() { var tracker = new FightTracker(new SpellParser(), new CharTracker()); tracker.HandleEvent(new LogOpenEvent { Player = PLAYER1 }); tracker.HandleEvent(new LogPartyEvent { Status = PartyStatus.RaidJoined, Name = PLAYER2 }); tracker.HandleEvent(new LogPartyEvent { Status = PartyStatus.RaidLeft, Name = PLAYER2 }); // since player2 is not the log owner, we should still be in the raid Assert.Equal("Raid", tracker.Party); }
public void Death_of_Friend() { var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Amount = 100 }); tracker.HandleEvent(new LogDeathEvent { Timestamp = DateTime.Now, Name = PLAYER1 }); Assert.Single(tracker.ActiveFights); }
public void Check_DPS_Intervals() { var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); var start = DateTime.Today.AddHours(3).AddSeconds(34); var time = start; tracker.HandleEvent(new LogHitEvent { Timestamp = time, Source = PLAYER1, Target = "Mob", Amount = 100 }); time = start.AddSeconds(1); tracker.HandleEvent(new LogHitEvent { Timestamp = time, Source = PLAYER1, Target = "Mob", Amount = 50 }); time = start.AddSeconds(3); tracker.HandleEvent(new LogHitEvent { Timestamp = time, Source = PLAYER1, Target = "Mob", Amount = 21 }); time = start.AddSeconds(4); tracker.HandleEvent(new LogHitEvent { Timestamp = time, Source = PLAYER2, Target = "Mob", Amount = 35 }); var f = tracker.ActiveFights[0]; f.Finish(); // the whole fight is only 4 seconds long so far but since the hits // cross a 6 second wall clock interval we should split the hits into // two intervals Assert.Equal(4, f.Participants[0].Duration); Assert.Equal(150, f.Participants[0].DPS[0]); Assert.Equal(21, f.Participants[0].DPS[1]); Assert.Equal(1, f.Participants[1].Duration); Assert.Equal(0, f.Participants[1].DPS[0]); Assert.Equal(35, f.Participants[1].DPS[1]); }
public void Ignore_Self_Hits() { var tracker = new FightTracker(new SpellParser(), new CharTracker()); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = PLAYER1, Amount = 100 }); Assert.Empty(tracker.ActiveFights); }
public void Dont_Double_Count_Self_Heal() { var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Amount = 100 }); tracker.HandleEvent(new LogHealEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = PLAYER1, Amount = 201 }); var f = tracker.ActiveFights[0]; Assert.Equal(201, f.Participants[0].InboundHealSum); Assert.Equal(201, f.Participants[0].OutboundHealSum); }
public void Death_of_Mob() { FightInfo f = null; var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); tracker.OnFightFinished += (args) => f = args; tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Type = "slash", Amount = 100 }); tracker.HandleEvent(new LogDeathEvent { Timestamp = DateTime.Now.AddSeconds(1), Name = "Mob1" }); Assert.NotNull(f); Assert.Equal("Mob1", f.Target.Name); Assert.Empty(tracker.ActiveFights); }
public void Fight_Ignored_If_Foe_Unknown() { var tracker = new FightTracker(new SpellParser(), new CharTracker()); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Type = "slash", Amount = 200 }); // since the tracker doesn't know if Player1 or Mob1 is the foe it // can't track the fight yet Assert.Empty(tracker.ActiveFights); }
public void Two_Fights_Back_to_Back_With_Same_Mob_Name() { var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Type = "slash", Amount = 100 }); Assert.Single(tracker.ActiveFights); tracker.HandleEvent(new LogDeathEvent { Timestamp = DateTime.Now, Name = "Mob1" }); Assert.Empty(tracker.ActiveFights); // same mob name, but it should be treated as a new fight since it died tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Type = "slash", Amount = 101 }); Assert.Single(tracker.ActiveFights); }
public void One_Fight() { var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); tracker.HandleEvent(new LogHitEvent { Timestamp = DateTime.Now, Source = PLAYER1, Target = "Mob1", Type = "slash", Amount = 200 }); var f = tracker.ActiveFights[0]; Assert.Equal("Mob1", f.Name); Assert.Equal("Mob1", f.Target.Name); Assert.Equal(PLAYER1, f.Participants[0].Name); }
static void Main(string[] args) { // load spells to give the trackers more context when processing log files // this is optional and can be skipped Console.Error.WriteLine("Loading spells..."); var spells = new SpellParser(); spells.Load("d:/games/everquest/spells_us.txt"); // generate an open event that stores the player name, server name, and file path // all trackers should receive this as their first event to signal that a new log file is being processed var open = LogOpenEvent.FromFileName("d:/games/everquest/logs/eqlog_Rumstil_erollisi.txt"); // create a CharTracker to help the FightTracker determine friends/foes var chars = new CharTracker(spells); chars.Files = new FileService(Path.GetDirectoryName(open.Path) + @"\..\"); chars.HandleEvent(open); // create a FightTracker to build fight summaries from various combat events var fights = new FightTracker(spells, chars); fights.HandleEvent(open); fights.OnFightStarted += ShowFight; fights.OnFightFinished += ShowFight; // create a log parser for converting log lines into events that can be passed to the trackers var parser = new LogParser(); parser.Player = open.Player; //parser.MinDate = DateTime.MinValue; //parser.MinDate = DateTime.Today.AddDays(-1).ToUniversalTime(); var timer = Stopwatch.StartNew(); var reader = File.OpenText(open.Path); while (true) { var line = reader.ReadLine(); if (line == null) { break; } // pass line to the parser and convert to an event // lines that cannot be parsed will be returned as nulls var e = parser.ParseLine(line); if (e == null) { continue; } // pass event to the trackers chars.HandleEvent(e); fights.HandleEvent(e); //if (e is LogRawEvent) Console.WriteLine(e); } ; fights.ForceFightTimeouts(); Console.Error.WriteLine("Parse completed in {0}", timer.Elapsed); }
/// <summary> /// Start monitoring a log file from a background task. /// </summary> private async void WatchFile(string path) { if (!File.Exists(path)) { return; } if (String.IsNullOrEmpty(LogOpenEvent.GetPlayerFromFileName(path))) { LogInfo($"Cannot open {path} because it doesn't use the standard naming convention."); return; } // cancel previous log parsing task if (cancellationSource != null) { cancellationSource.Cancel(); await Task.Delay(600); } // always disable auto uploads when opening a file to avoid accidentally uploading a lot of data chkAutoUpload.Checked = chkAutoUpload.Enabled = false; // we don't know where to find the spell_us.txt file until we open a log file // spells should be one folder down from the log folder if (!spells.IsReady) { var spellpath = Path.GetDirectoryName(path) + @"\..\spells_us.txt"; if (File.Exists(spellpath)) { LogInfo("Loading " + spellpath); spells.Load(spellpath); if (!spells.IsReady) { LogInfo("spells_us.txt could not be loaded. Class detection and buff tracking will not work properly."); } } else { LogInfo("spells_us.txt not found. Class detection and buff tracking will not work properly."); } } config.Write("filename", path); LogInfo("Loading " + path); var open = LogOpenEvent.FromFileName(path); var parser = new LogParser(); parser.Player = open.Player; // reset the trackers by creating new ones var charTracker = new CharTracker(spells); charTracker.Files = new FileService(Path.GetDirectoryName(path) + @"\..\"); charTracker.HandleEvent(open); charTracker.ImportPlayers(config.Read("chars:" + open.Server)); var fightTracker = new FightTracker(spells, charTracker); fightTracker.OnFightFinished += x => fightsQueue.Enqueue(x); fightTracker.AddTemplateFromResource(); fightTracker.HandleEvent(open); var lootTracker = new LootTracker(); lootTracker.OnLoot += x => lootQueue.Enqueue(x); lootTracker.HandleEvent(open); // reset UI lvFights.VirtualListSize = 0; fightList.Clear(); fightListSearchResults = null; toolStripStatusLabel1.Text = "-"; toolStripStatusLabel2.Text = "-"; // read roster files to assist player tracking if (!String.IsNullOrEmpty(open.Server)) { var files = new DirectoryInfo(Path.GetDirectoryName(path)).Parent .GetFiles("*_" + open.Server + "-*.txt") .Where(x => x.CreationTime > DateTime.Today.AddDays(-14)) .Take(20); var roster = RosterParser.Load(files); foreach (var who in roster) { charTracker.HandleEvent(who); } } // this handler runs in a background thread and must be threadsafe Action <string> handler = line => { var e = parser.ParseLine(line); if (e != null) { charTracker.HandleEvent(e); fightTracker.HandleEvent(e); lootTracker.HandleEvent(e); } }; // this event runs in the main app thread // https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread/18033198#18033198 var progress = new Progress <LogReaderStatus>(p => { toolStripStatusLabel1.Text = p.Percent.ToString("P0") + " " + p.Notes; var completed = p.Percent > 0.99; chkAutoUpload.Enabled = completed; }); cancellationSource = new CancellationTokenSource(); var reader = new BackgroundLogReader(path, cancellationSource.Token, handler, progress); try { await reader.Start(); } catch (Exception ex) { LogInfo("Error: " + ex.Message); LogInfo("Last Line: " + reader.LastLine); } //LogInfo("Closing " + path); // this log message can occur after the form has been disposed if (open.Server != null) { // save a list of players so that we have better information next time we run the parser var players = charTracker.ExportPlayers(); config.Write("chars:" + open.Server, players); } //await ProcessLogFileAsync(path); }
public void Raid_Matched() { var temp = new RaidTemplate() { Zone = "Crystallos, Lair of the Awakened", Name = "Kerafyrm", Mobs = new[] { "Kerafyrm the Awakened", "A wyvern assassin" }, EndsOnDeath = new[] { "Kerafyrm the Awakened" } }; var chars = new CharTracker(); chars.GetOrAdd(PLAYER1).Type = CharType.Friend; chars.GetOrAdd(PLAYER2).Type = CharType.Friend; var tracker = new FightTracker(new SpellParser(), chars); tracker.HandleEvent(new LogPartyEvent { Status = PartyStatus.RaidJoined, Name = PLAYER1 }); tracker.AddTemplate(temp); var results = new List <FightInfo>(); tracker.OnFightFinished += e => results.Add(e); // act var time = DateTime.Now; tracker.HandleEvent(new LogZoneEvent { Timestamp = time, Name = temp.Zone }); tracker.HandleEvent(new LogHitEvent { Timestamp = time, Source = PLAYER1, Target = temp.Mobs[0], Amount = 100 }); tracker.HandleEvent(new LogHitEvent { Timestamp = time, Source = PLAYER2, Target = temp.Mobs[1], Amount = 20 }); tracker.HandleEvent(new LogHitEvent { Timestamp = time, Source = PLAYER2, Target = "Unrelated Mob", Amount = 30 }); tracker.HandleEvent(new LogDeathEvent { Timestamp = time, Name = temp.EndsOnDeath[0] }); // assert var raids = results.OfType <RaidFightInfo>().ToArray(); Assert.Single(raids); Assert.Equal("Kerafyrm", raids[0].Name); Assert.Equal("Crystallos, Lair of the Awakened", raids[0].Zone); Assert.Equal(2, raids[0].MobCount); Assert.Equal(120, raids[0].HP); Assert.Single(tracker.ActiveFights); // the unrelated mob /* * * // this should be ignored * var f3 = new FightInfo() { Name = "Fippy Darkpaw", Zone = "Crystallos, Lair of the Awakened", Status = FightStatus.Killed }; * f3.Target.InboundHitSum = 20; * tracker.HandleFight(f3); * * // this should be included, and create a new raid * var f2 = new FightInfo() { Name = "A wyvern assassin", Zone = "Crystallos, Lair of the Awakened", Status = FightStatus.Killed }; * f2.Target.InboundHitSum = 10; * tracker.HandleFight(f2); * * // this should finish the raid * var f1 = new FightInfo() { Name = "Kerafyrm the Awakened", Zone = "Crystallos, Lair of the Awakened", Status = FightStatus.Killed }; * f1.Target.InboundHitSum = 100; * tracker.HandleFight(f1); * * Assert.Single(results); * Assert.Equal("Kerafyrm", results[0].Name); * Assert.Equal("Crystallos, Lair of the Awakened", results[0].Zone); * Assert.Equal(2, results[0].MobCount); * Assert.Equal(110, results[0].HP); * */ }