public VolskayaFoundry1ReplayParserTests() { StormReplayResult result = StormReplay.Parse(Path.Combine(_replaysFolder, _replayFile)); _stormReplay = result.Replay; _result = result.Status; }
public async Task WriteDetailsAsync(StormReplay replay) { var mmr = settings.EnableMMR ? $"MMR: " + await heroesProfileService.CalculateMMRAsync(replay) : string.Empty; var map = gameDataService.Maps.Find(map => map.AltName.Equals(replay.Replay.MapAlternativeName) || replay.Replay.Map.Equals(map.Name)); var mode = replay.Replay.GameMode switch { //GameMode.StormLeague => "Storm League", //GameMode.UnrankedDraft => "Unranked", //GameMode.QuickMatch => "Quick Match", //_ => replay.Replay.GameMode.ToString() _ => string.Empty }; var bans = from ban in replay.Replay.DraftOrder.Where(pick => pick.PickType == DraftPickType.Banned).Select((pick, index) => new { Hero = pick.HeroSelected, Index = index + 1 }) from hero in gameDataService.Heroes where ban.Hero.Equals(hero.Name, StringComparison.OrdinalIgnoreCase) || ban.Hero.Equals(hero.AltName, StringComparison.OrdinalIgnoreCase) select $"{hero.Name}"; if (bans.Any()) { bans = bans.Prepend("Bans:"); } string[] details = new[] { mode, mmr, replay.Replay.ReplayVersion }.Where(line => !string.IsNullOrEmpty(line)).ToArray(); logger.LogInformation($"WriteDetailsAsync: {settings.CurrentReplayPath}"); await File.WriteAllLinesAsync(settings.CurrentReplayPath, details.Concat(bans).Where(line => !string.IsNullOrWhiteSpace(line)), CancellationToken.None); } }
public GameEventArgs(StormReplay stormReplay, T data, TimeSpan timer, string message) { Timer = timer; StormReplay = stormReplay; Message = message; Data = data; }
public MissingWorkSetSlotId1ReplayParserTests() { StormReplayResult result = StormReplay.Parse(Path.Combine(_replaysFolder, _replayFile)); _stormReplay = result.Replay; _result = result.Status; }
private static void NoGameEvents(StormReplayResult result) { StormReplay replay = result.Replay !; Assert.AreEqual(0, replay.GameEvents.Count); Assert.IsNull(replay.Owner?.PlayerHero?.HeroName); }
public LostCavernNonSingleUnit1ReplayParserTests() { StormReplayResult result = StormReplay.Parse(Path.Combine(_replaysFolder, "LostCavernNonSingleUnit1_76517.StormR")); _stormReplay = result.Replay; _result = result.Status; }
public async Task <StormReplay> LaunchAsync(StormReplay stormReplay) { int latestBuild = Directory.EnumerateDirectories(Path.Combine(settings.Location.GameInstallPath, VersionsFolder)).Select(x => x).Select(x => int.Parse(Path.GetFileName(x).Replace("Base", string.Empty))).Max(); var requiresAuth = stormReplay.Replay.ReplayBuild == latestBuild; if (IsLaunched && await IsReplay()) { return(stormReplay); // already authenticated or in a replay } else if (IsLaunched && await IsHomeScreen()) { await LaunchAndWait(stormReplay); } else if (requiresAuth) { await LaunchGameFromBattlenet(); await LaunchAndWait(stormReplay); } else { await LaunchAndWait(stormReplay); } return(stormReplay); }
public AIDragonShire1ReplayParserTests() { StormReplayResult result = StormReplay.Parse(Path.Combine(_replaysFolder, _replayFile)); _stormReplay = result.Replay; _result = result.Status; }
public EscapeFromBraxisHeroic1ReplayParserTests() { StormReplayResult result = StormReplay.Parse(Path.Combine(_replaysFolder, "EscapeFromBraxis(Heroic)1_70200.StormR")); _stormReplay = result.Replay; _result = result.Status; }
private static void GetInfo(StormReplayResult stormReplayResult) { StormReplay replay = stormReplayResult.Replay; List <StormPlayer> players = replay.StormPlayers.ToList(); Console.WriteLine($"{"File Name: ",11}{Path.GetFileName(stormReplayResult.FileName)}"); Console.WriteLine($"{"Game Mode: ",11}{replay.GameMode}"); Console.WriteLine($"{"Map: ",11}{replay.MapInfo.MapName} [ID:{replay.MapInfo.MapId}]"); Console.WriteLine($"{"Version: ",11}{replay.ReplayVersion}"); Console.WriteLine($"{"Region: ",11}{replay.Region}"); Console.WriteLine($"{"Game Time: ",11}{replay.ReplayLength}"); if (_failed) { Environment.Exit(1); } IEnumerable <StormPlayer> blueTeam = players.Where(x => x.Team == StormTeam.Blue); IEnumerable <StormPlayer> redTeam = players.Where(x => x.Team == StormTeam.Red); List <StormPlayer> playersWithObservers = replay.StormObservers.ToList(); StormTeamDisplay(replay, blueTeam, StormTeam.Blue); StormTeamDisplay(replay, redTeam, StormTeam.Red); StormTeamDisplay(replay, playersWithObservers, StormTeam.Observer); }
public TowersofDoom1ReplayParserTests() { StormReplayResult result = StormReplay.Parse(Path.Combine(_replaysFolder, "TowersofDoom1_39445.StormR")); _stormReplay = result.Replay; _result = result.Status; }
public async Task WriteDetailsAsync(StormReplay replay) { try { var mmr = settings.HeroesProfileApi.EnableMMR ? $"Tier: " + await heroesProfileService.CalculateMMRAsync(replay) : string.Empty; var bans = from ban in replay.Replay.DraftOrder.Where(pick => pick.PickType == DraftPickType.Banned).Select((pick, index) => new { Hero = pick.HeroSelected, Index = index + 1 }) from hero in gameData.Heroes where ban.Hero.Equals(hero.Name, StringComparison.OrdinalIgnoreCase) || ban.Hero.Equals(hero.AltName, StringComparison.OrdinalIgnoreCase) select $"{hero.Name}"; if (bans.Any()) { bans = bans.Prepend("Bans:"); } string[] details = new[] { mmr }.Where(line => !string.IsNullOrWhiteSpace(line)).ToArray(); logger.LogInformation($"writing replay details to: {settings.CurrentReplayInfoFilePath}"); await File.WriteAllLinesAsync(settings.CurrentReplayInfoFilePath, details.Concat(bans).Where(line => !string.IsNullOrWhiteSpace(line)), CancellationToken.None); } catch (Exception e) { logger.LogError(e, $"Could not update the file: {settings.CurrentReplayInfoFilePath}"); } }
public static void Parse(StormReplay replay, ReadOnlySpan <byte> source) { BitReader bitReader = new BitReader(source, EndianType.BigEndian); bitReader.ReadAlignedBytes(3); bitReader.ReadAlignedByte(); bitReader.ReadAlignedBytes(4); // Data Max Size bitReader.ReadAlignedBytes(4); // Header Offset bitReader.ReadAlignedBytes(4); // User Data Header Size VersionedDecoder versionedDecoder = new VersionedDecoder(ref bitReader); // headerStructure.StructureByIndex[0].GetValueAsString(); // m_signature => "Heroes of the Storm replay 11 // m_version struct replay.ReplayVersion.Major = (int)(versionedDecoder.Structure?[1].Structure?[1].GetValueAsUInt32() !); // m_major replay.ReplayVersion.Minor = (int)(versionedDecoder.Structure?[1].Structure?[2].GetValueAsUInt32() !); // m_minor replay.ReplayVersion.Revision = (int)(versionedDecoder.Structure?[1].Structure?[3].GetValueAsUInt32() !); // m_revision replay.ReplayVersion.Build = (int)(versionedDecoder.Structure?[1].Structure?[4].GetValueAsUInt32() !); // m_build replay.ReplayVersion.BaseBuild = (int)(versionedDecoder.Structure?[1].Structure?[5].GetValueAsUInt32() !); // m_baseBuild // the major version is a 0 before build 51978, it may be set as a 1 /* if (stormReplay.ReplayBuild < 51978) * stormReplay.ReplayVersion.Major = 1; */ /* headerStructure.StructureByIndex[2].GetValueAsUInt32(); m_type */ replay.ElapsedGamesLoops = (int)versionedDecoder.Structure ![3].GetValueAsUInt32(); // m_elapsedGameLoops
public async Task SetSessionAsync(StormReplay stormReplay) { await sessionCreater.CreateAsync(stormReplay); await replayFileWriter.WriteDetailsAsync(stormReplay); await gameController.LaunchAsync(stormReplay); }
private bool IsMatchingClientVersion(StormReplay stormReplay, Process p) { bool match = p.MainModule.FileVersionInfo.FileVersion == stormReplay.Replay.ReplayVersion; Logger.LogInformation($"Current: {p.MainModule.FileVersionInfo.FileVersion}"); Logger.LogInformation($"Required: {stormReplay.Replay.ReplayVersion}"); return(match); }
private static void SetParsedData(StormReplay replay) { foreach (StormTrackerEvent stormTrackerEvent in replay.TrackerEventsInternal) { switch (stormTrackerEvent.TrackerEventType) { case StormTrackerEventType.PlayerSetupEvent: if (replay.NoWorkingSetSlotID) { uint playerId = stormTrackerEvent.VersionedDecoder !.Structure ![3].OptionalData !.GetValueAsUInt32();
public async Task <Uri> SaveClip(StormPlayer stormPlayer, StormReplay stormReplay) { // Limited api functionality // Cannot modify the title of the clip // Cannot modify the time of the clip from 30 to 60 seconds // When twitch allow it, modify the title to a format like: [Hero] [Map] - [Quintuple, Quad, Tripple] kill CreatedClipResponse response = await twitchAPI.Helix.Clips.CreateClipAsync(settings.TwitchBroadcasterId); GetClipResponse clip = await twitchAPI.Helix.Clips.GetClipAsync(response.CreatedClips[0].Id); return(new Uri(clip.Clips[0].Url)); }
public virtual async Task <bool> WaitForMapLoadingAsync(StormReplay stormReplay, CancellationToken token = default) { return(await Policy .Handle <Exception>() // Issue getting Process information (terminated?) .OrResult <bool>(loaded => loaded == false) // it's not the game version that supports the replay .WaitAndRetryAsync(retryCount: 120, retry => TimeSpan.FromSeconds(1)) // this can time some time, especially if the game is downloading assets. .ExecuteAsync(async(t) => { string[] names = stormReplay.Replay.Players.Select(p => p.Name).ToArray(); return await GetWindowContainsAnyAsync(new[] { Constants.Ocr.LOADING_SCREEN_TEXT, stormReplay.Replay.Map }.Concat(names)); }, token)); }
private async Task LaunchGame(StormReplay stormReplay) { if (!await process.LaunchSelectedReplayAsync(stormReplay)) { throw new Exception($"Game process version not found matching replay version: {stormReplay.Replay.ReplayVersion}"); } if (!await process.WaitForMapLoadingAsync(stormReplay)) { throw new Exception($"Map loading state was not detected after selecting: {stormReplay.Path}"); } }
public async Task ReplayAsync(StormReplay stormReplay) { try { RegisterEvents(); await RunAsync(stormReplay); } finally { zoomout = false; DeregisterEvents(); } }
public void NoTrackerEventsParsingTests() { StormReplayResult result = StormReplay.Parse(Path.Combine(_replaysFolder, _replayFile), new ParseOptions() { AllowPTR = false, ShouldParseGameEvents = true, ShouldParseMessageEvents = true, ShouldParseTrackerEvents = false, }); Assert.AreEqual(StormReplayParseStatus.Success, result.Status); NoTrackerEvents(result); }
public async Task <Uri> GetMatchLink(StormReplay stormReplay) { var apiKey = settings.HeroesProfileApiKey; var hotsApiReplayId = replayHelper.TryGetReplayId(stormReplay); using (var client = new HttpClient() { BaseAddress = settings.HeroesProfileBaseUri }) { string replayId = await client.GetStringAsync($"Heroesprofile/ReplayID?hotsapi_replayID={hotsApiReplayId}&api_token={apiKey}").ConfigureAwait(false); return(new Uri($"https://www.heroesprofile.com/Match/Single/?replayID={replayId}")); } }
public StormPlayer?GetStormPlayer(StormPlayer?currentPlayer, StormReplay stormReplay, TimeSpan timer) { IEnumerable <StormPlayer> stormPlayers = heroTool.GetPlayers(stormReplay.Replay, timer); if (stormPlayers.Any(p => p.SpectateEvent.IsAny(SpectateEvent.Proximity, SpectateEvent.Alive))) { if (currentPlayer == null) { return(stormPlayers.Shuffle().FirstOrDefault()); } return(stormPlayers.Where(stormPlayer => stormPlayer.Player != currentPlayer?.Player && stormPlayer.Player.Team != currentPlayer?.Player.Team).Shuffle().FirstOrDefault()); } return(stormPlayers.Take(1).Select(stormPlayer => new StormPlayer(stormPlayer.Player, stormPlayer.Timer, stormPlayer.Duration - timer, stormPlayer.SpectateEvent)).FirstOrDefault()); }
public virtual async Task <bool> LaunchSelectedReplayAsync(StormReplay stormReplay, CancellationToken token = default) { using (var defaultLaunch = Process.Start("explorer.exe", stormReplay.Path)) { defaultLaunch.WaitForExit(); // new patch or old patch can be upto 50Mb, give this some time return(await Task.FromResult(Policy .Handle <Exception>() .OrResult <bool>(result => result == false) .WaitAndRetry(retryCount : 300, retry => TimeSpan.FromSeconds(1)) .Execute(t => Process .GetProcessesByName(ProcessName) .Any(p => IsMatchingClientVersion(stormReplay, p)), token))); } }
public async Task CreateAsync(StormReplay stormReplay) { DateTime start = DateTime.Now; logger.LogInformation($"Creating session for: {stormReplay.Path}"); var players = replayAnalyzer.GetPlayers(stormReplay.Replay); var panels = replayAnalyzer.GetPanels(stormReplay.Replay); var end = replayAnalyzer.GetEnd(stormReplay.Replay); var isCarriedObjectiveMap = replayAnalyzer.IsCarriedObjectiveMap(stormReplay.Replay); sessionSetter.Set(new SessionData(players, panels, end, isCarriedObjectiveMap), stormReplay); logger.LogDebug($"Time to create session data: {DateTime.Now - start}"); logger.LogInformation($"Session set for: {stormReplay.Path}"); }
private static void Parse(string replayPath, bool onlyResult) { StormReplayResult stormReplayResult = StormReplay.Parse(replayPath, new ParseOptions() { AllowPTR = true, ShouldParseTrackerEvents = true, ShouldParseGameEvents = true, ShouldParseMessageEvents = true, }); ResultLine(stormReplayResult); if (!onlyResult) { GetInfo(stormReplayResult); Console.WriteLine(); } }
public static void Parse(StormReplay replay, ReadOnlySpan <byte> source) { BitReader bitReader = new BitReader(source, EndianType.BigEndian); uint gameLoop = 0; while (bitReader.Index < source.Length) { gameLoop += new VersionedDecoder(ref bitReader).ChoiceData !.GetValueAsUInt32(); TimeSpan timeSpan = TimeSpan.FromSeconds(gameLoop / 16.0); StormTrackerEventType type = (StormTrackerEventType) new VersionedDecoder(ref bitReader).GetValueAsUInt32(); VersionedDecoder decoder = new VersionedDecoder(ref bitReader); replay.TrackerEventsInternal.Add(new StormTrackerEvent(type, timeSpan, decoder)); } SetParsedData(replay); }
public static void Main(string[] args) { if (args != null && args.Length == 1 && File.Exists(args[0])) { StormReplayResult stormReplayResult = StormReplay.Parse(args[0], new ParseOptions() { AllowPTR = true, ShouldParseGameEvents = true, ShouldParseMessageEvents = true, ShouldParseTrackerEvents = true, }); Console.WriteLine(stormReplayResult.Status); } else { Console.WriteLine("No file."); } }
private static void NoTrackerEvents(StormReplayResult result) { StormReplay replay = result.Replay !; Assert.IsNull(result.Replay.MapInfo.MapId); Assert.AreEqual(0, replay.TrackerEvents.Count); Assert.IsNull(replay.GetTeamLevels(StormTeam.Blue)); Assert.IsNull(replay.GetTeamLevels(StormTeam.Red)); Assert.IsNull(replay.GetTeamXPBreakdown(StormTeam.Blue)); Assert.IsNull(replay.GetTeamXPBreakdown(StormTeam.Red)); Assert.AreEqual(0, replay.DraftPicks.Count); List <StormPlayer> players = replay.StormPlayers.ToList(); Assert.IsNull(players[0].Talents[0].TalentNameId); Assert.IsNull(players[0].ScoreResult); Assert.IsNull(players[0].MatchAwards); Assert.IsNull(players[0].MatchAwardsCount); }
private async Task LaunchAndWait(StormReplay stormReplay) { // This will make the HeroSwitcher communicate with existing game to launch selected replay using (var defaultLaunch = Process.Start(ExplorerProcess, stormReplay.Path)) { Policy .Handle <Exception>() .OrResult <bool>(result => result == false) .WaitAndRetry(retryCount: 150, retry => TimeSpan.FromSeconds(5)) .Execute(() => IsMatchingClientVersion(stormReplay.Replay)); } var searchTerms = stormReplay.Replay.Players.Select(x => x.Name).Concat(stormReplay.Replay.Players.Select(x => x.Character)).Concat(settings.OCR.LoadingScreenText).Concat(new[] { stormReplay.Replay.Map }); await Policy .Handle <Exception>() .OrResult <bool>(result => result == false) .WaitAndRetryAsync(retryCount: 10, retry => TimeSpan.FromSeconds(5)) .ExecuteAsync(async(t) => await ContainsAnyAsync(searchTerms), this.tokenProvider.Token); }