/// <summary> /// Parses from the given stream. On parsing failure, parsingFailureReason will be filled with the reason of the failure and the method will return null /// <see cref="ParsingFailureReason"/> /// </summary> /// <param name="operation">Operation object bound to the UI</param> /// <param name="evtcStream">The stream of the log</param> /// <param name="parsingFailureReason">The reason why the parsing failed, if applicable</param> /// <returns>the ParsedEvtcLog</returns> public ParsedEvtcLog ParseLog(ParserController operation, Stream evtcStream, out ParsingFailureReason parsingFailureReason) { parsingFailureReason = null; try { using (BinaryReader reader = CreateReader(evtcStream)) { operation.UpdateProgressWithCancellationCheck("Reading Binary"); operation.UpdateProgressWithCancellationCheck("Parsing fight data"); ParseFightData(reader, operation); operation.UpdateProgressWithCancellationCheck("Parsing agent data"); ParseAgentData(reader, operation); operation.UpdateProgressWithCancellationCheck("Parsing skill data"); ParseSkillData(reader, operation); operation.UpdateProgressWithCancellationCheck("Parsing combat list"); ParseCombatList(reader, operation); operation.UpdateProgressWithCancellationCheck("Linking agents to combat list"); CompleteAgents(); operation.UpdateProgressWithCancellationCheck("Preparing data for log generation"); PreProcessEvtcData(); operation.UpdateProgressWithCancellationCheck("Data parsed"); return(new ParsedEvtcLog(_buildVersion, _fightData, _agentData, _skillData, _combatItems, _playerList, _logEndTime - _logStartTime, _parserSettings, operation)); } } catch (Exception ex) { parsingFailureReason = new ParsingFailureReason(ex); return(null); } }
/*private bool TryRead(Stream stream, byte[] data) * { * int offset = 0; * int count = data.Length; * while (count > 0) * { * int bytesRead = stream.Read(data, offset, count); * if (bytesRead == 0) * { * return false; * } * offset += bytesRead; * count -= bytesRead; * } * return true; * }*/ //sub Parse methods /// <summary> /// Parses fight related data /// </summary> private void ParseFightData(BinaryReader reader, ParserController operation) { // 12 bytes: arc build version _buildVersion = GetString(reader, 12); operation.UpdateProgressWithCancellationCheck("ArcDPS Build " + _buildVersion); // 1 byte: revision _revision = reader.ReadByte(); operation.UpdateProgressWithCancellationCheck("ArcDPS Combat Item Revision " + _revision); // 2 bytes: fight instance ID _id = reader.ReadUInt16(); operation.UpdateProgressWithCancellationCheck("Fight Instance " + _id); // 1 byte: skip _ = reader.ReadByte(); }
/// <summary> /// Returns true if the combat item contains valid data and should be used, false otherwise /// </summary> /// <param name="combatItem"></param> /// <returns>true if the combat item is valid</returns> private bool IsValid(CombatItem combatItem, ParserController operation) { if (combatItem.IsStateChange == ArcDPSEnums.StateChange.HealthUpdate && combatItem.DstAgent > 20000) { // DstAgent should be target health % times 100, values higher than 10000 are unlikely. // If it is more than 200% health ignore this record return(false); } if (combatItem.IsExtension) { // Generic versioning check, we expect that the first event that'll be sent by an addon will always be meta data if (combatItem.Pad == 0) { AbstractExtensionHandler handler = ExtensionHelper.GetExtensionHandler(combatItem); if (handler != null) { _enabledExtensions[handler.Signature] = handler; operation.UpdateProgressWithCancellationCheck("Encountered supported extension " + handler.Name + " on " + handler.Version); } // No need to keep that event, it'll be immediately parsed by the handler return(false); } else { return(_enabledExtensions.ContainsKey(combatItem.Pad)); } } if (combatItem.SrcInstid == 0 && combatItem.DstAgent == 0 && combatItem.SrcAgent == 0 && combatItem.DstInstid == 0 && combatItem.IFF == ArcDPSEnums.IFF.Unknown) { return(false); } return(combatItem.IsStateChange != ArcDPSEnums.StateChange.Unknown && combatItem.IsStateChange != ArcDPSEnums.StateChange.ReplInfo && combatItem.IsStateChange != ArcDPSEnums.StateChange.StatReset && combatItem.IsStateChange != ArcDPSEnums.StateChange.APIDelayed); }
internal ParsedEvtcLog(int evtcVersion, FightData fightData, AgentData agentData, SkillData skillData, List <CombatItem> combatItems, List <Player> playerList, IReadOnlyDictionary <uint, AbstractExtensionHandler> extensions, long evtcLogDuration, EvtcParserSettings parserSettings, ParserController operation) { FightData = fightData; AgentData = agentData; SkillData = skillData; PlayerList = playerList; ParserSettings = parserSettings; _operation = operation; var friendlies = new List <AbstractSingleActor>(); friendlies.AddRange(playerList); friendlies.AddRange(fightData.Logic.NonPlayerFriendlies); Friendlies = friendlies; // FriendliesListBySpec = friendlies.GroupBy(x => x.Spec).ToDictionary(x => x.Key, x => x.ToList()); PlayerAgents = new HashSet <AgentItem>(playerList.Select(x => x.AgentItem)); FriendlyAgents = new HashSet <AgentItem>(friendlies.Select(x => x.AgentItem)); _operation.UpdateProgressWithCancellationCheck("Creating GW2EI Combat Events"); CombatData = new CombatData(combatItems, FightData, AgentData, SkillData, playerList, operation, extensions, evtcVersion); operation.UpdateProgressWithCancellationCheck("Checking CM"); FightData.SetCM(CombatData, AgentData); operation.UpdateProgressWithCancellationCheck("Setting Fight Name"); FightData.SetFightName(CombatData, AgentData); // _operation.UpdateProgressWithCancellationCheck("Checking Success"); FightData.Logic.CheckSuccess(CombatData, AgentData, FightData, PlayerAgents); if (FightData.FightDuration <= ParserSettings.TooShortLimit) { throw new TooShortException(FightData.FightDuration, ParserSettings.TooShortLimit); } if (ParserSettings.SkipFailedTries && !FightData.Success) { throw new SkipException(); } _operation.UpdateProgressWithCancellationCheck("Creating GW2EI Log Meta Data"); LogData = new LogData(evtcVersion, CombatData, evtcLogDuration, playerList, extensions, operation); // _operation.UpdateProgressWithCancellationCheck("Creating Buff Container"); Buffs = new BuffsContainer(LogData.GW2Build, CombatData, operation); _operation.UpdateProgressWithCancellationCheck("Creating Damage Modifier Container"); DamageModifiers = new DamageModifiersContainer(LogData.GW2Build, fightData.Logic.Mode, parserSettings); _operation.UpdateProgressWithCancellationCheck("Creating Mechanic Data"); MechanicData = FightData.Logic.GetMechanicData(); _operation.UpdateProgressWithCancellationCheck("Creating General Statistics Container"); StatisticsHelper = new StatisticsHelper(CombatData, PlayerList, Buffs); }
/// <summary> /// Pre process evtc data for EI /// </summary> private void PreProcessEvtcData(ParserController operation) { operation.UpdateProgressWithCancellationCheck("Offset time"); OffsetEvtcData(); // Removal of players present before the fight but not during var agentsToRemove = new HashSet <AgentItem>(); foreach (Player p in _playerList) { if (p.LastAware < 0) { agentsToRemove.Add(p.AgentItem); operation.UpdateProgressWithCancellationCheck("Removing player " + p.Character + " from player list (gone before fight start)"); } } // if (_fightData.Logic.Mode == FightLogic.ParseMode.Instanced10) { foreach (Player p in _playerList) { // check for players who have spawned after fight start if (p.FirstAware > 100) { // look for a spawn event close to first aware CombatItem spawnEvent = _combatItems.FirstOrDefault(x => x.IsStateChange == ArcDPSEnums.StateChange.Spawn && x.SrcMatchesAgent(p.AgentItem) && x.Time <= p.FirstAware + 500); if (spawnEvent != null) { var damageEvents = _combatItems.Where(x => x.IsDamage() && x.SrcMatchesAgent(p.AgentItem) && ((x.IsBuff > 0 && x.Value == 0) || (x.IsBuff == 0 && x.Value > 0))).ToList(); if (!damageEvents.Any()) { agentsToRemove.Add(p.AgentItem); operation.UpdateProgressWithCancellationCheck("Removing player " + p.Character + " from player list (spawned after fight start in 10 men content)"); } } } } } _playerList.RemoveAll(x => agentsToRemove.Contains(x.AgentItem)); if (_playerList.Count == 0) { throw new EvtcAgentException("No valid players"); } // operation.UpdateProgressWithCancellationCheck("Encounter specific processing"); _fightData.Logic.EIEvtcParse(_gw2Build, _fightData, _agentData, _combatItems, _enabledExtensions); if (!_fightData.Logic.Targets.Any()) { throw new MissingKeyActorsException("No Targets found"); } operation.UpdateProgressWithCancellationCheck("Player count: " + _playerList.Count); operation.UpdateProgressWithCancellationCheck("Friendlies count: " + _fightData.Logic.NonPlayerFriendlies.Count); operation.UpdateProgressWithCancellationCheck("Targets count: " + _fightData.Logic.Targets.Count); operation.UpdateProgressWithCancellationCheck("Trash Mobs count: " + _fightData.Logic.TrashMobs.Count); }
/*private bool TryRead(Stream stream, byte[] data) * { * int offset = 0; * int count = data.Length; * while (count > 0) * { * int bytesRead = stream.Read(data, offset, count); * if (bytesRead == 0) * { * return false; * } * offset += bytesRead; * count -= bytesRead; * } * return true; * }*/ //sub Parse methods /// <summary> /// Parses fight related data /// </summary> private void ParseFightData(Stream stream, ParserController operation) { using (BinaryReader reader = CreateReader(stream)) { // 12 bytes: arc build version _buildVersion = ParserHelper.GetString(stream, 12); operation.UpdateProgressWithCancellationCheck("ArcDPS Build " + _buildVersion); // 1 byte: skip _revision = reader.ReadByte(); operation.UpdateProgressWithCancellationCheck("ArcDPS Combat Item Revision " + _revision); // 2 bytes: fight instance ID _id = reader.ReadUInt16(); operation.UpdateProgressWithCancellationCheck("Fight Instance " + _id); // 1 byte: position ParserHelper.SafeSkip(stream, 1); } }
/*private bool TryRead(Stream stream, byte[] data) * { * int offset = 0; * int count = data.Length; * while (count > 0) * { * int bytesRead = stream.Read(data, offset, count); * if (bytesRead == 0) * { * return false; * } * offset += bytesRead; * count -= bytesRead; * } * return true; * }*/ //sub Parse methods /// <summary> /// Parses fight related data /// </summary> private void ParseFightData(BinaryReader reader, ParserController operation) { // 12 bytes: arc build version string evtcVersion = GetString(reader, 12); if (!evtcVersion.StartsWith("EVTC") || !int.TryParse(new string(evtcVersion.Where(char.IsDigit).ToArray()), out _evtcVersion)) { throw new EvtcFileException("Not EVTC"); } operation.UpdateProgressWithCancellationCheck("ArcDPS Build " + evtcVersion); // 1 byte: revision _revision = reader.ReadByte(); operation.UpdateProgressWithCancellationCheck("ArcDPS Combat Item Revision " + _revision); // 2 bytes: fight instance ID _id = reader.ReadUInt16(); operation.UpdateProgressWithCancellationCheck("Fight Instance " + _id); // 1 byte: skip _ = reader.ReadByte(); }
//Main Parse method------------------------------------------------------------------------------------------------------------------------------------------------ /// <summary> /// Parses the given log /// </summary> /// <param name="operation">Operation object bound to the UI</param> /// <param name="evtc">The path to the log to parse</param> /// <returns>the ParsedEvtcLog</returns> public ParsedEvtcLog ParseLog(ParserController operation, FileInfo evtc) { operation.UpdateProgressWithCancellationCheck("Reading Binary"); if (!evtc.Exists) { throw new FileNotFoundException("File " + evtc.FullName + " does not exist"); } if (!ParserHelper.IsSupportedFormat(evtc.Name)) { throw new InvalidDataException("Not EVTC"); } using (var fs = new FileStream(evtc.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) { if (ParserHelper.IsCompressedFormat(evtc.Name)) { using (var arch = new ZipArchive(fs, ZipArchiveMode.Read)) { if (arch.Entries.Count != 1) { throw new InvalidDataException("Invalid Archive"); } using (Stream data = arch.Entries[0].Open()) { using (var ms = new MemoryStream()) { data.CopyTo(ms); ms.Position = 0; ParseLog(operation, ms); }; } } } else { ParseLog(operation, fs); } } operation.UpdateProgressWithCancellationCheck("Data parsed"); return(new ParsedEvtcLog(_buildVersion, _fightData, _agentData, _skillData, _combatItems, _playerList, _logEndTime - _logStartTime, _parserSettings, operation)); }
/// <summary> /// Parses skill related data /// </summary> private void ParseSkillData(BinaryReader reader, ParserController operation) { // 4 bytes: player count uint skillCount = reader.ReadUInt32(); operation.UpdateProgressWithCancellationCheck("Skill Count " + skillCount); //TempData["Debug"] += "Skill Count:" + skill_count.ToString(); // 68 bytes: each skill for (int i = 0; i < skillCount; i++) { // 4 bytes: skill ID int skillId = reader.ReadInt32(); // 64 bytes: name string name = GetString(reader, 64); //Save _skillData.Add(skillId, name); } }
internal ParsedEvtcLog(string buildVersion, FightData fightData, AgentData agentData, SkillData skillData, List <CombatItem> combatItems, List <Player> playerList, long evtcLogDuration, EvtcParserSettings parserSettings, ParserController operation) { FightData = fightData; AgentData = agentData; SkillData = skillData; PlayerList = playerList; ParserSettings = parserSettings; _operation = operation; // PlayerListBySpec = playerList.GroupBy(x => x.Prof).ToDictionary(x => x.Key, x => x.ToList()); PlayerAgents = new HashSet <AgentItem>(playerList.Select(x => x.AgentItem)); _operation.UpdateProgressWithCancellationCheck("Creating GW2EI Combat Events"); CombatData = new CombatData(combatItems, FightData, AgentData, SkillData, playerList, operation); _operation.UpdateProgressWithCancellationCheck("Creating GW2EI Log Meta Data"); LogData = new LogData(buildVersion, CombatData, evtcLogDuration, playerList, operation); // _operation.UpdateProgressWithCancellationCheck("Checking Success"); FightData.Logic.CheckSuccess(CombatData, AgentData, FightData, PlayerAgents); if (FightData.FightEnd <= ParserSettings.TooShortLimit) { throw new TooShortException(FightData.FightEnd, ParserSettings.TooShortLimit); } if (ParserSettings.SkipFailedTries && !FightData.Success) { throw new SkipException(); } _operation.UpdateProgressWithCancellationCheck("Checking CM"); FightData.SetCM(CombatData, AgentData, FightData); // _operation.UpdateProgressWithCancellationCheck("Creating Buff Container"); Buffs = new BuffsContainer(LogData.GW2Build, CombatData, operation); _operation.UpdateProgressWithCancellationCheck("Creating Damage Modifier Container"); DamageModifiers = new DamageModifiersContainer(LogData.GW2Build, fightData.Logic.Mode, parserSettings); _operation.UpdateProgressWithCancellationCheck("Creating Mechanic Data"); MechanicData = FightData.Logic.GetMechanicData(); _operation.UpdateProgressWithCancellationCheck("Creating General Statistics Container"); StatisticsHelper = new StatisticsHelper(CombatData, PlayerList, Buffs); }
/// <summary> /// Parses combat related data /// </summary> private void ParseCombatList(BinaryReader reader, ParserController operation) { // 64 bytes: each combat long cbtItemCount = (reader.BaseStream.Length - reader.BaseStream.Position) / 64; operation.UpdateProgressWithCancellationCheck("Combat Event Count " + cbtItemCount); for (long i = 0; i < cbtItemCount; i++) { CombatItem combatItem = _revision > 0 ? ReadCombatItemRev1(reader) : ReadCombatItem(reader); if (!IsValid(combatItem, operation)) { continue; } if (combatItem.HasTime()) { if (_logStartTime == 0) { _logStartTime = combatItem.Time; } _logEndTime = combatItem.Time; } _combatItems.Add(combatItem); if (combatItem.IsStateChange == ArcDPSEnums.StateChange.GWBuild && combatItem.SrcAgent != 0) { _gw2Build = combatItem.SrcAgent; } } if (!_combatItems.Any()) { throw new EvtcCombatEventException("No combat events found"); } if (_logEndTime - _logStartTime < _parserSettings.TooShortLimit) { throw new TooShortException(_logEndTime - _logStartTime, _parserSettings.TooShortLimit); } // 24 hours if (_logEndTime - _logStartTime > 86400000) { throw new TooLongException(); } }
private void ParseLog(ParserController operation, Stream stream) { operation.UpdateProgressWithCancellationCheck("Parsing fight data"); ParseFightData(stream, operation); operation.UpdateProgressWithCancellationCheck("Parsing agent data"); ParseAgentData(stream, operation); operation.UpdateProgressWithCancellationCheck("Parsing skill data"); ParseSkillData(stream, operation); operation.UpdateProgressWithCancellationCheck("Parsing combat list"); ParseCombatList(stream, operation); operation.UpdateProgressWithCancellationCheck("Linking agents to combat list"); CompleteAgents(); operation.UpdateProgressWithCancellationCheck("Preparing data for log generation"); PreProcessEvtcData(); }
/// <summary> /// Parses skill related data /// </summary> private void ParseSkillData(Stream stream, ParserController operation) { using (BinaryReader reader = CreateReader(stream)) { // 4 bytes: player count uint skillCount = reader.ReadUInt32(); operation.UpdateProgressWithCancellationCheck("Skill Count " + skillCount); //TempData["Debug"] += "Skill Count:" + skill_count.ToString(); // 68 bytes: each skill for (int i = 0; i < skillCount; i++) { // 4 bytes: skill ID int skillId = reader.ReadInt32(); // 64 bytes: name string name = ParserHelper.GetString(stream, 64); //Save var skill = new SkillItem(skillId, name); _skillData.Add(skill); } } }
/// <summary> /// Parses combat related data /// </summary> private void ParseCombatList(Stream stream, ParserController operation) { // 64 bytes: each combat using (BinaryReader reader = CreateReader(stream)) { long cbtItemCount = (reader.BaseStream.Length - reader.BaseStream.Position) / 64; operation.UpdateProgressWithCancellationCheck("Combat Event Count " + cbtItemCount); for (long i = 0; i < cbtItemCount; i++) { CombatItem combatItem = _revision > 0 ? ReadCombatItemRev1(reader) : ReadCombatItem(reader); if (!IsValid(combatItem)) { continue; } if (combatItem.IsStateChange.HasTime()) { if (_logStartTime == 0) { _logStartTime = combatItem.Time; } _logEndTime = combatItem.Time; } _combatItems.Add(combatItem); } } if (!_combatItems.Any()) { throw new InvalidDataException("No combat events found"); } if (_logEndTime - _logStartTime < 2200) { throw new TooShortException(); } // 24 hours if (_logEndTime - _logStartTime > 86400000) { throw new TooLongException(); } }
/// <summary> /// Pre process evtc data for EI /// </summary> private void PreProcessEvtcData(ParserController operation) { operation.UpdateProgressWithCancellationCheck("Offset time"); OffsetEvtcData(); _friendlies = new List <AbstractSingleActor>(); _friendlies.AddRange(_playerList); operation.UpdateProgressWithCancellationCheck("Encounter specific processing"); _fightData.Logic.EIEvtcParse(_gw2Build, _fightData, _agentData, _combatItems, _friendlies, _enabledExtensions); if (!_fightData.Logic.Targets.Any()) { throw new MissingKeyActorsException("No Targets found"); } operation.UpdateProgressWithCancellationCheck("Player count: " + _playerList.Count); operation.UpdateProgressWithCancellationCheck("Friendlies count: " + (_friendlies.Count - _playerList.Count)); operation.UpdateProgressWithCancellationCheck("Targets count: " + _fightData.Logic.Targets.Count); operation.UpdateProgressWithCancellationCheck("Trash Mobs count: " + _fightData.Logic.TrashMobs.Count); }
private void CompleteAgents(ParserController operation) { var agentsLookup = _allAgentsList.GroupBy(x => x.Agent).ToDictionary(x => x.Key, x => x.ToList()); //var agentsLookup = _allAgentsList.ToDictionary(x => x.Agent); // Set Agent instid, firstAware and lastAware var invalidCombatItems = new HashSet <CombatItem>(); foreach (CombatItem c in _combatItems) { if (c.SrcIsAgent()) { if (agentsLookup.TryGetValue(c.SrcAgent, out List <AgentItem> agents)) { bool updatedAgent = false; foreach (AgentItem agent in agents) { updatedAgent = UpdateAgentData(agent, c.Time, c.SrcInstid, agents.Count > 1); if (updatedAgent) { break; } } // this means that this particular combat item does not point to a proper agent if (!updatedAgent && c.SrcInstid != 0) { invalidCombatItems.Add(c); } } } if (c.DstIsAgent()) { if (agentsLookup.TryGetValue(c.DstAgent, out List <AgentItem> agents)) { bool updatedAgent = false; foreach (AgentItem agent in agents) { updatedAgent = UpdateAgentData(agent, c.Time, c.DstInstid, agents.Count > 1); if (updatedAgent) { break; } } // this means that this particular combat item does not point to a proper agent if (!updatedAgent && c.DstInstid != 0) { invalidCombatItems.Add(c); } } } } if (invalidCombatItems.Any()) { #if DEBUG throw new InvalidDataException("Must remove " + invalidCombatItems.Count + " invalid combat items"); #else operation.UpdateProgressWithCancellationCheck("Removing " + invalidCombatItems.Count + " invalid combat items"); _combatItems.RemoveAll(x => invalidCombatItems.Contains(x)); #endif } _allAgentsList.RemoveAll(x => !(x.InstID != 0 && x.LastAware - x.FirstAware >= 0 && x.FirstAware != 0 && x.LastAware != long.MaxValue) && (x.Type != AgentItem.AgentType.Player && x.Type != AgentItem.AgentType.NonSquadPlayer)); _agentData = new AgentData(_allAgentsList); if (_agentData.GetAgentByType(AgentItem.AgentType.Player).Count == 0) { throw new EvtcAgentException("No players found"); } _fightData = new FightData(_id, _agentData, _parserSettings, _logStartTime, _logEndTime); operation.UpdateProgressWithCancellationCheck("Linking minions to their masters"); foreach (CombatItem c in _combatItems) { if (c.SrcIsAgent() && c.SrcMasterInstid != 0) { FindAgentMaster(c.Time, c.SrcMasterInstid, c.SrcAgent); } if (c.DstIsAgent() && c.DstMasterInstid != 0) { FindAgentMaster(c.Time, c.DstMasterInstid, c.DstAgent); } } // Adjust extension events if needed if (_enabledExtensions.Any()) { operation.UpdateProgressWithCancellationCheck("Adjust extension events"); foreach (CombatItem combatItem in _combatItems) { if (combatItem.IsExtension) { if (_enabledExtensions.TryGetValue(combatItem.Pad, out AbstractExtensionHandler handler)) { handler.AdjustCombatEvent(combatItem, _agentData); } } } } operation.UpdateProgressWithCancellationCheck("Creating players"); CompletePlayers(operation); }
private void CompletePlayers(ParserController operation) { //Fix Disconnected players IReadOnlyList <AgentItem> playerAgentList = _agentData.GetAgentByType(AgentItem.AgentType.Player); foreach (AgentItem playerAgent in playerAgentList) { if (playerAgent.InstID == 0 || playerAgent.FirstAware == 0 || playerAgent.LastAware == long.MaxValue) { operation.UpdateProgressWithCancellationCheck("Skipping invalid player " + playerAgent.Name); continue; } bool skip = false; var player = new Player(playerAgent, _fightData.Logic.Mode == FightLogic.ParseMode.Instanced5 || _fightData.Logic.Mode == FightLogic.ParseMode.sPvP); foreach (Player p in _playerList) { if (p.Account == player.Account) // same player { if (p.Character == player.Character) // same character, can be fused { skip = true; ulong agent = p.AgentItem.Agent; operation.UpdateProgressWithCancellationCheck("Merging player " + p.Character); foreach (CombatItem c in _combatItems) { if (c.DstMatchesAgent(player.AgentItem, _enabledExtensions)) { c.OverrideDstAgent(agent); } if (c.SrcMatchesAgent(player.AgentItem, _enabledExtensions)) { c.OverrideSrcAgent(agent); } } _agentData.SwapMasters(player.AgentItem, p.AgentItem); p.AgentItem.OverrideAwareTimes(Math.Min(p.AgentItem.FirstAware, player.AgentItem.FirstAware), Math.Max(p.AgentItem.LastAware, player.AgentItem.LastAware)); break; } } } if (!skip) { _playerList.Add(player); } } if (_parserSettings.AnonymousPlayer) { for (int i = 0; i < _playerList.Count; i++) { _playerList[i].Anonymize(i + 1); } } _playerList = _playerList.OrderBy(a => a.Group).ToList(); if (_playerList.Exists(x => x.Group == 0)) { _playerList.ForEach(x => x.MakeSquadless()); } uint minToughness = _playerList.Min(x => x.Toughness); if (minToughness > 0) { operation.UpdateProgressWithCancellationCheck("Adjusting player toughness scores"); uint maxToughness = _playerList.Max(x => x.Toughness); foreach (Player p in _playerList) { p.AgentItem.OverrideToughness((ushort)Math.Round(10.0 * (p.AgentItem.Toughness - minToughness) / Math.Max(1.0, maxToughness - minToughness))); } } }
/// <summary> /// Parses agent related data /// </summary> private void ParseAgentData(BinaryReader reader, ParserController operation) { // 4 bytes: player count uint agentCount = reader.ReadUInt32(); operation.UpdateProgressWithCancellationCheck("Agent Count " + agentCount); // 96 bytes: each player for (int i = 0; i < agentCount; i++) { // 8 bytes: agent ulong agent = reader.ReadUInt64(); // 4 bytes: profession uint prof = reader.ReadUInt32(); // 4 bytes: is_elite uint isElite = reader.ReadUInt32(); // 2 bytes: toughness ushort toughness = reader.ReadUInt16(); // 2 bytes: healing ushort concentration = reader.ReadUInt16(); // 2 bytes: healing ushort healing = reader.ReadUInt16(); // 2 bytes: hitbox width uint hbWidth = (uint)(2 * reader.ReadUInt16()); // 2 bytes: condition ushort condition = reader.ReadUInt16(); // 2 bytes: hitbox height uint hbHeight = (uint)(2 * reader.ReadUInt16()); // 68 bytes: name string name = GetString(reader, 68, false); //Save ParserHelper.Spec agentProf = ParserHelper.ProfToSpec(GetAgentProfString(prof, isElite, operation)); AgentItem.AgentType type; ushort ID = 0; switch (agentProf) { case ParserHelper.Spec.NPC: // NPC if (!ushort.TryParse(prof.ToString().PadLeft(5, '0'), out ID)) { ID = 0; } type = AgentItem.AgentType.NPC; break; case ParserHelper.Spec.Gadget: // Gadget if (!ushort.TryParse((prof & 0x0000ffff).ToString().PadLeft(5, '0'), out ID)) { ID = 0; } type = AgentItem.AgentType.Gadget; break; default: // Player type = AgentItem.AgentType.Player; break; } _allAgentsList.Add(new AgentItem(agent, name, agentProf, ID, type, toughness, healing, condition, concentration, hbWidth, hbHeight)); } }
private string GetAgentProfString(uint prof, uint elite, ParserController operation) { // non player if (elite == 0xFFFFFFFF) { if ((prof & 0xffff0000) == 0xffff0000) { return("GDG"); } else { return("NPC"); } } // base profession else if (elite == 0) { switch (prof) { case 1: return("Guardian"); case 2: return("Warrior"); case 3: return("Engineer"); case 4: return("Ranger"); case 5: return("Thief"); case 6: return("Elementalist"); case 7: return("Mesmer"); case 8: return("Necromancer"); case 9: return("Revenant"); } } // old elite else if (elite == 1) { switch (prof) { case 1: return("Dragonhunter"); case 2: return("Berserker"); case 3: return("Scrapper"); case 4: return("Druid"); case 5: return("Daredevil"); case 6: return("Tempest"); case 7: return("Chronomancer"); case 8: return("Reaper"); case 9: return("Herald"); } } // new way else { GW2APISpec spec = _apiController.GetAPISpec((int)elite); if (spec == null) { operation.UpdateProgressWithCancellationCheck("Missing or outdated GW2 API Cache or unknown player spec"); return("Unknown"); } if (spec.Elite) { return(spec.Name); } else { return(spec.Profession); } } throw new EvtcAgentException("Unexpected profession pattern in evtc"); }
public void UpdateProgressWithCancellationCheck(string status) { _operation.UpdateProgressWithCancellationCheck(status); }
/// <summary> /// Parses agent related data /// </summary> private void ParseAgentData(Stream stream, ParserController operation) { using (BinaryReader reader = CreateReader(stream)) { // 4 bytes: player count int agentCount = reader.ReadInt32(); operation.UpdateProgressWithCancellationCheck("Agent Count " + agentCount); // 96 bytes: each player for (int i = 0; i < agentCount; i++) { // 8 bytes: agent ulong agent = reader.ReadUInt64(); // 4 bytes: profession uint prof = reader.ReadUInt32(); // 4 bytes: is_elite uint isElite = reader.ReadUInt32(); // 2 bytes: toughness uint toughness = reader.ReadUInt16(); // 2 bytes: healing uint concentration = reader.ReadUInt16(); // 2 bytes: healing uint healing = reader.ReadUInt16(); // 2 bytes: hitbox width uint hbWidth = (uint)2 * reader.ReadUInt16(); // 2 bytes: condition uint condition = reader.ReadUInt16(); // 2 bytes: hitbox height uint hbHeight = (uint)2 * reader.ReadUInt16(); // 68 bytes: name string name = ParserHelper.GetString(stream, 68, false); //Save string agentProf = GetAgentProfString(prof, isElite); AgentItem.AgentType type; ushort ID = 0; switch (agentProf) { case "NPC": // NPC try { ID = ushort.Parse(prof.ToString().PadLeft(5, '0')); } catch (FormatException) { ID = 0; } type = AgentItem.AgentType.NPC; break; case "GDG": // Gadget try { ID = ushort.Parse((prof & 0x0000ffff).ToString().PadLeft(5, '0')); } catch (FormatException) { ID = 0; } type = AgentItem.AgentType.Gadget; break; default: // Player type = AgentItem.AgentType.Player; break; } _allAgentsList.Add(new AgentItem(agent, name, agentProf, ID, type, toughness, healing, condition, concentration, hbWidth, hbHeight)); } } }
/// <summary> /// Parses from the given stream. On parsing failure, parsingFailureReason will be filled with the reason of the failure and the method will return null /// <see cref="ParsingFailureReason"/> /// </summary> /// <param name="operation">Operation object bound to the UI</param> /// <param name="evtcStream">The stream of the log</param> /// <param name="parsingFailureReason">The reason why the parsing failed, if applicable</param> /// <param name="multiThreadAccelerationForBuffs">Will preprocess buff simulation using multi threading </param> /// <returns>the ParsedEvtcLog</returns> public ParsedEvtcLog ParseLog(ParserController operation, Stream evtcStream, out ParsingFailureReason parsingFailureReason, bool multiThreadAccelerationForBuffs = false) { parsingFailureReason = null; try { using (BinaryReader reader = CreateReader(evtcStream)) { operation.UpdateProgressWithCancellationCheck("Reading Binary"); operation.UpdateProgressWithCancellationCheck("Parsing fight data"); ParseFightData(reader, operation); operation.UpdateProgressWithCancellationCheck("Parsing agent data"); ParseAgentData(reader, operation); operation.UpdateProgressWithCancellationCheck("Parsing skill data"); ParseSkillData(reader, operation); operation.UpdateProgressWithCancellationCheck("Parsing combat list"); ParseCombatList(reader, operation); operation.UpdateProgressWithCancellationCheck("Linking agents to combat list"); CompleteAgents(operation); operation.UpdateProgressWithCancellationCheck("Preparing data for log generation"); PreProcessEvtcData(operation); operation.UpdateProgressWithCancellationCheck("Data parsed"); var log = new ParsedEvtcLog(_evtcVersion, _fightData, _agentData, _skillData, _combatItems, _playerList, _enabledExtensions, _logEndTime - _logStartTime, _parserSettings, operation); // if (multiThreadAccelerationForBuffs) { IReadOnlyList <PhaseData> phases = log.FightData.GetPhases(log); operation.UpdateProgressWithCancellationCheck("Multi threading"); var friendliesAndTargets = new List <AbstractSingleActor>(log.Friendlies); friendliesAndTargets.AddRange(log.FightData.Logic.Targets); var friendliesAndTargetsAndMobs = new List <AbstractSingleActor>(log.FightData.Logic.TrashMobs); friendliesAndTargetsAndMobs.AddRange(friendliesAndTargets); foreach (AbstractSingleActor actor in friendliesAndTargetsAndMobs) { // that part can't be // due to buff extensions actor.GetTrackedBuffs(log); actor.GetMinions(log); } Parallel.ForEach(friendliesAndTargets, actor => actor.GetStatus(log)); /*if (log.CombatData.HasMovementData) * { * // init all positions * Parallel.ForEach(friendliesAndTargetsAndMobs, actor => actor.GetCombatReplayPolledPositions(log)); * }*/ Parallel.ForEach(friendliesAndTargetsAndMobs, actor => actor.GetBuffGraphs(log)); Parallel.ForEach(friendliesAndTargets, actor => { foreach (PhaseData phase in phases) { actor.GetBuffDistribution(log, phase.Start, phase.End); } }); Parallel.ForEach(friendliesAndTargets, actor => { foreach (PhaseData phase in phases) { actor.GetBuffPresence(log, phase.Start, phase.End); } }); // //Parallel.ForEach(log.PlayerList, player => player.GetDamageModifierStats(log, null)); Parallel.ForEach(log.Friendlies, actor => { foreach (PhaseData phase in phases) { actor.GetBuffs(BuffEnum.Self, log, phase.Start, phase.End); } }); Parallel.ForEach(log.PlayerList, actor => { foreach (PhaseData phase in phases) { actor.GetBuffs(BuffEnum.Group, log, phase.Start, phase.End); } }); Parallel.ForEach(log.PlayerList, actor => { foreach (PhaseData phase in phases) { actor.GetBuffs(BuffEnum.OffGroup, log, phase.Start, phase.End); } }); Parallel.ForEach(log.PlayerList, actor => { foreach (PhaseData phase in phases) { actor.GetBuffs(BuffEnum.Squad, log, phase.Start, phase.End); } }); Parallel.ForEach(log.FightData.Logic.Targets, actor => { foreach (PhaseData phase in phases) { actor.GetBuffs(BuffEnum.Self, log, phase.Start, phase.End); } }); } // return(log); } } catch (Exception ex) { parsingFailureReason = new ParsingFailureReason(ex); return(null); } }
internal ParsedEvtcLog(int evtcVersion, FightData fightData, AgentData agentData, SkillData skillData, List <CombatItem> combatItems, List <Player> playerList, IReadOnlyDictionary <uint, AbstractExtensionHandler> extensions, long evtcLogDuration, EvtcParserSettings parserSettings, ParserController operation) { FightData = fightData; AgentData = agentData; SkillData = skillData; PlayerList = playerList; PlayerAgents = new HashSet <AgentItem>(PlayerList.Select(x => x.AgentItem)); ParserSettings = parserSettings; _operation = operation; if (parserSettings.AnonymousPlayer) { operation.UpdateProgressWithCancellationCheck("Anonymous players"); for (int i = 0; i < PlayerList.Count; i++) { PlayerList[i].Anonymize(i + 1); } IReadOnlyList <AgentItem> allPlayerAgents = agentData.GetAgentByType(AgentItem.AgentType.Player); int playerOffset = PlayerList.Count; foreach (AgentItem playerAgent in allPlayerAgents) { if (!PlayerAgents.Contains(playerAgent)) { string character = "Player " + playerOffset; string account = "Account " + (playerOffset++); playerAgent.OverrideName(character + "\0:" + account + "\01"); } } } // _operation.UpdateProgressWithCancellationCheck("Creating GW2EI Combat Events"); CombatData = new CombatData(combatItems, FightData, AgentData, SkillData, PlayerList, operation, extensions, evtcVersion); if (parserSettings.AnonymousPlayer) { operation.UpdateProgressWithCancellationCheck("Anonymous guilds"); IReadOnlyList <AgentItem> allPlayerAgents = agentData.GetAgentByType(AgentItem.AgentType.Player); foreach (AgentItem playerAgent in allPlayerAgents) { foreach (GuildEvent guildEvent in CombatData.GetGuildEvents(playerAgent)) { guildEvent.Anonymize(); } } } // operation.UpdateProgressWithCancellationCheck("Checking CM"); FightData.SetEncounterMode(CombatData, AgentData); operation.UpdateProgressWithCancellationCheck("Setting Fight Name"); FightData.SetFightName(CombatData, AgentData); // var friendlies = new List <AbstractSingleActor>(); friendlies.AddRange(PlayerList); friendlies.AddRange(fightData.Logic.NonPlayerFriendlies); Friendlies = friendlies; FriendliesListBySpec = friendlies.GroupBy(x => x.Spec).ToDictionary(x => x.Key, x => x.ToList()); FriendlyAgents = new HashSet <AgentItem>(Friendlies.Select(x => x.AgentItem)); // _operation.UpdateProgressWithCancellationCheck("Checking Success"); FightData.Logic.CheckSuccess(CombatData, AgentData, FightData, PlayerAgents); if (FightData.FightDuration <= ParserSettings.TooShortLimit) { throw new TooShortException(FightData.FightDuration, ParserSettings.TooShortLimit); } if (ParserSettings.SkipFailedTries && !FightData.Success) { throw new SkipException(); } _operation.UpdateProgressWithCancellationCheck("Creating GW2EI Log Meta Data"); LogData = new LogData(evtcVersion, CombatData, evtcLogDuration, playerList, extensions, operation); // _operation.UpdateProgressWithCancellationCheck("Creating Buff Container"); Buffs = new BuffsContainer(LogData.GW2Build, CombatData, operation); _operation.UpdateProgressWithCancellationCheck("Creating Damage Modifier Container"); DamageModifiers = new DamageModifiersContainer(LogData.GW2Build, fightData.Logic.Mode, parserSettings); _operation.UpdateProgressWithCancellationCheck("Creating Mechanic Data"); MechanicData = FightData.Logic.GetMechanicData(); _operation.UpdateProgressWithCancellationCheck("Creating General Statistics Container"); StatisticsHelper = new StatisticsHelper(CombatData, PlayerList, Buffs); }