- Minor refactoring
- Querying cooldown is now written to and read from ProgramData so it can be persistent and hopefully steam's API limit is reached a little less often
- Increased steam game info querying cooldown to 600 seconds (10 minutes) and steam DLC info querying cooldown to 1200 seconds (20 minutes)
- Introduced a 600 second (10 minute) cooldown for Epic game queries using the same system above
This commit is contained in:
pointfeev 2022-03-11 19:46:18 -05:00
parent dc2bc0e9e1
commit 62f9ba8ee9
8 changed files with 91 additions and 50 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.5</Version> <Version>3.2.3.0</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

@ -27,21 +27,18 @@ internal static class EpicLibrary
if (!Directory.Exists(EpicAppDataPath)) return games; if (!Directory.Exists(EpicAppDataPath)) return games;
string manifests = EpicAppDataPath + @"\Manifests"; string manifests = EpicAppDataPath + @"\Manifests";
if (!Directory.Exists(manifests)) return games; if (!Directory.Exists(manifests)) return games;
string[] files = Directory.GetFiles(manifests); string[] files = Directory.GetFiles(manifests, "*.item");
foreach (string file in files) foreach (string file in files)
{ {
if (Program.Canceled) return games; if (Program.Canceled) return games;
if (Path.GetExtension(file) == ".item") string json = File.ReadAllText(file);
try
{ {
string json = File.ReadAllText(file); Manifest manifest = JsonSerializer.Deserialize<Manifest>(json);
try if (manifest is not null && manifest.CatalogItemId == manifest.MainGameCatalogItemId)
{ games.Add(manifest);
Manifest manifest = JsonSerializer.Deserialize<Manifest>(json);
if (manifest is not null && manifest.CatalogItemId == manifest.MainGameCatalogItemId)
games.Add(manifest);
}
catch { };
} }
catch { };
} }
return games; return games;
}); });

View file

@ -16,36 +16,42 @@ namespace CreamInstaller.Epic;
internal static class EpicStore internal static class EpicStore
{ {
//private const int COOLDOWN_CATALOG_ITEM = 600;
/* need a method to query catalog items /* need a method to query catalog items
internal static async Task QueryCatalogItems(Manifest manifest) internal static async Task QueryCatalogItems(Manifest manifest)
{ {
}*/ }*/
private const int COOLDOWN_ENTITLEMENT = 600;
internal static async Task<List<(string id, string name, string product, string icon, string developer)>> QueryEntitlements(string categoryNamespace) internal static async Task<List<(string id, string name, string product, string icon, string developer)>> QueryEntitlements(string categoryNamespace)
{ {
List<(string id, string name, string product, string icon, string developer)> dlcIds = new(); List<(string id, string name, string product, string icon, string developer)> dlcIds = new();
Response response = await QueryGraphQL(categoryNamespace);
string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json"; string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json";
bool cachedExists = Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile);
Response response = null;
if (!cachedExists || ProgramData.CheckCooldown(categoryNamespace, COOLDOWN_ENTITLEMENT))
{
response = await QueryGraphQL(categoryNamespace);
try
{
File.WriteAllText(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented));
}
catch { }
}
else if (cachedExists)
{
try
{
response = JsonConvert.DeserializeObject<Response>(File.ReadAllText(cacheFile));
}
catch
{
File.Delete(cacheFile);
}
}
if (response is null) if (response is null)
if (Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile)) return dlcIds;
try
{
response = JsonConvert.DeserializeObject<Response>(File.ReadAllText(cacheFile));
}
catch
{
File.Delete(cacheFile);
}
if (response is null) return dlcIds;
try
{
File.WriteAllText(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented));
}
catch //(Exception e)
{
//using DialogForm dialogForm = new(null);
//dialogForm.Show(SystemIcons.Error, "Unsuccessful serialization of query for category namespace " + categoryNamespace + ":\n\n" + e.ToString(), "FUCK");
}
List<Element> searchStore = new(response.Data.Catalog.SearchStore.Elements); List<Element> searchStore = new(response.Data.Catalog.SearchStore.Elements);
foreach (Element element in searchStore) foreach (Element element in searchStore)
{ {

View file

@ -144,7 +144,7 @@ internal partial class SelectForm : CustomForm
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
return; return;
} }
AppData appData = await SteamStore.QueryStoreAPI(appId, 60); AppData appData = await SteamStore.QueryStoreAPI(appId);
VProperty 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)
{ {
@ -169,7 +169,7 @@ internal partial class SelectForm : CustomForm
Thread.Sleep(0); 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, true);
if (dlcAppData is not null) if (dlcAppData is not null)
{ {
dlcName = dlcAppData.name; dlcName = dlcAppData.name;

View file

@ -112,12 +112,12 @@
<value>2.0</value> <value>2.0</value>
</resheader> </resheader>
<resheader name="reader"> <resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\ini.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>..\resources\ini.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>

View file

@ -68,11 +68,11 @@ internal static class SteamLibrary
{ {
List<Tuple<string, string, string, int, string>> games = new(); List<Tuple<string, string, string, int, string>> games = new();
if (Program.Canceled || !Directory.Exists(libraryDirectory)) return null; if (Program.Canceled || !Directory.Exists(libraryDirectory)) return null;
string[] files = Directory.GetFiles(libraryDirectory); string[] files = Directory.GetFiles(libraryDirectory, "*.acf");
foreach (string file in files) foreach (string file in files)
{ {
if (Program.Canceled) return null; if (Program.Canceled) return null;
if (Path.GetExtension(file) == ".acf" && ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result)) if (ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result))
{ {
string appId = result.Value.GetChild("appid")?.ToString(); string appId = result.Value.GetChild("appid")?.ToString();
string installdir = result.Value.GetChild("installdir")?.ToString(); string installdir = result.Value.GetChild("installdir")?.ToString();

View file

@ -1,8 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CreamInstaller.Utility; using CreamInstaller.Utility;
@ -23,23 +20,22 @@ internal static class SteamStore
return dlcIds; return dlcIds;
}); });
private static readonly ConcurrentDictionary<string, DateTime> lastQueries = new(); private const int COOLDOWN_GAME = 600;
internal static async Task<AppData> QueryStoreAPI(string appId, int cooldown = 10) private const int COOLDOWN_DLC = 1200;
internal static async Task<AppData> QueryStoreAPI(string appId, bool isDlc = false)
{ {
if (Program.Canceled) return null; if (Program.Canceled) return null;
Thread.Sleep(0);
string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json"; string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json";
DateTime now = DateTime.UtcNow; bool cachedExists = Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile);
if (!lastQueries.TryGetValue(appId, out DateTime lastQuery) || (now - lastQuery).TotalSeconds > cooldown) if (!cachedExists || ProgramData.CheckCooldown(appId, isDlc ? COOLDOWN_DLC : COOLDOWN_GAME))
{ {
lastQueries[appId] = now;
string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}"); 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;
@ -65,7 +61,7 @@ internal static class SteamStore
} }
} }
} }
if (Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile)) if (cachedExists)
{ {
try try
{ {

View file

@ -16,6 +16,8 @@ internal static class ProgramData
internal static readonly Version MinimumAppInfoVersion = Version.Parse("3.2.0.0"); internal static readonly Version MinimumAppInfoVersion = Version.Parse("3.2.0.0");
internal static readonly string CooldownPath = DirectoryPath + @"\cooldown";
internal static async Task Setup() => await Task.Run(() => internal static async Task Setup() => await Task.Run(() =>
{ {
if (Directory.Exists(DirectoryPathOld)) if (Directory.Exists(DirectoryPathOld))
@ -30,5 +32,45 @@ internal static class ProgramData
Directory.CreateDirectory(AppInfoPath); Directory.CreateDirectory(AppInfoPath);
File.WriteAllText(AppInfoVersionPath, Application.ProductVersion, Encoding.UTF8); File.WriteAllText(AppInfoVersionPath, Application.ProductVersion, Encoding.UTF8);
} }
if (!Directory.Exists(CooldownPath))
Directory.CreateDirectory(CooldownPath);
}); });
internal static bool CheckCooldown(string identifier, int cooldown)
{
DateTime now = DateTime.UtcNow;
DateTime lastCheck = GetCooldown(identifier) ?? now;
bool cooldownOver = (now - lastCheck).TotalSeconds > cooldown;
if (cooldownOver || now == lastCheck)
SetCooldown(identifier, now);
return cooldownOver;
}
private static DateTime? GetCooldown(string identifier)
{
if (Directory.Exists(CooldownPath))
{
string cooldownFile = CooldownPath + @$"\{identifier}.txt";
if (File.Exists(cooldownFile))
{
try
{
if (DateTime.TryParse(File.ReadAllText(cooldownFile), out DateTime cooldown))
return cooldown;
}
catch { }
}
}
return null;
}
private static void SetCooldown(string identifier, DateTime time)
{
if (!Directory.Exists(CooldownPath))
Directory.CreateDirectory(CooldownPath);
string cooldownFile = CooldownPath + @$"\{identifier}.txt";
try
{
File.WriteAllText(cooldownFile, time.ToString());
}
catch { }
}
} }