From 176479387c400f57cf1f9e87e783161787998627 Mon Sep 17 00:00:00 2001 From: UrbanCMC Date: Fri, 28 Jan 2022 15:50:40 +0100 Subject: [PATCH] Added support for retrieving game achievements --- GoldbergGUI.Core/Services/GoldbergService.cs | 79 ++++++++++++++++++++ GoldbergGUI.Core/Services/SteamService.cs | 28 +++++++ GoldbergGUI.Core/ViewModels/MainViewModel.cs | 30 ++++++++ GoldbergGUI.WPF/Views/MainView.xaml | 12 ++- 4 files changed, 147 insertions(+), 2 deletions(-) diff --git a/GoldbergGUI.Core/Services/GoldbergService.cs b/GoldbergGUI.Core/Services/GoldbergService.cs index bbe0be1..6b7bcfa 100644 --- a/GoldbergGUI.Core/Services/GoldbergService.cs +++ b/GoldbergGUI.Core/Services/GoldbergService.cs @@ -208,6 +208,7 @@ namespace GoldbergGUI.Core.Services { _log.Info("Reading configuration..."); var appId = -1; + var achievementList = new List(); var dlcList = new List(); var steamAppidTxt = Path.Combine(path, "steam_appid.txt"); if (File.Exists(steamAppidTxt)) @@ -221,6 +222,19 @@ namespace GoldbergGUI.Core.Services _log.Info(@"""steam_appid.txt"" missing! Skipping..."); } + var achievementJson = Path.Combine(path, "steam_settings", "achievements.json"); + if (File.Exists(achievementJson)) + { + _log.Info("Getting achievements..."); + var json = await File.ReadAllTextAsync(achievementJson) + .ConfigureAwait(false); + achievementList = System.Text.Json.JsonSerializer.Deserialize>(json); + } + else + { + _log.Info(@"""steam_settings/achievements.json"" missing! Skipping..."); + } + var dlcTxt = Path.Combine(path, "steam_settings", "DLC.txt"); var appPathTxt = Path.Combine(path, "steam_settings", "app_paths.txt"); if (File.Exists(dlcTxt)) @@ -263,6 +277,7 @@ namespace GoldbergGUI.Core.Services return new GoldbergConfiguration { AppId = appId, + Achievements = achievementList, DlcList = dlcList, Offline = File.Exists(Path.Combine(path, "steam_settings", "offline.txt")), DisableNetworking = File.Exists(Path.Combine(path, "steam_settings", "disable_networking.txt")), @@ -303,6 +318,53 @@ namespace GoldbergGUI.Core.Services await File.WriteAllTextAsync(Path.Combine(path, "steam_appid.txt"), c.AppId.ToString()) .ConfigureAwait(false); + // Achievements + Images + if (c.Achievements.Count > 0) + { + _log.Info("Downloading images..."); + var imagePath = Path.Combine(path, "steam_settings", "images"); + Directory.CreateDirectory(imagePath); + + foreach (var achievement in c.Achievements) + { + await DownloadImageAsync(imagePath, achievement.Icon); + await DownloadImageAsync(imagePath, achievement.IconGray); + + // Update achievement list to point to local images instead + achievement.Icon = $"images/{Path.GetFileName(achievement.Icon)}"; + achievement.IconGray = $"images/{Path.GetFileName(achievement.IconGray)}"; + } + + _log.Info("Saving achievements..."); + + var achievementJson = System.Text.Json.JsonSerializer.Serialize( + c.Achievements, + new System.Text.Json.JsonSerializerOptions + { + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + WriteIndented = true + }); + await File.WriteAllTextAsync(Path.Combine(path, "steam_settings", "achievements.json"), achievementJson) + .ConfigureAwait(false); + + _log.Info("Finished saving achievements."); + } + else + { + _log.Info("No achievements set! Removing achievement files..."); + var imagePath = Path.Combine(path, "steam_settings", "images"); + if (Directory.Exists(imagePath)) + { + Directory.Delete(imagePath); + } + var achievementPath = Path.Combine(path, "steam_settings", "achievements"); + if (File.Exists(achievementPath)) + { + File.Delete(achievementPath); + } + _log.Info("Removed achievement files."); + } + // DLC + App path if (c.DlcList.Count > 0) { @@ -621,5 +683,22 @@ namespace GoldbergGUI.Core.Services return success; } + + private async Task DownloadImageAsync(string imageFolder, string imageUrl) + { + var fileName = Path.GetFileName(imageUrl); + var targetPath = Path.Combine(imageFolder, fileName); + if (File.Exists(targetPath)) + { + return; + } + else if (imageUrl.StartsWith("images/")) + { + _log.Warn($"Previously downloaded image '{imageUrl}' is now missing!"); + } + + var wc = new System.Net.WebClient(); + await wc.DownloadFileTaskAsync(new Uri(imageUrl, UriKind.Absolute), targetPath); + } } } \ No newline at end of file diff --git a/GoldbergGUI.Core/Services/SteamService.cs b/GoldbergGUI.Core/Services/SteamService.cs index 5e79f4d..74c369b 100644 --- a/GoldbergGUI.Core/Services/SteamService.cs +++ b/GoldbergGUI.Core/Services/SteamService.cs @@ -23,6 +23,7 @@ namespace GoldbergGUI.Core.Services public Task> GetListOfAppsByName(string name); public Task GetAppByName(string name); public Task GetAppById(int appid); + public Task> GetListOfAchievements(SteamApp steamApp); public Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb); } @@ -81,6 +82,7 @@ namespace GoldbergGUI.Core.Services private const string AppTypeGame = "game"; private const string AppTypeDlc = "dlc"; private const string Database = "steamapps.cache"; + private const string GameSchemaUrl = "https://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v2/"; private IMvxLog _log; @@ -168,6 +170,32 @@ namespace GoldbergGUI.Core.Services return app; } + public async Task> GetListOfAchievements(SteamApp steamApp) + { + var achievementList = new List(); + if (steamApp == null) + { + return achievementList; + } + + _log.Info($"Getting achievements for App {steamApp}"); + + var client = new HttpClient(); + client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent); + var apiUrl = $"{GameSchemaUrl}?key={Secrets.SteamWebApiKey()}&appid={steamApp.AppId}&l=en"; + + var response = await client.GetAsync(apiUrl); + var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + var jsonResponse = JsonDocument.Parse(responseBody); + var achievementData = jsonResponse.RootElement.GetProperty("game") + .GetProperty("availableGameStats") + .GetProperty("achievements"); + + achievementList = JsonSerializer.Deserialize>(achievementData.GetRawText()); + return achievementList; + } + public async Task> GetListOfDlc(SteamApp steamApp, bool useSteamDb) { var dlcList = new List(); diff --git a/GoldbergGUI.Core/ViewModels/MainViewModel.cs b/GoldbergGUI.Core/ViewModels/MainViewModel.cs index 6f57aae..7eb9993 100644 --- a/GoldbergGUI.Core/ViewModels/MainViewModel.cs +++ b/GoldbergGUI.Core/ViewModels/MainViewModel.cs @@ -30,6 +30,7 @@ namespace GoldbergGUI.Core.ViewModels private int _appId; //private SteamApp _currentGame; + private List achievements; private ObservableCollection _dlcs; private string _accountName; private long _steamId; @@ -368,6 +369,32 @@ namespace GoldbergGUI.Core.ViewModels if (steamApp != null) GameName = steamApp.Name; } + public IMvxCommand GetListOfAchievementsCommand => new MvxAsyncCommand(GetListOfAchievements); + + private async Task GetListOfAchievements() + { + if (AppId <= 0) + { + _log.Error("Invalid Steam App!"); + return; + } + + MainWindowEnabled = false; + StatusText = "Trying to get list of achievements..."; + achievements = await _steam.GetListOfAchievements(new SteamApp { AppId = AppId, Name = GameName }); + MainWindowEnabled = true; + + if (achievements.Count > 0) + { + var empty = achievements.Count == 1 ? "" : "s"; + StatusText = $"Successfully got {achievements.Count} achievement{empty}! Ready."; + } + else + { + StatusText = "No achievements found! Ready."; + } + } + public IMvxCommand GetListOfDlcCommand => new MvxAsyncCommand(GetListOfDlc); private async Task GetListOfDlc() @@ -416,6 +443,7 @@ namespace GoldbergGUI.Core.ViewModels await _goldberg.Save(dirPath, new GoldbergConfiguration { AppId = AppId, + Achievements = achievements.ToList(), DlcList = DLCs.ToList(), Offline = Offline, DisableNetworking = DisableNetworking, @@ -526,6 +554,7 @@ namespace GoldbergGUI.Core.ViewModels DllPath = "Path to game's steam_api(64).dll..."; GameName = "Game name..."; AppId = -1; + achievements = new List(); DLCs = new ObservableCollection(); AccountName = "Account name..."; SteamId = -1; @@ -546,6 +575,7 @@ namespace GoldbergGUI.Core.ViewModels private void SetFormFromConfig(GoldbergConfiguration config) { AppId = config.AppId; + achievements = new List(config.Achievements); DLCs = new ObservableCollection(config.DlcList); Offline = config.Offline; DisableNetworking = config.DisableNetworking; diff --git a/GoldbergGUI.WPF/Views/MainView.xaml b/GoldbergGUI.WPF/Views/MainView.xaml index 818b49c..13b5ccb 100644 --- a/GoldbergGUI.WPF/Views/MainView.xaml +++ b/GoldbergGUI.WPF/Views/MainView.xaml @@ -61,8 +61,16 @@ - -