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
This commit is contained in:
parent
477e7b4445
commit
509033c8c9
3 changed files with 50 additions and 30 deletions
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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,30 +24,38 @@ 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";
|
||||||
if (response is not null)
|
DateTime now = DateTime.UtcNow;
|
||||||
|
if (!lastQueries.TryGetValue(appId, out DateTime lastQuery) || (now - lastQuery).TotalSeconds > cooldown)
|
||||||
{
|
{
|
||||||
IDictionary<string, JToken> apps = (dynamic)JsonConvert.DeserializeObject(response);
|
lastQueries[appId] = now;
|
||||||
foreach (KeyValuePair<string, JToken> app in apps)
|
string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}");
|
||||||
|
if (response is not null)
|
||||||
{
|
{
|
||||||
try
|
IDictionary<string, JToken> apps = (dynamic)JsonConvert.DeserializeObject(response);
|
||||||
|
foreach (KeyValuePair<string, JToken> app in apps)
|
||||||
{
|
{
|
||||||
AppData data = JsonConvert.DeserializeObject<AppDetails>(app.Value.ToString()).data;
|
Thread.Sleep(0);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.WriteAllText(cacheFile, JsonConvert.SerializeObject(data, Formatting.Indented));
|
AppData data = JsonConvert.DeserializeObject<AppDetails>(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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue