- 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
This commit is contained in:
pointfeev 2022-03-08 21:31:22 -05:00
parent 477e7b4445
commit 509033c8c9
3 changed files with 50 additions and 30 deletions

View file

@ -5,7 +5,7 @@
<UseWindowsForms>True</UseWindowsForms> <UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>Resources\ini.ico</ApplicationIcon> <ApplicationIcon>Resources\ini.ico</ApplicationIcon>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract> <IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<Version>3.2.2.1</Version> <Version>3.2.2.2</Version>
<PackageIcon>Resources\ini.ico</PackageIcon> <PackageIcon>Resources\ini.ico</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright> <Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>

View file

@ -96,6 +96,7 @@ internal partial class SelectForm : CustomForm
progress.Report(++CompleteGameCount); progress.Report(++CompleteGameCount);
} }
if (Program.Canceled) return; if (Program.Canceled) return;
List<TreeNode> treeNodes = TreeNodes;
RemainingGames.Clear(); // for display purposes only, otherwise ignorable RemainingGames.Clear(); // for display purposes only, otherwise ignorable
RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable
List<Task> appTasks = new(); List<Task> appTasks = new();
@ -112,7 +113,7 @@ internal partial class SelectForm : CustomForm
selection.DllDirectories = steamDllDirectories ?? await EpicLibrary.GetDllDirectoriesFromGameDirectory(selection.RootDirectory); selection.DllDirectories = steamDllDirectories ?? await EpicLibrary.GetDllDirectoriesFromGameDirectory(selection.RootDirectory);
selection.IsSteam = steamDllDirectories is not null; 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.Name = selection.Id;
programNode.Text = selection.Name; programNode.Text = selection.Name;
programNode.Checked = selection.Enabled; programNode.Checked = selection.Enabled;
@ -129,6 +130,7 @@ internal partial class SelectForm : CustomForm
string branch = program.Item3; string branch = program.Item3;
int buildId = program.Item4; int buildId = program.Item4;
string directory = program.Item5; string directory = program.Item5;
Thread.Sleep(0);
ProgramSelection selection = ProgramSelection.FromId(appId); ProgramSelection selection = ProgramSelection.FromId(appId);
if (Program.Canceled) return; if (Program.Canceled) return;
if (Program.IsGameBlocked(name, directory)) continue; if (Program.IsGameBlocked(name, directory)) continue;
@ -136,14 +138,15 @@ internal partial class SelectForm : CustomForm
Task task = Task.Run(async () => Task task = Task.Run(async () =>
{ {
if (Program.Canceled) return; if (Program.Canceled) return;
Thread.Sleep(0);
List<string> dllDirectories = await SteamLibrary.GetDllDirectoriesFromGameDirectory(directory); List<string> dllDirectories = await SteamLibrary.GetDllDirectoriesFromGameDirectory(directory);
if (dllDirectories is null) if (dllDirectories is null)
{ {
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
return; return;
} }
AppData appData = await SteamStore.QueryStoreAPI(appId); AppData appData = await SteamStore.QueryStoreAPI(appId, 60);
VProperty appInfo = appInfo = await SteamCMD.GetAppInfo(appId, branch, buildId); VProperty appInfo = await SteamCMD.GetAppInfo(appId, branch, buildId);
if (appData is null && appInfo is null) if (appData is null && appInfo is null)
{ {
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
@ -164,6 +167,7 @@ internal partial class SelectForm : CustomForm
Task task = Task.Run(async () => Task task = Task.Run(async () =>
{ {
if (Program.Canceled) return; if (Program.Canceled) return;
Thread.Sleep(0);
string dlcName = null; string dlcName = null;
string dlcIcon = null; string dlcIcon = null;
AppData dlcAppData = await SteamStore.QueryStoreAPI(dlcAppId); AppData dlcAppData = await SteamStore.QueryStoreAPI(dlcAppId);
@ -223,7 +227,8 @@ internal partial class SelectForm : CustomForm
Program.Invoke(selectionTreeView, delegate Program.Invoke(selectionTreeView, delegate
{ {
if (Program.Canceled) return; 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.Name = appId;
programNode.Text = appData?.name ?? name; programNode.Text = appData?.name ?? name;
programNode.Checked = selection.Enabled; programNode.Checked = selection.Enabled;
@ -232,11 +237,12 @@ internal partial class SelectForm : CustomForm
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in dlc) foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in dlc)
{ {
if (Program.Canceled || programNode is null) return; if (Program.Canceled || programNode is null) return;
Thread.Sleep(0);
string appId = pair.Key; string appId = pair.Key;
(DlcType type, string name, string icon) dlcApp = pair.Value; (DlcType type, string name, string icon) dlcApp = pair.Value;
selection.AllDlc[appId] = dlcApp; selection.AllDlc[appId] = dlcApp;
if (allCheckBox.Checked) selection.SelectedDlc[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.Name = appId;
dlcNode.Text = dlcApp.name; dlcNode.Text = dlcApp.name;
dlcNode.Checked = selection.SelectedDlc.ContainsKey(appId); dlcNode.Checked = selection.SelectedDlc.ContainsKey(appId);
@ -258,6 +264,7 @@ internal partial class SelectForm : CustomForm
string @namespace = manifest.CatalogNamespace; string @namespace = manifest.CatalogNamespace;
string name = manifest.DisplayName; string name = manifest.DisplayName;
string directory = manifest.InstallLocation; string directory = manifest.InstallLocation;
Thread.Sleep(0);
ProgramSelection selection = ProgramSelection.FromId(@namespace); ProgramSelection selection = ProgramSelection.FromId(@namespace);
if (Program.Canceled) return; if (Program.Canceled) return;
if (Program.IsGameBlocked(name, directory)) continue; if (Program.IsGameBlocked(name, directory)) continue;
@ -265,6 +272,7 @@ internal partial class SelectForm : CustomForm
Task task = Task.Run(async () => Task task = Task.Run(async () =>
{ {
if (Program.Canceled) return; if (Program.Canceled) return;
Thread.Sleep(0);
List<string> dllDirectories = await EpicLibrary.GetDllDirectoriesFromGameDirectory(directory); List<string> dllDirectories = await EpicLibrary.GetDllDirectoriesFromGameDirectory(directory);
if (dllDirectories is null) 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) foreach ((string id, string name, string product, string icon, string developer) in entitlementIds)
{ {
if (Program.Canceled) return; if (Program.Canceled) return;
Thread.Sleep(0);
AddToRemainingDLCs(id); AddToRemainingDLCs(id);
Task task = Task.Run(() => Task task = Task.Run(() =>
{ {
@ -311,24 +320,28 @@ internal partial class SelectForm : CustomForm
selection.RootDirectory = directory; selection.RootDirectory = directory;
selection.DllDirectories = dllDirectories; selection.DllDirectories = dllDirectories;
foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in entitlements) foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in entitlements)
{
Thread.Sleep(0);
if (pair.Value.name == selection.Name) if (pair.Value.name == selection.Name)
{ {
selection.ProductUrl = "https://www.epicgames.com/store/product/" + pair.Value.product; selection.ProductUrl = "https://www.epicgames.com/store/product/" + pair.Value.product;
selection.IconUrl = pair.Value.icon; selection.IconUrl = pair.Value.icon;
selection.Publisher = pair.Value.developer; selection.Publisher = pair.Value.developer;
} }
}
if (Program.Canceled) return; if (Program.Canceled) return;
Program.Invoke(selectionTreeView, delegate Program.Invoke(selectionTreeView, delegate
{ {
if (Program.Canceled) return; 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.Name = @namespace;
programNode.Text = name; programNode.Text = name;
programNode.Checked = selection.Enabled; programNode.Checked = selection.Enabled;
programNode.Remove(); programNode.Remove();
selectionTreeView.Nodes.Add(programNode); 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.Name = @namespace + "_catalogItems";
catalogItemsNode.Text = "Catalog Items"; catalogItemsNode.Text = "Catalog Items";
catalogItemsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.CatalogItem); catalogItemsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.CatalogItem);
@ -336,7 +349,7 @@ internal partial class SelectForm : CustomForm
programNode.Nodes.Add(catalogItemsNode);*/ programNode.Nodes.Add(catalogItemsNode);*/
if (entitlements.Any()) 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.Name = @namespace + "_entitlements";
entitlementsNode.Text = "Entitlements"; entitlementsNode.Text = "Entitlements";
entitlementsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.Entitlement); entitlementsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.Entitlement);
@ -345,11 +358,12 @@ internal partial class SelectForm : CustomForm
foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in entitlements) foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in entitlements)
{ {
if (Program.Canceled || programNode is null/* || entitlementsNode is null*/) return; if (Program.Canceled || programNode is null/* || entitlementsNode is null*/) return;
Thread.Sleep(0);
string dlcId = pair.Key; string dlcId = pair.Key;
(DlcType type, string name, string icon) dlcApp = (DlcType.Entitlement, pair.Value.name, pair.Value.icon); (DlcType type, string name, string icon) dlcApp = (DlcType.Entitlement, pair.Value.name, pair.Value.icon);
selection.AllDlc[dlcId] = dlcApp; selection.AllDlc[dlcId] = dlcApp;
if (allCheckBox.Checked) selection.SelectedDlc[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.Name = dlcId;
dlcNode.Text = dlcApp.name; dlcNode.Text = dlcApp.name;
dlcNode.Checked = selection.SelectedDlc.ContainsKey(dlcId); dlcNode.Checked = selection.SelectedDlc.ContainsKey(dlcId);
@ -413,11 +427,7 @@ internal partial class SelectForm : CustomForm
setup = false; setup = false;
progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . "; progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . ";
ProgramSelection.ValidateAll(); ProgramSelection.ValidateAll();
TreeNodes.ForEach(node => TreeNodes.ForEach(node => node.Remove());
{
if (node.Parent is null && ProgramSelection.FromId(node.Name) is null)
node.Remove();
});
await GetApplicablePrograms(iProgress); await GetApplicablePrograms(iProgress);
await SteamCMD.Cleanup(); await SteamCMD.Cleanup();

View file

@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CreamInstaller.Utility; using CreamInstaller.Utility;
@ -22,16 +24,23 @@ internal static class SteamStore
return dlcIds; return dlcIds;
}); });
internal static async Task<AppData> QueryStoreAPI(string appId) private static readonly ConcurrentDictionary<string, DateTime> lastQueries = new();
internal static async Task<AppData> QueryStoreAPI(string appId, int cooldown = 10)
{ {
if (Program.Canceled) return null; 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"; string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json";
DateTime now = DateTime.UtcNow;
if (!lastQueries.TryGetValue(appId, out DateTime lastQuery) || (now - lastQuery).TotalSeconds > cooldown)
{
lastQueries[appId] = now;
string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}");
if (response is not null) if (response is not null)
{ {
IDictionary<string, JToken> apps = (dynamic)JsonConvert.DeserializeObject(response); IDictionary<string, JToken> apps = (dynamic)JsonConvert.DeserializeObject(response);
foreach (KeyValuePair<string, JToken> app in apps) foreach (KeyValuePair<string, JToken> app in apps)
{ {
Thread.Sleep(0);
try try
{ {
AppData data = JsonConvert.DeserializeObject<AppDetails>(app.Value.ToString()).data; AppData data = JsonConvert.DeserializeObject<AppDetails>(app.Value.ToString()).data;
@ -49,6 +58,7 @@ internal static class SteamStore
} }
} }
} }
}
if (Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile)) if (Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile))
{ {
try try