From 509033c8c93f223c06e02314056d5ee1bf993594 Mon Sep 17 00:00:00 2001 From: pointfeev Date: Tue, 8 Mar 2022 21:31:22 -0500 Subject: [PATCH] v3.2.2.2 - Added a per-game 10 second cooldown and per-DLC 60 second cooldown between store queries to help prevent steam from thinking they're getting DDoSed (lol) - Optimized selection form node updating --- CreamInstaller/CreamInstaller.csproj | 2 +- CreamInstaller/SelectForm.cs | 38 ++++++++++++++++---------- CreamInstaller/Steam/SteamStore.cs | 40 +++++++++++++++++----------- 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index a3fc2dc..417c9ca 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -5,7 +5,7 @@ True Resources\ini.ico true - 3.2.2.1 + 3.2.2.2 Resources\ini.ico LICENSE 2021, pointfeev (https://github.com/pointfeev) diff --git a/CreamInstaller/SelectForm.cs b/CreamInstaller/SelectForm.cs index 86974b6..2ee6d97 100644 --- a/CreamInstaller/SelectForm.cs +++ b/CreamInstaller/SelectForm.cs @@ -96,6 +96,7 @@ internal partial class SelectForm : CustomForm progress.Report(++CompleteGameCount); } if (Program.Canceled) return; + List treeNodes = TreeNodes; RemainingGames.Clear(); // for display purposes only, otherwise ignorable RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable List appTasks = new(); @@ -112,7 +113,7 @@ internal partial class SelectForm : CustomForm selection.DllDirectories = steamDllDirectories ?? await EpicLibrary.GetDllDirectoriesFromGameDirectory(selection.RootDirectory); selection.IsSteam = steamDllDirectories is not null; - TreeNode programNode = TreeNodes.Find(s => s.Name == selection.Id) ?? new(); + TreeNode programNode = treeNodes.Find(s => s.Name == selection.Id) ?? new(); programNode.Name = selection.Id; programNode.Text = selection.Name; programNode.Checked = selection.Enabled; @@ -129,6 +130,7 @@ internal partial class SelectForm : CustomForm string branch = program.Item3; int buildId = program.Item4; string directory = program.Item5; + Thread.Sleep(0); ProgramSelection selection = ProgramSelection.FromId(appId); if (Program.Canceled) return; if (Program.IsGameBlocked(name, directory)) continue; @@ -136,14 +138,15 @@ internal partial class SelectForm : CustomForm Task task = Task.Run(async () => { if (Program.Canceled) return; + Thread.Sleep(0); List dllDirectories = await SteamLibrary.GetDllDirectoriesFromGameDirectory(directory); if (dllDirectories is null) { RemoveFromRemainingGames(name); return; } - AppData appData = await SteamStore.QueryStoreAPI(appId); - VProperty appInfo = appInfo = await SteamCMD.GetAppInfo(appId, branch, buildId); + AppData appData = await SteamStore.QueryStoreAPI(appId, 60); + VProperty appInfo = await SteamCMD.GetAppInfo(appId, branch, buildId); if (appData is null && appInfo is null) { RemoveFromRemainingGames(name); @@ -164,6 +167,7 @@ internal partial class SelectForm : CustomForm Task task = Task.Run(async () => { if (Program.Canceled) return; + Thread.Sleep(0); string dlcName = null; string dlcIcon = null; AppData dlcAppData = await SteamStore.QueryStoreAPI(dlcAppId); @@ -223,7 +227,8 @@ internal partial class SelectForm : CustomForm Program.Invoke(selectionTreeView, delegate { if (Program.Canceled) return; - TreeNode programNode = TreeNodes.Find(s => s.Name == appId) ?? new(); + Thread.Sleep(0); + TreeNode programNode = treeNodes.Find(s => s.Name == appId) ?? new(); programNode.Name = appId; programNode.Text = appData?.name ?? name; programNode.Checked = selection.Enabled; @@ -232,11 +237,12 @@ internal partial class SelectForm : CustomForm foreach (KeyValuePair pair in dlc) { if (Program.Canceled || programNode is null) return; + Thread.Sleep(0); string appId = pair.Key; (DlcType type, string name, string icon) dlcApp = pair.Value; selection.AllDlc[appId] = dlcApp; if (allCheckBox.Checked) selection.SelectedDlc[appId] = dlcApp; - TreeNode dlcNode = TreeNodes.Find(s => s.Name == appId) ?? new(); + TreeNode dlcNode = treeNodes.Find(s => s.Name == appId) ?? new(); dlcNode.Name = appId; dlcNode.Text = dlcApp.name; dlcNode.Checked = selection.SelectedDlc.ContainsKey(appId); @@ -258,6 +264,7 @@ internal partial class SelectForm : CustomForm string @namespace = manifest.CatalogNamespace; string name = manifest.DisplayName; string directory = manifest.InstallLocation; + Thread.Sleep(0); ProgramSelection selection = ProgramSelection.FromId(@namespace); if (Program.Canceled) return; if (Program.IsGameBlocked(name, directory)) continue; @@ -265,6 +272,7 @@ internal partial class SelectForm : CustomForm Task task = Task.Run(async () => { if (Program.Canceled) return; + Thread.Sleep(0); List dllDirectories = await EpicLibrary.GetDllDirectoriesFromGameDirectory(directory); if (dllDirectories is null) { @@ -280,6 +288,7 @@ internal partial class SelectForm : CustomForm foreach ((string id, string name, string product, string icon, string developer) in entitlementIds) { if (Program.Canceled) return; + Thread.Sleep(0); AddToRemainingDLCs(id); Task task = Task.Run(() => { @@ -311,24 +320,28 @@ internal partial class SelectForm : CustomForm selection.RootDirectory = directory; selection.DllDirectories = dllDirectories; foreach (KeyValuePair pair in entitlements) + { + Thread.Sleep(0); if (pair.Value.name == selection.Name) { selection.ProductUrl = "https://www.epicgames.com/store/product/" + pair.Value.product; selection.IconUrl = pair.Value.icon; selection.Publisher = pair.Value.developer; } + } if (Program.Canceled) return; Program.Invoke(selectionTreeView, delegate { if (Program.Canceled) return; - TreeNode programNode = TreeNodes.Find(s => s.Name == @namespace) ?? new(); + Thread.Sleep(0); + TreeNode programNode = treeNodes.Find(s => s.Name == @namespace) ?? new(); programNode.Name = @namespace; programNode.Text = name; programNode.Checked = selection.Enabled; programNode.Remove(); selectionTreeView.Nodes.Add(programNode); - /*TreeNode catalogItemsNode = TreeNodes.Find(s => s.Name == @namespace + "_catalogItems") ?? new(); + /*TreeNode catalogItemsNode = treeNodes.Find(s => s.Name == @namespace + "_catalogItems") ?? new(); catalogItemsNode.Name = @namespace + "_catalogItems"; catalogItemsNode.Text = "Catalog Items"; catalogItemsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.CatalogItem); @@ -336,7 +349,7 @@ internal partial class SelectForm : CustomForm programNode.Nodes.Add(catalogItemsNode);*/ if (entitlements.Any()) { - /*TreeNode entitlementsNode = TreeNodes.Find(s => s.Name == @namespace + "_entitlements") ?? new(); + /*TreeNode entitlementsNode = treeNodes.Find(s => s.Name == @namespace + "_entitlements") ?? new(); entitlementsNode.Name = @namespace + "_entitlements"; entitlementsNode.Text = "Entitlements"; entitlementsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.Entitlement); @@ -345,11 +358,12 @@ internal partial class SelectForm : CustomForm foreach (KeyValuePair pair in entitlements) { if (Program.Canceled || programNode is null/* || entitlementsNode is null*/) return; + Thread.Sleep(0); string dlcId = pair.Key; (DlcType type, string name, string icon) dlcApp = (DlcType.Entitlement, pair.Value.name, pair.Value.icon); selection.AllDlc[dlcId] = dlcApp; if (allCheckBox.Checked) selection.SelectedDlc[dlcId] = dlcApp; - TreeNode dlcNode = TreeNodes.Find(s => s.Name == dlcId) ?? new(); + TreeNode dlcNode = treeNodes.Find(s => s.Name == dlcId) ?? new(); dlcNode.Name = dlcId; dlcNode.Text = dlcApp.name; dlcNode.Checked = selection.SelectedDlc.ContainsKey(dlcId); @@ -413,11 +427,7 @@ internal partial class SelectForm : CustomForm setup = false; progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . "; ProgramSelection.ValidateAll(); - TreeNodes.ForEach(node => - { - if (node.Parent is null && ProgramSelection.FromId(node.Name) is null) - node.Remove(); - }); + TreeNodes.ForEach(node => node.Remove()); await GetApplicablePrograms(iProgress); await SteamCMD.Cleanup(); diff --git a/CreamInstaller/Steam/SteamStore.cs b/CreamInstaller/Steam/SteamStore.cs index 572dfab..987d16a 100644 --- a/CreamInstaller/Steam/SteamStore.cs +++ b/CreamInstaller/Steam/SteamStore.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Drawing; using System.IO; +using System.Threading; using System.Threading.Tasks; using CreamInstaller.Utility; @@ -22,30 +24,38 @@ internal static class SteamStore return dlcIds; }); - internal static async Task QueryStoreAPI(string appId) + private static readonly ConcurrentDictionary lastQueries = new(); + internal static async Task QueryStoreAPI(string appId, int cooldown = 10) { if (Program.Canceled) return null; - string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}"); + Thread.Sleep(0); string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json"; - if (response is not null) + DateTime now = DateTime.UtcNow; + if (!lastQueries.TryGetValue(appId, out DateTime lastQuery) || (now - lastQuery).TotalSeconds > cooldown) { - IDictionary apps = (dynamic)JsonConvert.DeserializeObject(response); - foreach (KeyValuePair app in apps) + lastQueries[appId] = now; + string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}"); + if (response is not null) { - try + IDictionary apps = (dynamic)JsonConvert.DeserializeObject(response); + foreach (KeyValuePair app in apps) { - AppData data = JsonConvert.DeserializeObject(app.Value.ToString()).data; + Thread.Sleep(0); try { - File.WriteAllText(cacheFile, JsonConvert.SerializeObject(data, Formatting.Indented)); + AppData data = JsonConvert.DeserializeObject(app.Value.ToString()).data; + try + { + File.WriteAllText(cacheFile, JsonConvert.SerializeObject(data, Formatting.Indented)); + } + catch { } + return data; + } + catch (Exception e) + { + using DialogForm dialogForm = new(null); + dialogForm.Show(SystemIcons.Error, "Unsuccessful deserialization of query for appid " + appId + ":\n\n" + e.ToString(), "FUCK"); } - catch { } - return data; - } - catch (Exception e) - { - using DialogForm dialogForm = new(null); - dialogForm.Show(SystemIcons.Error, "Unsuccessful deserialization of query for appid " + appId + ":\n\n" + e.ToString(), "FUCK"); } } }