private async void EditGame(GameStats game) { if (game == null) { return; } var dialog = new AddGameDialog(game); await Helper.MainWindow.ShowMetroDialogAsync(dialog, new MetroDialogSettings { AffirmativeButtonText = "save", NegativeButtonText = "cancel" }); var result = await dialog.WaitForButtonPressAsync(); await Helper.MainWindow.HideMetroDialogAsync(dialog); if (result == null) //cancelled { return; } Refresh(); if (Config.Instance.HearthStatsAutoUploadNewGames && HearthStatsAPI.IsLoggedIn) { var deck = DeckList.Instance.Decks.FirstOrDefault(d => d.DeckId == game.DeckId); if (deck != null) { if (game.GameMode == GameMode.Arena) { HearthStatsManager.UpdateArenaMatchAsync(game, deck, true, true); } else { HearthStatsManager.UpdateMatchAsync(game, deck.GetVersion(game.PlayerDeckVersion), true, true); } } } DeckStatsList.Save(); Helper.MainWindow.DeckPickerList.UpdateDecks(); }
private static async Task <bool> TryUpload(string[] logLines, GameMetaData gameMetaData, GameStats game, bool submitFailure) { try { game?.HsReplay.UploadTry(); var lines = logLines.SkipWhile(x => !x.Contains("CREATE_GAME")).ToArray(); var metaData = UploadMetaDataGenerator.Generate(gameMetaData, game); Log.Info("Creating upload request..."); var uploadRequest = await ApiWrapper.CreateUploadRequest(metaData); Log.Info("Upload Id: " + uploadRequest.ShortId); await ApiWrapper.UploadLog(uploadRequest, lines); Log.Info("Upload complete"); if (game != null) { game.HsReplay.UploadId = uploadRequest.ShortId; game.HsReplay.ReplayUrl = uploadRequest.ReplayUrl; if (DefaultDeckStats.Instance.DeckStats.Any(x => x.DeckId == game.DeckId)) { DefaultDeckStats.Save(); } else { DeckStatsList.Save(); } } return(true); } catch (WebException ex) { Log.Error(ex); if (submitFailure) { Influx.OnGameUploadFailed(ex.Status); } return(false); } }
private void DeleteDeck(Deck deck, bool saveAndUpdate = true) { if (deck == null) { return; } DeckStats deckStats; if (DeckStatsList.Instance.DeckStats.TryGetValue(deck.DeckId, out deckStats)) { if (deckStats.Games.Any()) { if (Config.Instance.KeepStatsWhenDeletingDeck) { var defaultDeck = DefaultDeckStats.Instance.GetDeckStats(deck.Class); defaultDeck?.Games.AddRange(deckStats.Games); DefaultDeckStats.Save(); Log.Info($"Moved deckstats for deck {deck.Name} to default stats"); } } DeckStatsList.Instance.DeckStats.TryRemove(deckStats.DeckId, out deckStats); if (saveAndUpdate) { DeckStatsList.Save(); } Log.Info("Removed deckstats from deck: " + deck.Name); } DeckList.Instance.Decks.Remove(deck); if (saveAndUpdate) { DeckList.Save(); DeckPickerList.UpdateDecks(); DeckPickerList.UpdateArchivedClassVisibility(); } ListViewDeck.ItemsSource = null; Log.Info("Deleted deck: " + deck.Name); }
public static async Task <bool> ShowAddGameDialog(this MetroWindow window, Deck deck) { if (deck == null) { return(false); } var dialog = new AddGameDialog(deck); await window.ShowMetroDialogAsync(dialog, new MetroDialogSettings { AffirmativeButtonText = "save", NegativeButtonText = "cancel" }); var game = await dialog.WaitForButtonPressAsync(); await window.HideMetroDialogAsync(dialog); if (game == null) { return(false); } deck.DeckStats.AddGameResult(game); DeckStatsList.Save(); Core.MainWindow.DeckPickerList.UpdateDecks(forceUpdate: new[] { deck }); return(true); }
internal async void ShowDeleteDecksMessage(IEnumerable <Deck> decks) { if (decks == null) { return; } var decksList = decks.ToList(); if (!decksList.Any()) { return; } var settings = new MessageDialogs.Settings { AffirmativeButtonText = "Yes", NegativeButtonText = "No" }; var keepStatsInfo = Config.Instance.KeepStatsWhenDeletingDeck ? "The stats will be kept (can be changed in options)" : "The stats will be deleted (can be changed in options)"; var result = await this.ShowMessageAsync("Deleting " + (decksList.Count == 1 ? decksList.First().Name : decksList.Count + " decks"), "Are you Sure?\n" + keepStatsInfo, MessageDialogStyle.AffirmativeAndNegative, settings); if (result == MessageDialogResult.Negative) { return; } foreach (var deck in decksList) { DeleteDeck(deck, false); } DeckStatsList.Save(); DeckList.Save(); DeckPickerList.UpdateDecks(); DeckPickerList.UpdateArchivedClassVisibility(); DeckManagerEvents.OnDeckDeleted.Execute(decksList); }
private static void ResolveDeckStatsIssue() { foreach (var deck in DeckList.Instance.Decks) { foreach (var deckVersion in deck.Versions) { if (deckVersion.DeckStats.Games.Any()) { var games = deckVersion.DeckStats.Games.ToList(); foreach (var game in games) { deck.DeckStats.AddGameResult(game); deckVersion.DeckStats.Games.Remove(game); } } } } foreach (var deckStats in DeckStatsList.Instance.DeckStats) { if (deckStats.Games.Any() && !DeckList.Instance.Decks.Any(d => deckStats.BelongsToDeck(d))) { var games = deckStats.Games.ToList(); foreach (var game in games) { var defaultStats = DefaultDeckStats.Instance.GetDeckStats(game.PlayerHero); if (defaultStats != null) { defaultStats.AddGameResult(game); deckStats.Games.Remove(game); } } } } DeckStatsList.Save(); Config.Instance.ResolvedDeckStatsIssue = true; Config.Save(); }
//https://github.com/HearthSim/Hearthstone-Deck-Tracker/issues/2675 private static void FixDeckStats() { var save = false; foreach (var d in DeckList.Instance.Decks.Where(d => d.DeckStats.DeckId != d.DeckId)) { if (!DeckStatsList.Instance.DeckStats.TryGetValue(d.DeckId, out var deckStats)) { continue; } foreach (var game in deckStats.Games.ToList()) { deckStats.Games.Remove(game); d.DeckStats.Games.Add(game); } save = true; } if (save) { DeckStatsList.Save(); Core.MainWindow.DeckPickerList.UpdateDecks(); } }
private void StoryboardFadeOut_OnCompleted(object sender, EventArgs e) { if (_edited) { DeckStatsList.Save(); if (Config.Instance.HearthStatsAutoUploadNewGames && HearthStatsAPI.IsLoggedIn) { var deck = DeckList.Instance.Decks.FirstOrDefault(d => d.DeckId == _game.DeckId); if (deck != null) { if (_game.HasHearthStatsId) { if (_game.GameMode == GameMode.Arena) { HearthStatsManager.UpdateArenaMatchAsync(_game, deck, true, true); } else { HearthStatsManager.UpdateMatchAsync(_game, deck.GetVersion(_game.PlayerDeckVersion), true, true); } } else { if (_game.GameMode == GameMode.Arena) { HearthStatsManager.UploadArenaMatchAsync(_game, deck, true, true).Forget(); } else { HearthStatsManager.UploadMatchAsync(_game, deck.GetVersion(_game.PlayerDeckVersion), true, true).Forget(); } } } } } Close(); }
internal async void BtnCloneSelectedVersion_Click(object sender, RoutedEventArgs e) { var deck = DeckPickerList.SelectedDecks.FirstOrDefault(); if (deck == null) { return; } var cloneStats = (await this.ShowMessageAsync("Clone game history?", "(Cloned games do not count towards class or overall stats.)", MessageDialogStyle.AffirmativeAndNegative, new MetroDialogSettings { AffirmativeButtonText = "clone history", NegativeButtonText = "do not clone history" })) == MessageDialogResult.Affirmative; var clone = (Deck)deck.CloneWithNewId(false); // bug #1316 - ResetVersions needs the current version as an argument clone.ResetVersions(); clone.ResetHearthstatsIds(); clone.Archived = false; var originalStatsEntry = clone.DeckStats; /*while(DeckList.DecksList.Any(d => d.Name == clone.Name)) * { * var settings = new MetroDialogSettings {AffirmativeButtonText = "Set", DefaultText = clone.Name}; * var name = * await * this.ShowInputAsync("Name already exists", "You already have a deck with that name, please select a different one.", settings); * * if(string.IsNullOrEmpty(name)) * return; * * clone.Name = name; * }*/ DeckList.Instance.Decks.Add(clone); DeckPickerList.UpdateDecks(); DeckList.Save(); var newStatsEntry = DeckStatsList.Instance.DeckStats.FirstOrDefault(ds => ds.BelongsToDeck(clone)); if (newStatsEntry == null) { newStatsEntry = new DeckStats(clone); DeckStatsList.Instance.DeckStats.Add(newStatsEntry); } //clone game stats if (cloneStats) { foreach (var game in originalStatsEntry.Games) { newStatsEntry.AddGameResult(game.CloneWithNewId()); } Logger.WriteLine("cloned gamestats (version)", "Edit"); } DeckStatsList.Save(); //DeckPickerList.UpdateList(); DeckPickerList.SelectDeckAndAppropriateView(clone); if (Config.Instance.HearthStatsAutoUploadNewDecks && HearthStatsAPI.IsLoggedIn) { HearthStatsManager.UploadDeckAsync(clone); } }
public async void SaveDeck(bool overwrite, SerializableVersion newVersion, bool workInProgressDeck = false) { var deckName = TextBoxDeckName.Text; if (string.IsNullOrEmpty(deckName)) { var settings = new MessageDialogs.Settings { AffirmativeButtonText = "Set", DefaultText = deckName }; var name = await this.ShowInputAsync("No name set", "Please set a name for the deck", settings); if (string.IsNullOrEmpty(name)) { return; } deckName = name; TextBoxDeckName.Text = name; } if (_newDeck.Cards.Sum(c => c.Count) != 30 && workInProgressDeck == false) { var settings = new MessageDialogs.Settings { AffirmativeButtonText = "Yes", NegativeButtonText = "No" }; var result = await this.ShowMessageAsync("Not 30 cards", $"Deck contains {_newDeck.Cards.Sum(c => c.Count)} cards. Is this what you want to save anyway?", MessageDialogStyle.AffirmativeAndNegative, settings); if (result != MessageDialogResult.Affirmative) { return; } } var previousVersion = _newDeck.Version; if (overwrite && (_newDeck.Version != newVersion)) { AddDeckHistory(); _newDeck.Version = newVersion; _newDeck.SelectedVersion = newVersion; _newDeck.HearthStatsDeckVersionId = ""; } if (EditingDeck && overwrite) { DeckList.Instance.Decks.Remove(_newDeck); } var oldDeckName = _newDeck.Name; _newDeck.Name = deckName; var newDeckClone = (Deck)_newDeck.Clone(); newDeckClone.Archived = false; DeckList.Instance.Decks.Add(newDeckClone); newDeckClone.LastEdited = DateTime.Now; DeckList.Save(); Log.Info("Saved Decks"); if (EditingDeck) { TagControlEdit.SetSelectedTags(new List <string>()); if (deckName != oldDeckName) { var statsEntry = DeckStatsList.Instance.DeckStats.FirstOrDefault(ds => ds.BelongsToDeck(_newDeck)); if (statsEntry != null) { if (overwrite) { statsEntry.Name = deckName; Log.Info("Deck has new name, updated deckstats"); foreach (var game in statsEntry.Games) { game.DeckName = deckName; } } else { var newStatsEntry = DeckStatsList.Instance.DeckStats.FirstOrDefault(ds => ds.BelongsToDeck(_newDeck)); if (newStatsEntry == null) { newStatsEntry = new DeckStats(_newDeck); DeckStatsList.Instance.DeckStats.Add(newStatsEntry); } foreach (var game in statsEntry.Games) { newStatsEntry.AddGameResult(game.CloneWithNewId()); } Log.Info("cloned gamestats for \"Set as new\""); } DeckStatsList.Save(); } } } if (Config.Instance.HearthStatsAutoUploadNewDecks && HearthStatsAPI.IsLoggedIn) { Log.Info("auto uploading new/edited deck"); if (EditingDeck) { if (previousVersion != newVersion) { HearthStatsManager.UploadVersionAsync(newDeckClone, _originalDeck.HearthStatsIdForUploading, background: true).Forget(); } else { HearthStatsManager.UpdateDeckAsync(newDeckClone, background: true).Forget(); } } else { HearthStatsManager.UploadDeckAsync(newDeckClone, background: true).Forget(); } } if (EditingDeck) { DeckManagerEvents.OnDeckUpdated.Execute(newDeckClone); } else { DeckManagerEvents.OnDeckCreated.Execute(newDeckClone); } EditingDeck = false; foreach (var tag in _newDeck.Tags) { SortFilterDecksFlyout.AddSelectedTag(tag); } DeckPickerList.SelectDeckAndAppropriateView(newDeckClone); DeckPickerList.UpdateDecks(forceUpdate: new[] { newDeckClone }); SelectDeck(newDeckClone, true); CloseNewDeck(); ClearNewDeckSection(); }
private async void Window_Closing(object sender, CancelEventArgs e) { try { if (HearthStatsManager.SyncInProgress && !_closeAnyway) { e.Cancel = true; var result = await this.ShowMessageAsync("WARNING! Sync with HearthStats in progress!", "Closing Hearthstone Deck Tracker now can cause data inconsistencies. Are you sure?", MessageDialogStyle.AffirmativeAndNegative, new MessageDialogs.Settings { AffirmativeButtonText = "close anyway", NegativeButtonText = "wait" }); if (result == MessageDialogResult.Negative) { while (HearthStatsManager.SyncInProgress) { await Task.Delay(100); } await this.ShowMessage("Sync is complete.", "You can close Hearthstone Deck Tracker now."); } else { _closeAnyway = true; Close(); } } Core.UpdateOverlay = false; Core.Update = false; //wait for update to finish, might otherwise crash when overlay gets disposed for (var i = 0; i < 100; i++) { if (Core.CanShutdown) { break; } await Task.Delay(50); } ReplayReader.CloseViewers(); Config.Instance.SelectedTags = Config.Instance.SelectedTags.Distinct().ToList(); //Config.Instance.ShowAllDecks = DeckPickerList.ShowAll; Config.Instance.SelectedDeckPickerClasses = DeckPickerList.SelectedClasses.ToArray(); Config.Instance.WindowWidth = (int)(Width - (GridNewDeck.Visibility == Visible ? GridNewDeck.ActualWidth : 0)); Config.Instance.WindowHeight = (int)(Height - _heightChangeDueToSearchBox); Config.Instance.TrackerWindowTop = (int)Top; Config.Instance.TrackerWindowLeft = (int)(Left + (MovedLeft ?? 0)); //position of add. windows is NaN if they were never opened. if (!double.IsNaN(Core.Windows.PlayerWindow.Left)) { Config.Instance.PlayerWindowLeft = (int)Core.Windows.PlayerWindow.Left; } if (!double.IsNaN(Core.Windows.PlayerWindow.Top)) { Config.Instance.PlayerWindowTop = (int)Core.Windows.PlayerWindow.Top; } Config.Instance.PlayerWindowHeight = (int)Core.Windows.PlayerWindow.Height; if (!double.IsNaN(Core.Windows.OpponentWindow.Left)) { Config.Instance.OpponentWindowLeft = (int)Core.Windows.OpponentWindow.Left; } if (!double.IsNaN(Core.Windows.OpponentWindow.Top)) { Config.Instance.OpponentWindowTop = (int)Core.Windows.OpponentWindow.Top; } Config.Instance.OpponentWindowHeight = (int)Core.Windows.OpponentWindow.Height; if (!double.IsNaN(Core.Windows.TimerWindow.Left)) { Config.Instance.TimerWindowLeft = (int)Core.Windows.TimerWindow.Left; } if (!double.IsNaN(Core.Windows.TimerWindow.Top)) { Config.Instance.TimerWindowTop = (int)Core.Windows.TimerWindow.Top; } Config.Instance.TimerWindowHeight = (int)Core.Windows.TimerWindow.Height; Config.Instance.TimerWindowWidth = (int)Core.Windows.TimerWindow.Width; Core.TrayIcon.NotifyIcon.Visible = false; Core.Overlay.Close(); await LogReaderManager.Stop(true); Core.Windows.TimerWindow.Shutdown(); Core.Windows.PlayerWindow.Shutdown(); Core.Windows.OpponentWindow.Shutdown(); Config.Save(); DeckList.Save(); DeckStatsList.Save(); PluginManager.SavePluginsSettings(); PluginManager.Instance.UnloadPlugins(); } catch (Exception) { //doesnt matter } finally { Application.Current.Shutdown(); } }
private async void SaveDeck(bool overwrite) { var deckName = TextBoxDeckName.Text; if (string.IsNullOrEmpty(deckName)) { var settings = new MetroDialogSettings { AffirmativeButtonText = "Set", DefaultText = deckName }; var name = await this.ShowInputAsync("No name set", "Please set a name for the deck", settings); if (String.IsNullOrEmpty(name)) { return; } deckName = name; TextBoxDeckName.Text = name; } while (DeckList.DecksList.Any(d => d.Name == deckName) && (!EditingDeck || !overwrite)) { var settings = new MetroDialogSettings { AffirmativeButtonText = "Set", DefaultText = deckName }; var name = await this.ShowInputAsync("Name already exists", "You already have a deck with that name, please select a different one.", settings); if (String.IsNullOrEmpty(name)) { return; } deckName = name; TextBoxDeckName.Text = name; } if (_newDeck.Cards.Sum(c => c.Count) != 30) { var settings = new MetroDialogSettings { AffirmativeButtonText = "Yes", NegativeButtonText = "No" }; var result = await this.ShowMessageAsync("Not 30 cards", string.Format("Deck contains {0} cards. Is this what you want to save anyway?", _newDeck.Cards.Sum(c => c.Count)), MessageDialogStyle.AffirmativeAndNegative, settings); if (result != MessageDialogResult.Affirmative) { return; } } if (EditingDeck && overwrite) { DeckList.DecksList.Remove(_newDeck); DeckPickerList.RemoveDeck(_newDeck); } var oldDeckName = _newDeck.Name; _newDeck.Name = deckName; _newDeck.Tags = TagControlEdit.GetTags(); var newDeckClone = (Deck)_newDeck.Clone(); DeckList.DecksList.Add(newDeckClone); newDeckClone.LastEdited = DateTime.Now; WriteDecks(); Logger.WriteLine("Saved Decks"); if (EditingDeck) { TagControlEdit.SetSelectedTags(new List <string>()); if (deckName != oldDeckName) { var statsEntry = DeckStatsList.Instance.DeckStats.FirstOrDefault(d => d.Name == oldDeckName); if (statsEntry != null) { if (overwrite) { statsEntry.Name = deckName; Logger.WriteLine("Deck has new name, updated deckstats"); } else { var newStatsEntry = DeckStatsList.Instance.DeckStats.FirstOrDefault(d => d.Name == deckName); if (newStatsEntry == null) { newStatsEntry = new DeckStats(deckName); DeckStatsList.Instance.DeckStats.Add(newStatsEntry); } foreach (var game in statsEntry.Games) { newStatsEntry.AddGameResult(game.CloneWithNewId()); } Logger.WriteLine("cloned gamestats for \"Set as new\""); } DeckStatsList.Save(); } } } //after cloning the stats, otherwise new stats will be generated DeckPickerList.AddAndSelectDeck(newDeckClone); EditingDeck = false; foreach (var tag in _newDeck.Tags) { SortFilterDecksFlyout.AddSelectedTag(tag); } DeckPickerList.UpdateList(); DeckPickerList.SelectDeck(newDeckClone); CloseNewDeck(); ClearNewDeckSection(); }
private async void DeleteGames(DataGrid dataGrid, bool overall) { MetroWindow window; if (Config.Instance.StatsInWindow) { window = Core.Windows.StatsWindow; } else { window = Helper.MainWindow; } var count = dataGrid.SelectedItems.Count; if (count == 1) { var selectedGame = dataGrid.SelectedItem as GameStats; if (selectedGame == null) { return; } if (await window.ShowDeleteGameStatsMessage(selectedGame) != MessageDialogResult.Affirmative) { return; } if (!overall) { if (_deck.DeckStats.Games.Contains(selectedGame)) { selectedGame.DeleteGameFile(); _deck.DeckStats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game " + selectedGame + "(overall=" + overall + ")", "DeckStatsControl"); DeckStatsList.Save(); } } else { var deck = DeckList.Instance.Decks.FirstOrDefault(d => d.DeckStats.Games.Contains(selectedGame)); if (deck != null) { if (deck.DeckStats.Games.Contains(selectedGame)) { selectedGame.DeleteGameFile(); deck.DeckStats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game " + selectedGame + "(overall=" + overall + ")", "DeckStatsControl"); DefaultDeckStats.Save(); } } else { var deckstats = DefaultDeckStats.Instance.DeckStats.FirstOrDefault(ds => ds.Games.Contains(selectedGame)); if (deckstats != null) { selectedGame.DeleteGameFile(); deckstats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game " + selectedGame + "(overall=" + overall + ")", "DeckStatsControl"); DefaultDeckStats.Save(); } } } if (HearthStatsAPI.IsLoggedIn && selectedGame.HasHearthStatsId && await Core.MainWindow.ShowCheckHearthStatsMatchDeletionDialog()) { HearthStatsManager.DeleteMatchesAsync(new List <GameStats> { selectedGame }); } //Core.MainWindow.DeckPickerList.Items.Refresh(); Core.MainWindow.DeckPickerList.UpdateDecks(); Refresh(); } else if (count > 1) { if (await window.ShowDeleteMultipleGameStatsMessage(count) != MessageDialogResult.Affirmative) { return; } var selectedGames = dataGrid.SelectedItems.Cast <GameStats>().Where(g => g != null).ToList(); foreach (var selectedGame in selectedGames) { if (!overall) { if (_deck.DeckStats.Games.Contains(selectedGame)) { selectedGame.DeleteGameFile(); _deck.DeckStats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game " + selectedGame + "(overall=" + overall + ")", "DeckStatsControl"); } } else { var deck = DeckList.Instance.Decks.FirstOrDefault(d => d.DeckStats.Games.Contains(selectedGame)); if (deck != null) { if (deck.DeckStats.Games.Contains(selectedGame)) { selectedGame.DeleteGameFile(); deck.DeckStats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game " + selectedGame + "(overall=" + overall + ")", "DeckStatsControl"); } } else { var deckstats = DefaultDeckStats.Instance.DeckStats.FirstOrDefault(ds => ds.Games.Contains(selectedGame)); if (deckstats != null) { selectedGame.DeleteGameFile(); deckstats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game " + selectedGame + "(overall=" + overall + ")", "DeckStatsControl"); } } } } if (HearthStatsAPI.IsLoggedIn && selectedGames.Any(g => g.HasHearthStatsId) && await Core.MainWindow.ShowCheckHearthStatsMatchDeletionDialog()) { HearthStatsManager.DeleteMatchesAsync(selectedGames); } DeckStatsList.Save(); DefaultDeckStats.Save(); Logger.WriteLine("Deleted " + count + " games", "DeckStatsControl"); Core.MainWindow.DeckPickerList.UpdateDecks(); Refresh(); } }
private void MoveGameToOtherDeck(List <GameStats> selectedGames) { if (selectedGames == null) { return; } var heroes = new Dictionary <string, int>(); foreach (var game in selectedGames) { if (!heroes.ContainsKey(game.PlayerHero)) { heroes.Add(game.PlayerHero, 0); } heroes[game.PlayerHero]++; } var heroPlayed = heroes.Any() ? heroes.OrderByDescending(x => x.Value).First().Key : "Any"; var possibleTargets = DeckList.Instance.Decks.Where(d => d.Class == heroPlayed || heroPlayed == "Any"); var dialog = new MoveGameDialog(possibleTargets); if (Config.Instance.StatsInWindow) { dialog.Owner = Core.Windows.StatsWindow; } else { dialog.Owner = Helper.MainWindow; } dialog.ShowDialog(); var selectedDeck = dialog.SelectedDeck; if (selectedDeck == null) { return; } foreach (var game in selectedGames) { var defaultDeck = DefaultDeckStats.Instance.DeckStats.FirstOrDefault(ds => ds.Games.Contains(game)); if (defaultDeck != null) { defaultDeck.Games.Remove(game); DefaultDeckStats.Save(); } else { var deck = DeckList.Instance.Decks.FirstOrDefault(d => game.DeckId == d.DeckId); if (deck != null) { deck.DeckStats.Games.Remove(game); } } game.PlayerDeckVersion = dialog.SelectedVersion; game.HearthStatsDeckVersionId = selectedDeck.GetVersion(dialog.SelectedVersion).HearthStatsDeckVersionId; game.DeckId = selectedDeck.DeckId; game.DeckName = selectedDeck.Name; selectedDeck.DeckStats.Games.Add(game); if (HearthStatsAPI.IsLoggedIn && Config.Instance.HearthStatsAutoUploadNewGames) { HearthStatsManager.MoveMatchAsync(game, selectedDeck, background: true); } } DeckStatsList.Save(); DeckList.Save(); Refresh(); Core.MainWindow.DeckPickerList.UpdateDecks(); }
public static async Task <PostResult> PostMultipleGameResultsAsync(IEnumerable <GameStats> games, Deck deck) { var validGames = games.Where(x => IsValidGame(x) && !x.HasHearthStatsId).ToList(); long versionId; if (!long.TryParse(deck.HearthStatsDeckVersionId, out versionId)) { Log.Error("invalid HearthStatsDeckVersionId"); return(PostResult.Failed); } var url = BaseUrl + "/matches/multi_create?auth_token=" + _authToken; dynamic gameObjs = new ExpandoObject[validGames.Count]; for (int i = 0; i < validGames.Count; i++) { gameObjs[i] = new ExpandoObject(); gameObjs[i].mode = validGames[i].GameMode.ToString(); gameObjs[i].@class = string.IsNullOrEmpty(validGames[i].PlayerHero) ? deck.Class : validGames[i].PlayerHero; gameObjs[i].result = validGames[i].Result.ToString(); gameObjs[i].coin = validGames[i].Coin.ToString().ToLower(); gameObjs[i].numturns = validGames[i].Turns; gameObjs[i].duration = (int)(validGames[i].EndTime - validGames[i].StartTime).TotalSeconds; gameObjs[i].deck_id = deck.HearthStatsIdForUploading; gameObjs[i].deck_version_id = versionId; if (!string.IsNullOrEmpty(validGames[i].OpponentHero)) { gameObjs[i].oppclass = validGames[i].OpponentHero; } if (!string.IsNullOrEmpty(validGames[i].OpponentName)) { gameObjs[i].oppname = validGames[i].OpponentName; } if (!string.IsNullOrEmpty(validGames[i].Note)) { gameObjs[i].notes = validGames[i].Note; } if (validGames[i].GameMode == GameMode.Ranked && validGames[i].HasRank) { gameObjs[i].ranklvl = validGames[i].Rank.ToString(); } var opponentCards = validGames[i].GetOpponentDeck().Cards; if (opponentCards.Where(Database.IsActualCard).Any()) { gameObjs[i].oppcards = opponentCards.Where(Database.IsActualCard).Select(c => new { id = c.Id, count = c.Count }).ToArray(); } gameObjs[i].created_at = validGames[i].StartTime.ToUniversalTime().ToString("s"); } var data = JsonConvert.SerializeObject(new { deck_id = deck.HearthStatsIdForUploading, matches = gameObjs }); try { var response = await PostAsync(url, data); dynamic json = JsonConvert.DeserializeObject(response); if (json.status.ToString() == "404") { //deck does not exist on hearthstats deck.ResetHearthstatsIds(); DeckList.Save(); deck.DeckStats.Games.ForEach(g => g.ResetHearthstatsIds()); DeckStatsList.Save(); return(PostResult.Failed); } if (json.status.ToString() != "200") { Log.Error($"{json.message.ToString()} (Status code: {json.status.ToString()})"); } for (int i = 0; i < validGames.Count; i++) { if (json.data[i].status == "200") { validGames[i].HearthStatsId = json.data[i].data.id; Log.Info("assigned id to match: " + validGames[i].HearthStatsId); } else { Log.Error($"Error uploading match {validGames[i]}: {json.data[i].status}"); } } return(PostResult.WasSuccess); } catch (Exception e) { Log.Error(e); return(PostResult.Failed); } }
private void SaveAndUpdateStats() { if (RecordCurrentGameMode) { if (Config.Instance.ShowNoteDialogAfterGame && Config.Instance.NoteDialogDelayed && !_showedNoteDialog) { _showedNoteDialog = true; new NoteDialog(_game.CurrentGameStats); } if (_game.CurrentGameStats != null) { _game.CurrentGameStats.Turns = _game.GetTurnNumber(); if (Config.Instance.DiscardZeroTurnGame && _game.CurrentGameStats.Turns < 1) { Log.Info("Game has 0 turns, discarded. (DiscardZeroTurnGame)"); return; } if (_game.CurrentGameStats.GameMode != _game.CurrentGameMode) { _game.CurrentGameStats.GameMode = _game.CurrentGameMode; Log.Info("Set CurrentGameStats.GameMode to " + _game.CurrentGameMode); } if (_game.CurrentGameStats.GameMode == Arena) { ArenaStats.Instance.UpdateArenaStats(); ArenaStats.Instance.UpdateArenaRuns(); ArenaStats.Instance.UpdateArenaStatsHighlights(); } else { ConstructedStats.Instance.UpdateConstructedStats(); } } if (_assignedDeck == null) { Log.Info("Saving DefaultDeckStats"); DefaultDeckStats.Save(); } else { _assignedDeck.StatsUpdated(); Log.Info("Saving DeckStats"); DeckStatsList.Save(); } LastGames.Instance.Add(_game.CurrentGameStats); LastGames.Save(); } else if (_assignedDeck != null && _assignedDeck.DeckStats.Games.Contains(_game.CurrentGameStats)) { //game was not supposed to be recorded, remove from deck again. _assignedDeck.DeckStats.Games.Remove(_game.CurrentGameStats); Log.Info($"Gamemode {_game.CurrentGameMode} is not supposed to be saved. Removed game from {_assignedDeck}."); } else if (_assignedDeck == null) { var defaultDeck = DefaultDeckStats.Instance.GetDeckStats(_game.Player.Class); if (defaultDeck != null) { defaultDeck.Games.Remove(_game.CurrentGameStats); Log.Info($"Gamemode {_game.CurrentGameMode} is not supposed to be saved. Removed game from default {_game.Player.Class}."); } } }
private void BtnMoveToOtherDeck_Click(object sender, RoutedEventArgs e) { var selectedGame = DataGridGames.SelectedItem as GameStats; if (selectedGame == null) { return; } var heroes = new Dictionary <string, int>(); foreach (var turn in selectedGame.TurnStats) { foreach (var play in turn.Plays) { if (!play.Type.ToString().Contains("Player")) { continue; } var hero = Game.GetCardFromId(play.CardId).PlayerClass; if (hero == null) { continue; } if (!heroes.ContainsKey(hero)) { heroes.Add(hero, 0); } heroes[hero]++; } } var heroPlayed = heroes.OrderByDescending(x => x.Value).First().Key; var possibleTargets = Helper.MainWindow.DeckList.DecksList.Where(d => d.Class == heroPlayed); var dialog = new MoveGameDialog(possibleTargets); if (Config.Instance.StatsInWindow) { dialog.Owner = Helper.MainWindow.StatsWindow; } else { dialog.Owner = Helper.MainWindow; } dialog.ShowDialog(); var selectedDeck = dialog.SelectedDeck; if (selectedDeck == null) { return; } _deck.DeckStats.Games.Remove(selectedGame); selectedDeck.DeckStats.Games.Add(selectedGame); DeckStatsList.Save(); Helper.MainWindow.WriteDecks(); Refresh(); Helper.MainWindow.DeckPickerList.UpdateList(); }
private async void BtnDelete_Click(object sender, RoutedEventArgs e) { if (_deck == null) { return; } MetroWindow window; if (Config.Instance.StatsInWindow) { window = Helper.MainWindow.StatsWindow; } else { window = Helper.MainWindow; } var count = DataGridGames.SelectedItems.Count; if (count == 1) { var selectedGame = DataGridGames.SelectedItem as GameStats; if (selectedGame == null) { return; } if (await window.ShowDeleteGameStatsMessage(selectedGame) != MessageDialogResult.Affirmative) { return; } if (_deck.DeckStats.Games.Contains(selectedGame)) { selectedGame.DeleteGameFile(); _deck.DeckStats.Games.Remove(selectedGame); DeckStatsList.Save(); Logger.WriteLine("Deleted game: " + selectedGame); Helper.MainWindow.DeckPickerList.Items.Refresh(); Refresh(); } } else if (count > 1) { if (await window.ShowDeleteMultipleGameStatsMessage(count) != MessageDialogResult.Affirmative) { return; } foreach (var selectedItem in DataGridGames.SelectedItems) { var selectedGame = selectedItem as GameStats; if (selectedGame == null) { continue; } if (!_deck.DeckStats.Games.Contains(selectedGame)) { continue; } selectedGame.DeleteGameFile(); _deck.DeckStats.Games.Remove(selectedGame); } DeckStatsList.Save(); Logger.WriteLine("Deleted " + count + " games"); Helper.MainWindow.DeckPickerList.Items.Refresh(); Refresh(); } }
/// <summary> /// v0.10.0 caused opponent names to be saved as the hero, rather than the name. /// </summary> private static async void ResolveOpponentNames() { var games = DeckStatsList.Instance.DeckStats.SelectMany(ds => ds.Games) .Where(g => g.HasReplayFile && Enum.GetNames(typeof(HeroClass)).Any(x => x == g.OpponentName)) .ToList(); if (!games.Any()) { Config.Instance.ResolvedOpponentNames = true; Config.Save(); return; } var controller = await Core.MainWindow.ShowProgressAsync("Fixing opponent names in recorded games...", "v0.10.0 caused opponent names to be set to their hero, rather than the actual name.\n\nThis may take a moment.\n\nYou can cancel to continue this at a later time (or not at all).", true); var count = 0; var lockMe = new object(); await Task.Run(() => { Parallel.ForEach(games, (game, loopState) => { if (controller.IsCanceled) { loopState.Stop(); } List <ReplayKeyPoint> replay = ReplayReader.LoadReplay(game.ReplayFile); if (replay == null) { return; } var last = replay.LastOrDefault(); if (last == null) { return; } var opponent = last.Data.FirstOrDefault(x => x.IsOpponent); if (opponent == null) { return; } game.OpponentName = opponent.Name; lock (lockMe) { controller.SetProgress(1.0 * ++count / games.Count); } }); }); await controller.CloseAsync(); if (controller.IsCanceled) { var fix = await Core.MainWindow.ShowMessageAsync("Cancelled", "Fix remaining names on next start?", MessageDialogStyle.AffirmativeAndNegative, new MetroDialogSettings { AffirmativeButtonText = "next time", NegativeButtonText = "don\'t fix" }); if (fix == MessageDialogResult.Negative) { Config.Instance.ResolvedOpponentNames = true; Config.Save(); } } else { Config.Instance.ResolvedOpponentNames = true; Config.Save(); } DeckStatsList.Save(); }
private async void BtnCloneSelectedVersion_Click(object sender, RoutedEventArgs e) { var deck = DeckPickerList.GetSelectedDeckVersion(); if (deck == null) { return; } var cloneStats = (await this.ShowMessageAsync("Clone game history?", "(Cloned games do not count towards class or overall stats.)", MessageDialogStyle.AffirmativeAndNegative, new MetroDialogSettings { AffirmativeButtonText = "clone history", NegativeButtonText = "do not clone history" })) == MessageDialogResult.Affirmative; var clone = (Deck)deck.Clone(); clone.ResetVersions(); var originalStatsEntry = clone.DeckStats; while (DeckList.DecksList.Any(d => d.Name == clone.Name)) { var settings = new MetroDialogSettings { AffirmativeButtonText = "Set", DefaultText = clone.Name }; var name = await this.ShowInputAsync("Name already exists", "You already have a deck with that name, please select a different one.", settings); if (string.IsNullOrEmpty(name)) { return; } clone.Name = name; } DeckList.DecksList.Add(clone); DeckPickerList.AddAndSelectDeck(clone); WriteDecks(); var newStatsEntry = DeckStatsList.Instance.DeckStats.FirstOrDefault(d => d.Name == clone.Name); if (newStatsEntry == null) { newStatsEntry = new DeckStats(clone.Name); DeckStatsList.Instance.DeckStats.Add(newStatsEntry); } //clone game stats if (cloneStats) { foreach (var game in originalStatsEntry.Games) { newStatsEntry.AddGameResult(game.CloneWithNewId()); } Logger.WriteLine("cloned gamestats"); } DeckStatsList.Save(); DeckPickerList.UpdateList(); }
public static async Task <PostResult> PostGameResultAsync(GameStats game, Deck deck) { if (!IsValidGame(game)) { return(PostResult.Failed); } if (!deck.HasHearthStatsId) { Log.Warn("can not upload game, deck has no hearthstats id"); return(PostResult.Failed); } long versionId; if (!long.TryParse(deck.HearthStatsDeckVersionId, out versionId)) { Log.Error("invalid HearthStatsDeckVersionId"); return(PostResult.Failed); } Log.Info("uploading match: " + game); var url = BaseUrl + "/matches?auth_token=" + _authToken; dynamic gameObj = new ExpandoObject(); gameObj.mode = game.GameMode.ToString(); gameObj.@class = string.IsNullOrEmpty(game.PlayerHero) ? deck.Class : game.PlayerHero; gameObj.result = game.Result.ToString(); gameObj.coin = game.Coin.ToString().ToLower(); gameObj.numturns = game.Turns; gameObj.duration = (int)(game.EndTime - game.StartTime).TotalSeconds; gameObj.deck_id = deck.HearthStatsIdForUploading; gameObj.deck_version_id = versionId; if (!string.IsNullOrEmpty(game.OpponentHero)) { gameObj.oppclass = game.OpponentHero; } if (!string.IsNullOrEmpty(game.OpponentName)) { gameObj.oppname = game.OpponentName; } if (!string.IsNullOrEmpty(game.Note)) { gameObj.notes = game.Note; } if (game.GameMode == GameMode.Ranked && game.HasRank) { gameObj.ranklvl = game.Rank.ToString(); } var opponentCards = game.GetOpponentDeck().Cards; if (opponentCards.Where(Database.IsActualCard).Any()) { gameObj.oppcards = opponentCards.Where(Database.IsActualCard).Select(c => new { id = c.Id, count = c.Count }).ToArray(); } gameObj.created_at = game.StartTime.ToUniversalTime().ToString("s"); var data = JsonConvert.SerializeObject(gameObj); try { var response = await PostAsync(url, data); var json = JsonConvert.DeserializeObject(response); if (json.status.ToString() == "200") { game.HearthStatsId = json.data.id; Log.Info("assigned id to match: " + game.HearthStatsId); return(PostResult.WasSuccess); } if (json.status.ToString() == "fail" && json.message.ToString().Contains("Deck could not be found")) { //deck does not exist on hearthstats deck.ResetHearthstatsIds(); DeckList.Save(); deck.DeckStats.Games.ForEach(g => g.ResetHearthstatsIds()); DeckStatsList.Save(); } return(PostResult.Failed); } catch (Exception e) { Log.Error(e); return(PostResult.Failed); } }
private void MoveGameToOtherDeck(GameStats selectedGame) { if (selectedGame == null) { return; } var heroes = new Dictionary <string, int>(); foreach (var turn in selectedGame.TurnStats) { foreach (var play in turn.Plays) { if (!play.Type.ToString().Contains("Player")) { continue; } var hero = Game.GetCardFromId(play.CardId).PlayerClass; if (hero == null) { continue; } if (!heroes.ContainsKey(hero)) { heroes.Add(hero, 0); } heroes[hero]++; } } var heroPlayed = heroes.Any() ? heroes.OrderByDescending(x => x.Value).First().Key : "Any"; var possibleTargets = Helper.MainWindow.DeckList.DecksList.Where(d => d.Class == heroPlayed || heroPlayed == "Any"); var dialog = new MoveGameDialog(possibleTargets); if (Config.Instance.StatsInWindow) { dialog.Owner = Helper.MainWindow.StatsWindow; } else { dialog.Owner = Helper.MainWindow; } dialog.ShowDialog(); var selectedDeck = dialog.SelectedDeck; if (selectedDeck == null) { return; } var defaultDeck = DefaultDeckStats.Instance.DeckStats.FirstOrDefault(ds => ds.Games.Contains(selectedGame)); if (defaultDeck != null) { defaultDeck.Games.Remove(selectedGame); DefaultDeckStats.Save(); } else { _deck.DeckStats.Games.Remove(selectedGame); } selectedGame.PlayerDeckVersion = selectedDeck.Version; //move to latest version selectedDeck.DeckStats.Games.Add(selectedGame); DeckStatsList.Save(); Helper.MainWindow.WriteDecks(); Refresh(); Helper.MainWindow.DeckPickerList.UpdateList(); }
public void SetDeck(Deck deck) { _deck = deck; if (deck == null) { TabControlCurrentOverall.SelectedIndex = 1; TabItemDeck.Visibility = Visibility.Collapsed; TabItemOverall.Visibility = Visibility.Collapsed; StackPanelUnassignedFilter.Visibility = Visibility.Visible; return; } TabItemDeck.Visibility = Visibility.Visible; TabItemOverall.Visibility = Visibility.Visible; StackPanelUnassignedFilter.Visibility = TabControlCurrentOverall.SelectedIndex == 1 ? Visibility.Visible : Visibility.Collapsed; DataGridGames.Items.Clear(); var filteredGames = FilterGames(deck.DeckStats.Games).ToList(); var modified = false; foreach (var game in filteredGames) { if (!game.VerifiedHeroes && VerifyHeroes(game)) { modified = true; } if (Config.Instance.StatsFilterOpponentHeroClass == HeroClassAll.All || game.OpponentHero == Config.Instance.StatsFilterOpponentHeroClass.ToString()) { DataGridGames.Items.Add(game); } } if (modified) { DeckStatsList.Save(); } DataGridWinLoss.Items.Clear(); DataGridWinLoss.Items.Add(new WinLoss(filteredGames, "%")); DataGridWinLoss.Items.Add(new WinLoss(filteredGames, "Win - Loss")); //current version var games = filteredGames.Where(g => g.BelongsToDeckVerion(deck)).ToList(); DataGridWinLoss.Items.Add(new WinLoss(games, "%", deck.Version)); DataGridWinLoss.Items.Add(new WinLoss(games, "Win - Loss", deck.Version)); //prev versions foreach (var v in deck.Versions.OrderByDescending(d => d.Version)) { games = filteredGames.Where(g => g.BelongsToDeckVerion(v)).ToList(); DataGridWinLoss.Items.Add(new WinLoss(games, "%", v.Version)); DataGridWinLoss.Items.Add(new WinLoss(games, "Win - Loss", v.Version)); } var defaultStats = DefaultDeckStats.Instance.GetDeckStats(deck.Class) ?? new DeckStats(); DataGridWinLossClass.Items.Clear(); var allGames = DeckList.Instance.Decks.Where(d => d.GetClass == _deck.GetClass) .SelectMany(d => FilterGames(d.DeckStats.Games).Where(g => !g.IsClone)) .Concat(FilterGames(defaultStats.Games)) .ToList(); DataGridWinLossClass.Items.Add(new WinLoss(allGames, "%")); DataGridWinLossClass.Items.Add(new WinLoss(allGames, "Win - Loss")); DataGridGames.Items.Refresh(); }
private async void DeleteGames(DataGrid dataGrid, bool overall) { MetroWindow window; if (Config.Instance.StatsInWindow) { window = Helper.MainWindow.StatsWindow; } else { window = Helper.MainWindow; } var count = dataGrid.SelectedItems.Count; if (count == 1) { var selectedGame = dataGrid.SelectedItem as GameStats; if (selectedGame == null) { return; } if (await window.ShowDeleteGameStatsMessage(selectedGame) != MessageDialogResult.Affirmative) { return; } if (!overall) { if (_deck.DeckStats.Games.Contains(selectedGame)) { selectedGame.DeleteGameFile(); _deck.DeckStats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game: " + selectedGame); DeckStatsList.Save(); } } else { var deck = Helper.MainWindow.DeckList.DecksList.FirstOrDefault(d => d.DeckStats.Games.Contains(selectedGame)); if (deck != null) { if (deck.DeckStats.Games.Contains(selectedGame)) { selectedGame.DeleteGameFile(); deck.DeckStats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game: " + selectedGame); DefaultDeckStats.Save(); } } else { var deckstats = DefaultDeckStats.Instance.DeckStats.FirstOrDefault(ds => ds.Games.Contains(selectedGame)); if (deckstats != null) { selectedGame.DeleteGameFile(); deckstats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game: " + selectedGame); DefaultDeckStats.Save(); } } } Helper.MainWindow.DeckPickerList.Items.Refresh(); Refresh(); } else if (count > 1) { if (await window.ShowDeleteMultipleGameStatsMessage(count) != MessageDialogResult.Affirmative) { return; } foreach (var selectedItem in dataGrid.SelectedItems) { var selectedGame = selectedItem as GameStats; if (selectedGame == null) { continue; } if (!overall) { if (_deck.DeckStats.Games.Contains(selectedGame)) { selectedGame.DeleteGameFile(); _deck.DeckStats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game: " + selectedGame); } } else { var deck = Helper.MainWindow.DeckList.DecksList.FirstOrDefault(d => d.DeckStats.Games.Contains(selectedGame)); if (deck != null) { if (deck.DeckStats.Games.Contains(selectedGame)) { selectedGame.DeleteGameFile(); deck.DeckStats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game: " + selectedGame); } } else { var deckstats = DefaultDeckStats.Instance.DeckStats.FirstOrDefault(ds => ds.Games.Contains(selectedGame)); if (deckstats != null) { selectedGame.DeleteGameFile(); deckstats.Games.Remove(selectedGame); Logger.WriteLine("Deleted game: " + selectedGame); } } } } DeckStatsList.Save(); DefaultDeckStats.Save(); Logger.WriteLine("Deleted " + count + " games"); Helper.MainWindow.DeckPickerList.Items.Refresh(); Refresh(); } }
public void LoadOverallStats() { var needToSaveDeckStats = false; DataGridOverallWinLoss.Items.Clear(); DataGridOverallGames.Items.Clear(); var sortedCol = DataGridOverallGames.Columns.FirstOrDefault(col => col.SortDirection != null); var total = new List <GameStats>(); var modified = false; var classes = Enum.GetNames(typeof(HeroClass)).Concat(DefaultDeckStats.Instance.DeckStats.Select(x => x.Name)).Distinct(); foreach (var @class in classes) { var allGames = new List <GameStats>(); if (Config.Instance.StatsOverallFilterDeckMode == FilterDeckMode.WithDeck || Config.Instance.StatsOverallFilterDeckMode == FilterDeckMode.All) { allGames.AddRange(DeckList.Instance.Decks.Where(x => x.Class == @class && MatchesTagFilters(x)).SelectMany(d => d.DeckStats.Games)); } if (Config.Instance.StatsOverallFilterDeckMode == FilterDeckMode.WithoutDeck || Config.Instance.StatsOverallFilterDeckMode == FilterDeckMode.All) { allGames.AddRange(DefaultDeckStats.Instance.GetDeckStats(@class).Games); } allGames = FilterGames(allGames).Where(g => !g.IsClone).ToList(); total.AddRange(allGames); DataGridOverallWinLoss.Items.Add(new WinLoss(allGames, CheckboxPercent.IsChecked ?? true, @class)); foreach (var game in allGames) { if (string.IsNullOrEmpty(game.PlayerHero)) { //for some reason this does not get loaded after saving it to the xml game.PlayerHero = @class; needToSaveDeckStats = true; } if (!game.VerifiedHeroes && VerifyHeroes(game)) { modified = true; } if ((Config.Instance.StatsOverallFilterPlayerHeroClass == HeroClassAll.All || game.PlayerHero == Config.Instance.StatsOverallFilterPlayerHeroClass.ToString()) && (Config.Instance.StatsFilterOpponentHeroClass == HeroClassAll.All || game.OpponentHero == Config.Instance.StatsFilterOpponentHeroClass.ToString())) { DataGridOverallGames.Items.Add(game); } } } if (needToSaveDeckStats || modified) { DeckStatsList.Save(); } DataGridOverallWinLoss.Items.Add(new WinLoss(total, CheckboxPercent.IsChecked ?? true, "Total")); if (sortedCol != null) { var prevSorted = DataGridOverallGames.Columns.FirstOrDefault(col => col.Header == sortedCol.Header); if (prevSorted != null) { prevSorted.SortDirection = sortedCol.SortDirection; } } DataGridOverallGames.Items.Refresh(); }
private async void Window_Closing(object sender, CancelEventArgs e) { try { Log.Info("Shutting down..."); Influx.OnAppExit(Helper.GetCurrentVersion()); Core.UpdateOverlay = false; Core.Update = false; //wait for update to finish, might otherwise crash when overlay gets disposed for (var i = 0; i < 100; i++) { if (Core.CanShutdown) { break; } await Task.Delay(50); } Config.Instance.SelectedTags = Config.Instance.SelectedTags.Distinct().ToList(); //Config.Instance.ShowAllDecks = DeckPickerList.ShowAll; Config.Instance.SelectedDeckPickerClasses = DeckPickerList.SelectedClasses.ToArray(); Config.Instance.WindowWidth = (int)(Width - (GridNewDeck.Visibility == Visible ? GridNewDeck.ActualWidth : 0)); Config.Instance.WindowHeight = (int)(Height - _heightChangeDueToSearchBox); Config.Instance.TrackerWindowTop = (int)Top; Config.Instance.TrackerWindowLeft = (int)(Left + (MovedLeft ?? 0)); //position of add. windows is NaN if they were never opened. if (!double.IsNaN(Core.Windows.PlayerWindow.Left)) { Config.Instance.PlayerWindowLeft = (int)Core.Windows.PlayerWindow.Left; } if (!double.IsNaN(Core.Windows.PlayerWindow.Top)) { Config.Instance.PlayerWindowTop = (int)Core.Windows.PlayerWindow.Top; } Config.Instance.PlayerWindowHeight = (int)Core.Windows.PlayerWindow.Height; if (!double.IsNaN(Core.Windows.OpponentWindow.Left)) { Config.Instance.OpponentWindowLeft = (int)Core.Windows.OpponentWindow.Left; } if (!double.IsNaN(Core.Windows.OpponentWindow.Top)) { Config.Instance.OpponentWindowTop = (int)Core.Windows.OpponentWindow.Top; } Config.Instance.OpponentWindowHeight = (int)Core.Windows.OpponentWindow.Height; if (!double.IsNaN(Core.Windows.TimerWindow.Left)) { Config.Instance.TimerWindowLeft = (int)Core.Windows.TimerWindow.Left; } if (!double.IsNaN(Core.Windows.TimerWindow.Top)) { Config.Instance.TimerWindowTop = (int)Core.Windows.TimerWindow.Top; } Config.Instance.TimerWindowHeight = (int)Core.Windows.TimerWindow.Height; Config.Instance.TimerWindowWidth = (int)Core.Windows.TimerWindow.Width; Core.TrayIcon.NotifyIcon.Visible = false; Core.Overlay.Close(); await Core.StopLogWacher(); Core.Windows.TimerWindow.Shutdown(); Core.Windows.PlayerWindow.Shutdown(); Core.Windows.OpponentWindow.Shutdown(); Config.Save(); DeckList.Save(); DeckStatsList.Save(); PluginManager.SavePluginsSettings(); PluginManager.Instance.UnloadPlugins(); } catch (Exception) { //doesnt matter } finally { Application.Current.Shutdown(); } }
private async void SaveDeck(bool overwrite, SerializableVersion newVersion) { var deckName = TextBoxDeckName.Text; if (string.IsNullOrEmpty(deckName)) { var settings = new MetroDialogSettings { AffirmativeButtonText = "Set", DefaultText = deckName }; var name = await this.ShowInputAsync("No name set", "Please set a name for the deck", settings); if (String.IsNullOrEmpty(name)) { return; } deckName = name; TextBoxDeckName.Text = name; } /*while(DeckList.DecksList.Any(d => d.Name == deckName) && (!EditingDeck || !overwrite)) * { * var settings = new MetroDialogSettings {AffirmativeButtonText = "Set", DefaultText = deckName}; * var name = * await * this.ShowInputAsync("Name already exists", "You already have a deck with that name, please select a different one.", settings); * * if(String.IsNullOrEmpty(name)) * return; * * deckName = name; * TextBoxDeckName.Text = name; * }*/ if (_newDeck.Cards.Sum(c => c.Count) != 30) { var settings = new MetroDialogSettings { AffirmativeButtonText = "Yes", NegativeButtonText = "No" }; var result = await this.ShowMessageAsync("Not 30 cards", string.Format("Deck contains {0} cards. Is this what you want to save anyway?", _newDeck.Cards.Sum(c => c.Count)), MessageDialogStyle.AffirmativeAndNegative, settings); if (result != MessageDialogResult.Affirmative) { return; } } var previousVersion = _newDeck.Version; if (overwrite && (_newDeck.Version != newVersion)) { AddDeckHistory(); _newDeck.Version = newVersion; _newDeck.SelectedVersion = newVersion; _newDeck.HearthStatsDeckVersionId = ""; //UpdateDeckHistoryPanel(_newDeck, false); } if (EditingDeck && overwrite) { DeckList.Instance.Decks.Remove(_newDeck); //DeckPickerList.RemoveDeck(_newDeck); } var oldDeckName = _newDeck.Name; _newDeck.Name = deckName; var newDeckClone = (Deck)_newDeck.Clone(); newDeckClone.Archived = false; DeckList.Instance.Decks.Add(newDeckClone); newDeckClone.LastEdited = DateTime.Now; DeckList.Save(); Logger.WriteLine("Saved Decks", "SaveDeck"); if (EditingDeck) { TagControlEdit.SetSelectedTags(new List <string>()); if (deckName != oldDeckName) { var statsEntry = DeckStatsList.Instance.DeckStats.FirstOrDefault(ds => ds.BelongsToDeck(_newDeck)); if (statsEntry != null) { if (overwrite) { statsEntry.Name = deckName; Logger.WriteLine("Deck has new name, updated deckstats", "SaveDeck"); } else { var newStatsEntry = DeckStatsList.Instance.DeckStats.FirstOrDefault(ds => ds.BelongsToDeck(_newDeck)); if (newStatsEntry == null) { newStatsEntry = new DeckStats(_newDeck); DeckStatsList.Instance.DeckStats.Add(newStatsEntry); } foreach (var game in statsEntry.Games) { newStatsEntry.AddGameResult(game.CloneWithNewId()); } Logger.WriteLine("cloned gamestats for \"Set as new\"", "SaveDeck"); } DeckStatsList.Save(); } } } if (Config.Instance.HearthStatsAutoUploadNewDecks && HearthStatsAPI.IsLoggedIn) { Logger.WriteLine("auto uploading new/edited deck", "SaveDeck"); if (EditingDeck) { if (previousVersion != newVersion) { HearthStatsManager.UploadVersionAsync(newDeckClone, _originalDeck.HearthStatsIdForUploading, background: true); } else { HearthStatsManager.UpdateDeckAsync(newDeckClone, background: true); } } else { HearthStatsManager.UploadDeckAsync(newDeckClone, background: true); } } //after cloning the stats, otherwise new stats will be generated //DeckPickerList.AddAndSelectDeck(newDeckClone); EditingDeck = false; foreach (var tag in _newDeck.Tags) { SortFilterDecksFlyout.AddSelectedTag(tag); } DeckPickerList.SelectDeckAndAppropriateView(newDeckClone); CloseNewDeck(); ClearNewDeckSection(); }
public static async void SyncAsync(bool forceFullSync = false, bool background = false) { Logger.WriteLine(string.Format("starting sync process: forceFullSync={0}, background={1}", forceFullSync, background), "HearthStatsManager"); if (!HearthStatsAPI.IsLoggedIn) { Logger.WriteLine("error: not logged in", "HearthStatsManager"); return; } try { if (SyncInProgress) { Logger.WriteLine("error: sync already in progress", "HearthStatsManager"); return; } SyncInProgress = true; if (background) { AddBackgroundActivity(); } var controller = background ? null : await Core.MainWindow.ShowProgressAsync("Syncing...", "Checking HearthStats for new decks..."); Logger.WriteLine("Checking HearthStats for new decks...", "HearthStatsManager"); var localDecks = DeckList.Instance.Decks; var remoteDecks = await DownloadDecksAsync(forceFullSync); if (remoteDecks.Any()) { var newDecks = remoteDecks.Where(deck => localDecks.All(localDeck => localDeck.HearthStatsId != deck.HearthStatsId)).ToList(); if (newDecks.Any()) { Core.MainWindow.FlyoutHearthStatsDownload.IsOpen = true; if (!background) { await controller.CloseAsync(); } newDecks = await Core.MainWindow.HearthStatsDownloadDecksControl.LoadDecks(newDecks); foreach (var deck in newDecks) { DeckList.Instance.Decks.Add(deck); Logger.WriteLine("saved new deck " + deck, "HearthStatsManager"); } DeckList.Save(); Core.MainWindow.DeckPickerList.UpdateDecks(); Core.MainWindow.DeckPickerList.UpdateArchivedClassVisibility(); background = false; } if (!background) { if (controller == null || !controller.IsOpen) { controller = await Core.MainWindow.ShowProgressAsync("Syncing...", "Checking for new versions..."); } else { controller.SetMessage("Checking for new versions..."); } } Logger.WriteLine("Checking for new versions...", "HearthStatsManager"); var decksWithNewVersions = remoteDecks.Where( deck => localDecks.Any( localDeck => localDeck.HasHearthStatsId && deck.HearthStatsId == localDeck.HearthStatsId && localDeck.GetMaxVerion() != deck.GetMaxVerion())).ToList(); if (decksWithNewVersions.Any()) { foreach (var deck in decksWithNewVersions) { var currentDeck = localDecks.FirstOrDefault(d => d.HasHearthStatsId && d.HearthStatsId == deck.HearthStatsId); if (currentDeck == null) { continue; } var originalDeck = (Deck)currentDeck.Clone(); var versions = deck.VersionsIncludingSelf.Where(v => !currentDeck.VersionsIncludingSelf.Contains(v)) .OrderBy(v => v) .Select(v => deck.GetVersion(v)) .Where(v => v != null) .ToList(); if (versions.Any()) { foreach (var newVersion in versions) { var clone = (Deck)currentDeck.Clone(); currentDeck.Version = newVersion.Version; currentDeck.SelectedVersion = newVersion.Version; currentDeck.HearthStatsDeckVersionId = newVersion.HearthStatsDeckVersionId; currentDeck.Versions.Add(clone); } Logger.WriteLine( string.Format("saved {0} new versions ({1}) to {2}", versions.Count, versions.Select(v => v.Version.ToString()).Aggregate((c, n) => c + ", " + n), deck), "HearthStatsManager"); } } DeckList.Save(); Core.MainWindow.DeckPickerList.UpdateDecks(); Core.MainWindow.DeckPickerList.UpdateArchivedClassVisibility(); } if (!background) { if (controller == null || !controller.IsOpen) { controller = await Core.MainWindow.ShowProgressAsync("Syncing...", "Checking for edited decks..."); } else { controller.SetMessage("Checking for edited decks..."); } } Logger.WriteLine("Checking for edited decks...", "HearthStatsManager"); var editedDecks = remoteDecks.Where( r => localDecks.Any( l => l.HasHearthStatsId && l.HearthStatsId == r.HearthStatsId && r.LastEdited > l.LastEdited && (l.Name != r.Name || !(new HashSet <string>(l.Tags).SetEquals(r.Tags)) || l.Note != r.Note || (l - r).Count > 0))).ToList(); if (editedDecks.Any()) { foreach (var deck in editedDecks) { var localDeck = localDecks.FirstOrDefault(d => d.HasHearthStatsId && d.HearthStatsId == deck.HearthStatsId); if (localDeck != null) { //localDeck = (Deck)localDeck.Clone(); localDeck.Name = deck.Name; localDeck.Tags = deck.Tags; localDeck.Note = deck.Note; localDeck.Cards.Clear(); foreach (var card in deck.Cards) { localDeck.Cards.Add((Card)card.Clone()); } Logger.WriteLine("edited latest version of " + localDeck, "HearthStatsManager"); } } Core.MainWindow.DeckPickerList.UpdateDecks(); Core.MainWindow.DeckPickerList.UpdateArchivedClassVisibility(); DeckList.Save(); Logger.WriteLine("edited " + editedDecks.Count + " decks", "HearthStatsManager"); } } if (!background) { if (controller == null || !controller.IsOpen) { controller = await Core.MainWindow.ShowProgressAsync("Syncing...", "Checking HearthStats for new matches..."); } else { controller.SetMessage("Checking HearthStats for new matches..."); } } Logger.WriteLine("Checking HearthStats for new matches...", "HearthStatsManager"); var newGames = await DownloadGamesAsync(forceFullSync); if (newGames.Any()) { foreach (var game in newGames) { var deck = DeckList.Instance.Decks.FirstOrDefault( d => d.VersionsIncludingSelf.Select(v => d.GetVersion(v.Major, v.Minor)) .Where(v => v != null && v.HasHearthStatsDeckVersionId) .Any(v => game.HearthStatsDeckVersionId == v.HearthStatsDeckVersionId)); if (deck == null) { //deck_version_id seems to be null for older matches deck = DeckList.Instance.Decks.FirstOrDefault(d => d.HasHearthStatsId && game.HearthStatsDeckId == d.HearthStatsId); if (deck != null) { game.HearthStatsDeckVersionId = deck.HearthStatsDeckVersionId; } } if (deck == null) { Logger.WriteLine(string.Format("no deck found for match {0}", game), "HearthStatsManager"); continue; } if (deck.DeckStats.Games.Any(g => g.HearthStatsId == game.HearthStatsId)) { Logger.WriteLine(string.Format("deck {0} already has match {1}", deck, game), "HearthStatsManager"); continue; } var deckVersion = deck.VersionsIncludingSelf.Select(deck.GetVersion) .FirstOrDefault(v => v.HearthStatsDeckVersionId == game.HearthStatsDeckVersionId); if (deckVersion == null) { continue; } Logger.WriteLine(string.Format("added match {0} to version {1} of deck {2}", game, deck.Version.ShortVersionString, deck), "HearthStatsManager"); game.PlayerDeckVersion = deckVersion.Version; deck.DeckStats.AddGameResult(game); } DeckStatsList.Save(); Core.MainWindow.DeckPickerList.UpdateDecks(); Core.MainWindow.DeckPickerList.UpdateArchivedClassVisibility(); Core.MainWindow.DeckStatsFlyout.LoadOverallStats(); } if (!background) { controller.SetMessage("Checking for new local decks..."); } Logger.WriteLine("Checking for new local decks...", "HearthStatsManager"); var newLocalDecks = localDecks.Where(deck => !deck.HasHearthStatsId && deck.IsArenaDeck != true).ToList(); if (newLocalDecks.Any(d => d.SyncWithHearthStats != false)) { var uploaded = 0; var total = newLocalDecks.Count; Logger.WriteLine("found " + newLocalDecks.Count + " new decks", "HearthStatsManager"); if (!background) { await controller.CloseAsync(); } Core.MainWindow.FlyoutHearthStatsUpload.IsOpen = true; newLocalDecks = await Core.MainWindow.HearthStatsUploadDecksControl.LoadDecks(newLocalDecks); if (newLocalDecks.Any()) { controller = await Core.MainWindow.ShowProgressAsync("Syncing...", "Uploading " + newLocalDecks.Count + " new decks..."); Logger.WriteLine("Uploading " + newLocalDecks.Count + " new decks...", "HearthStatsManager"); await Task.Run(() => { Parallel.ForEach(newLocalDecks, deck => { UploadDeck(deck, false); if (controller != null) { Core.MainWindow.Dispatcher.BeginInvoke(new Action(() => { controller.SetProgress(1.0 * (++uploaded) / total); })); } }); }); DeckList.Save(); //save new ids background = false; } } if (!background) { if (controller == null || !controller.IsOpen) { controller = await Core.MainWindow.ShowProgressAsync("Syncing...", "Checking for new local versions..."); } else { controller.SetMessage("Checking for new local versions..."); } } Logger.WriteLine("Checking for new local versions...", "HearthStatsManager"); var localNewVersions = localDecks.Where(x => x.HasHearthStatsId) .SelectMany( x => x.VersionsIncludingSelf.Select(x.GetVersion) .Where(v => !v.HasHearthStatsDeckVersionId) .Select(v => new { version = v, hearthStatsId = x.HearthStatsIdForUploading })) .ToList(); if (localNewVersions.Any()) { var uploaded = 0; var total = localNewVersions.Count; if (!background) { controller.SetMessage("Uploading " + localNewVersions.Count + " new versions..."); } Logger.WriteLine("Uploading " + localNewVersions.Count + " new versions...", "HearthStatsManager"); //this can't happen in parallel (?) foreach (var v in localNewVersions) { var result = await UploadVersionAsync(v.version, v.hearthStatsId, false); if (!result.Success && result.Retry) { await Task.Delay(RetryDelay); await UploadVersionAsync(v.version, v.hearthStatsId, false); if (controller != null) { Core.MainWindow.Dispatcher.BeginInvoke(new Action(() => { controller.SetProgress(1.0 * (++uploaded) / total); })); } } } DeckList.Save(); } if (!background) { controller.SetMessage("Checking for edited local decks..."); } Logger.WriteLine("Checking for edited local decks...", "HearthStatsManager"); var editedLocalDecks = localDecks.Where( l => remoteDecks.Any( r => r.HasHearthStatsId && r.HearthStatsId == l.HearthStatsId && l.LastEdited > r.LastEdited && (r.Name != l.Name || !(new HashSet <string>(r.Tags).SetEquals(l.Tags)) || r.Note != l.Note || (r - l).Count > 0))).ToList(); if (editedLocalDecks.Any()) { if (!background) { controller.SetMessage("Updating " + editedLocalDecks.Count + " decks..."); } Logger.WriteLine("Updating " + editedLocalDecks.Count + " decks...", "HearthStatsManager"); foreach (var deck in editedLocalDecks) { await UpdateDeckAsync(deck); } Logger.WriteLine("updated " + editedLocalDecks.Count + " decks", "HearthStatsManager"); } if (!background) { controller.SetMessage("Checking for new local matches..."); } Logger.WriteLine("Checking for new local matches...", "HearthStatsManager"); var newMatches = DeckList.Instance.Decks.Where(d => d.SyncWithHearthStats == true && d.HasHearthStatsId) .SelectMany(d => d.DeckStats.Games.Where(g => !g.HasHearthStatsId).Select(g => new { deck = d, game = g })) .ToList(); if (newMatches.Any()) { var uploaded = 0; var total = newMatches.Count; if (!background) { controller.SetMessage("Uploading " + newMatches.Count + " new matches..."); } Logger.WriteLine("Uploading " + newMatches.Count + " new matches...", "HearthStatsManager"); await Task.Run(() => { var groupedMatchObs = newMatches.Select( match => new { match.game, deckVersion = (match.game.HasHearthStatsDeckVersionId ? (match.deck.VersionsIncludingSelf.Where(v => v != null) .Select(match.deck.GetVersion) .FirstOrDefault( d => d.HasHearthStatsDeckVersionId && d.HearthStatsDeckVersionId == match.game.HearthStatsDeckVersionId) ?? match.deck.GetVersion(match.game.PlayerDeckVersion)) : (match.game.PlayerDeckVersion != null ? match.deck.GetVersion(match.game.PlayerDeckVersion) : match.deck)) }).Where(x => x.deckVersion != null).GroupBy(x => x.deckVersion.HearthStatsDeckVersionId); Parallel.ForEach(groupedMatchObs, matches => { UploadMultipleMatches(matches.Select(m => m.game), matches.First().deckVersion, false); if (controller != null) { Core.MainWindow.Dispatcher.BeginInvoke( new Action( () => { controller.SetProgress(1.0 * (uploaded += matches.Count()) / total); })); } }); }); DeckStatsList.Save(); } Config.Instance.LastHearthStatsDecksSync = DateTime.Now.ToUnixTime() - 600; //10 minute overlap Config.Instance.LastHearthStatsGamesSync = DateTime.Now.ToUnixTime() - 600; Config.Save(); if (!background) { await controller.CloseAsync(); } RemoveBackgroundActivity(); SyncInProgress = false; Logger.WriteLine("finished sync process", "HearthStatsManager"); } catch (Exception e) { Logger.WriteLine("There was an error syncing with HearthStats:\n" + e, "HearthStatsManager"); SyncInProgress = false; } }
private async void BtnSaveDeck_Click(object sender, RoutedEventArgs e) { //NewDeck.Cards = // new ObservableCollection<Card>( // NewDeck.Cards.OrderBy(c => c.Cost).ThenByDescending(c => c.Type).ThenBy(c => c.Name).ToList()); //ListViewNewDeck.ItemsSource = NewDeck.Cards; var deckName = TextBoxDeckName.Text; if (EditingDeck) { var settings = new MetroDialogSettings { AffirmativeButtonText = "Overwrite", NegativeButtonText = "Save as new" }; var result = await this.ShowMessageAsync("Saving deck", "How do you wish to save the deck?", MessageDialogStyle.AffirmativeAndNegative, settings); if (result == MessageDialogResult.Affirmative) { SaveDeck(true); } else if (result == MessageDialogResult.Negative) { SaveDeck(false); } } else if (DeckList.DecksList.Any(d => d.Name == deckName)) { var settings = new MetroDialogSettings { AffirmativeButtonText = "Overwrite", NegativeButtonText = "Set new name" }; var result = await this.ShowMessageAsync("A deck with that name already exists", "Overwriting the deck can not be undone!", MessageDialogStyle.AffirmativeAndNegative, settings); if (result == MessageDialogResult.Affirmative) { Deck oldDeck; while ((oldDeck = DeckList.DecksList.FirstOrDefault(d => d.Name == deckName)) != null) { var deckStats = DeckStatsList.Instance.DeckStats.FirstOrDefault(ds => ds.Name == oldDeck.Name); if (deckStats != null) { foreach (var game in deckStats.Games) { game.DeleteGameFile(); } DeckStatsList.Instance.DeckStats.Remove(deckStats); DeckStatsList.Save(); Logger.WriteLine("Deleted deckstats for deck: " + oldDeck.Name); } DeckList.DecksList.Remove(oldDeck); DeckPickerList.RemoveDeck(oldDeck); } SaveDeck(true); } else if (result == MessageDialogResult.Negative) { SaveDeck(false); } } else { SaveDeck(false); } editedDeckName = string.Empty; }
public static async void SyncAsync(bool forceFullSync = false, bool background = false) { Logger.WriteLine(string.Format("starting sync process: forceFullSync={0}, background={1}", forceFullSync, background), "HearthStatsManager"); if (!HearthStatsAPI.IsLoggedIn) { Logger.WriteLine("error: not logged in", "HearthStatsManager"); return; } try { if (_syncInProgress) { Logger.WriteLine("error: sync already in progress", "HearthStatsManager"); return; } _syncInProgress = true; if (background) { AddBackgroundActivity(); } var controller = background ? null : await Helper.MainWindow.ShowProgressAsync("Syncing...", "Checking HearthStats for new decks..."); Logger.WriteLine("Checking HearthStats for new decks...", "HearthStatsManager"); var localDecks = DeckList.Instance.Decks; var remoteDecks = await DownloadDecksAsync(forceFullSync); if (remoteDecks.Any()) { var newDecks = remoteDecks.Where(deck => localDecks.All(localDeck => localDeck.HearthStatsId != deck.HearthStatsId)).ToList(); if (newDecks.Any()) { Helper.MainWindow.FlyoutHearthStatsDownload.IsOpen = true; if (!background) { await controller.CloseAsync(); } newDecks = await Helper.MainWindow.HearthStatsDownloadDecksControl.LoadDecks(newDecks); foreach (var deck in newDecks) { DeckList.Instance.Decks.Add(deck); //Helper.MainWindow.DeckPickerList.AddDeck(deck); Logger.WriteLine("saved new deck " + deck, "HearthStatsManager"); } DeckList.Save(); Helper.MainWindow.DeckPickerList.UpdateDecks(); background = false; } if (!background) { if (controller == null || !controller.IsOpen) { controller = await Helper.MainWindow.ShowProgressAsync("Syncing...", "Checking for new versions..."); } else { controller.SetMessage("Checking for new versions..."); } } Logger.WriteLine("Checking for new versions...", "HearthStatsManager"); var decksWithNewVersions = remoteDecks.Where( deck => localDecks.Any( localDeck => localDeck.HasHearthStatsId && deck.HearthStatsId == localDeck.HearthStatsId && localDeck.GetMaxVerion() != deck.GetMaxVerion())).ToList(); if (decksWithNewVersions.Any()) // TODO: TEST { foreach (var deck in decksWithNewVersions) { var currentDeck = localDecks.FirstOrDefault(d => d.HasHearthStatsId && d.HearthStatsId == deck.HearthStatsId); if (currentDeck == null) { continue; } var originalDeck = (Deck)currentDeck.Clone(); var versions = deck.VersionsIncludingSelf.Where(v => !currentDeck.VersionsIncludingSelf.Contains(v)) .OrderBy(v => v) .Select(v => deck.GetVersion(v)) .Where(v => v != null) .ToList(); if (versions.Any()) { foreach (var newVersion in versions) { var clone = (Deck)currentDeck.Clone(); currentDeck.Version = newVersion.Version; currentDeck.SelectedVersion = newVersion.Version; currentDeck.HearthStatsDeckVersionId = newVersion.HearthStatsDeckVersionId; currentDeck.Versions.Add(clone); } //Helper.MainWindow.DeckPickerList.RemoveDeck(originalDeck); //Helper.MainWindow.DeckPickerList.AddDeck(currentDeck); Logger.WriteLine( string.Format("saved {0} new versions ({1}) to {2}", versions.Count, versions.Select(v => v.Version.ToString()).Aggregate((c, n) => c + ", " + n), deck), "HearthStatsManager"); } } DeckList.Save(); Helper.MainWindow.DeckPickerList.UpdateDecks(); } if (!background) { if (controller == null || !controller.IsOpen) { controller = await Helper.MainWindow.ShowProgressAsync("Syncing...", "Checking for edited decks..."); } else { controller.SetMessage("Checking for edited decks..."); } } Logger.WriteLine("Checking for edited decks...", "HearthStatsManager"); var editedDecks = remoteDecks.Where( r => !localDecks.Any( l => l.HasHearthStatsId && l.HearthStatsId == r.HearthStatsId && (l.Name != r.Name || l.Tags != r.Tags || l.Note != r.Note || l.Cards.Any(lc => r.Cards.All(rc => !rc.EqualsWithCount(lc)))))).ToList(); if (editedDecks.Any()) { foreach (var deck in editedDecks) { var localDeck = localDecks.FirstOrDefault(d => d.HasHearthStatsId && d.HearthStatsId == deck.HearthStatsId); if (localDeck != null) { localDeck = (Deck)localDeck.Clone(); localDeck.Name = deck.Name; localDeck.Tags = deck.Tags; localDeck.Note = deck.Note; localDeck.Cards.Clear(); foreach (var card in deck.Cards) { localDeck.Cards.Add((Card)card.Clone()); } Logger.WriteLine("edited latest version of " + localDeck, "HearthStatsManager"); } } Helper.MainWindow.DeckPickerList.UpdateDecks(); DeckList.Save(); Logger.WriteLine("edited " + editedDecks.Count + " decks", "HearthStatsManager"); } } if (!background) { if (controller == null || !controller.IsOpen) { controller = await Helper.MainWindow.ShowProgressAsync("Syncing...", "Checking HearthStats for new matches..."); } else { controller.SetMessage("Checking HearthStats for new matches..."); } } Logger.WriteLine("Checking HearthStats for new matches...", "HearthStatsManager"); var newGames = await DownloadGamesAsync(forceFullSync); if (newGames.Any()) { foreach (var game in newGames) { var deck = DeckList.Instance.Decks.FirstOrDefault( d => d.VersionsIncludingSelf.Select(v => d.GetVersion(v.Major, v.Minor)) .Where(v => v != null) .Any(v => game.BelongsToDeckVerion(v))); if (deck == null) { Logger.WriteLine(string.Format("no deck found for match {0}", game), "HearthStatsManager"); continue; } if (deck.DeckStats.Games.Any(g => g.HearthStatsId == game.HearthStatsId)) { Logger.WriteLine(string.Format("deck {0} already has match {1}", deck, game), "HearthStatsManager"); continue; } Logger.WriteLine(string.Format("added match {0} to version {1} of deck {2}", game, deck.Version.ShortVersionString, deck), "HearthStatsManager"); deck.DeckStats.AddGameResult(game); } DeckStatsList.Save(); Helper.MainWindow.DeckPickerList.UpdateDecks(); Helper.MainWindow.DeckStatsFlyout.LoadOverallStats(); } if (!background) { controller.SetMessage("Checking for new local decks..."); } Logger.WriteLine("Checking for new local decks...", "HearthStatsManager"); var newLocalDecks = localDecks.Where(deck => !deck.HasHearthStatsId && deck.IsArenaDeck != true).ToList(); if (newLocalDecks.Any(d => d.SyncWithHearthStats != false)) { Logger.WriteLine("found " + newLocalDecks.Count + " new decks", "HearthStatsManager"); if (!background) { await controller.CloseAsync(); } Helper.MainWindow.FlyoutHearthStatsUpload.IsOpen = true; newLocalDecks = await Helper.MainWindow.HearthStatsUploadDecksControl.LoadDecks(newLocalDecks); if (newLocalDecks.Any()) { controller = await Helper.MainWindow.ShowProgressAsync("Syncing...", "Uploading " + newLocalDecks.Count + " new decks..."); Logger.WriteLine("Uploading " + newLocalDecks.Count + " new decks...", "HearthStatsManager"); await Task.Run(() => { Parallel.ForEach(newLocalDecks, deck => UploadDeck(deck, false)); }); DeckList.Save(); //save new ids background = false; } } if (!background) { if (controller == null || !controller.IsOpen) { controller = await Helper.MainWindow.ShowProgressAsync("Syncing...", "Checking for new local versions..."); } else { controller.SetMessage("Checking for new local versions..."); } } Logger.WriteLine("Checking for new local versions...", "HearthStatsManager"); var localNewVersions = localDecks.Where(x => x.HasHearthStatsId) .SelectMany( x => x.Versions.Where(v => !v.HasHearthStatsDeckVersionId) .Select(v => new { version = v, hearthStatsId = x.HearthStatsId })) .ToList(); if (localNewVersions.Any()) { if (!background) { controller.SetMessage("Uploading " + localNewVersions.Count + " new versions..."); } Logger.WriteLine("Uploading " + localNewVersions.Count + " new versions...", "HearthStatsManager"); //this can't happen in parallel (?) foreach (var v in localNewVersions) { var result = await UploadVersionAsync(v.version, v.hearthStatsId, false); if (!result.Success && result.Retry) { await Task.Delay(RetryDelay); await UploadVersionAsync(v.version, v.hearthStatsId, false); } } DeckList.Save(); } if (!background) { controller.SetMessage("Checking for new local matches..."); } Logger.WriteLine("Checking for new local matches...", "HearthStatsManager"); var newMatches = DeckList.Instance.Decks.Where(d => d.SyncWithHearthStats == true && d.HasHearthStatsId) .SelectMany(d => d.DeckStats.Games.Where(g => !g.HasHearthStatsId).Select(g => new { deck = d, game = g })) .ToList(); if (newMatches.Any()) { if (!background) { controller.SetMessage("Uploading " + newMatches.Count + " new matches..."); } Logger.WriteLine("Uploading " + newMatches.Count + " new matches...", "HearthStatsManager"); await Task.Run(() => { Parallel.ForEach(newMatches, match => UploadMatch(match.game, match.deck, false)); }); DeckStatsList.Save(); } Config.Instance.LastHearthStatsDecksSync = DateTime.Now.ToUnixTime() - 600; //10 minute overlap Config.Instance.LastHearthStatsGamesSync = DateTime.Now.ToUnixTime() - 600; Config.Save(); if (!background) { await controller.CloseAsync(); } RemoveBackgroundActivity(); _syncInProgress = false; Logger.WriteLine("finished sync process", "HearthStatsManager"); } catch (Exception e) { Logger.WriteLine("There was an error syncing with HearthStats:\n" + e, "HearthStatsManager"); _syncInProgress = false; } }