- 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>
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<Version>3.2.2.5</Version>
<Version>3.2.3.0</Version>
<PackageIcon>Resources\ini.ico</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>

View file

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

View file

@ -16,36 +16,42 @@ namespace CreamInstaller.Epic;
internal static class EpicStore
{
//private const int COOLDOWN_CATALOG_ITEM = 600;
/* need a method to query catalog items
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)
{
List<(string id, string name, string product, string icon, string developer)> dlcIds = new();
Response response = await QueryGraphQL(categoryNamespace);
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 (Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile))
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");
}
return dlcIds;
List<Element> searchStore = new(response.Data.Catalog.SearchStore.Elements);
foreach (Element element in searchStore)
{

View file

@ -144,7 +144,7 @@ internal partial class SelectForm : CustomForm
RemoveFromRemainingGames(name);
return;
}
AppData appData = await SteamStore.QueryStoreAPI(appId, 60);
AppData appData = await SteamStore.QueryStoreAPI(appId);
VProperty appInfo = await SteamCMD.GetAppInfo(appId, branch, buildId);
if (appData is null && appInfo is null)
{
@ -169,7 +169,7 @@ internal partial class SelectForm : CustomForm
Thread.Sleep(0);
string dlcName = null;
string dlcIcon = null;
AppData dlcAppData = await SteamStore.QueryStoreAPI(dlcAppId);
AppData dlcAppData = await SteamStore.QueryStoreAPI(dlcAppId, true);
if (dlcAppData is not null)
{
dlcName = dlcAppData.name;

View file

@ -112,12 +112,12 @@
<value>2.0</value>
</resheader>
<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 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>
<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">
<value>..\resources\ini.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>

View file

@ -68,11 +68,11 @@ internal static class SteamLibrary
{
List<Tuple<string, string, string, int, string>> games = new();
if (Program.Canceled || !Directory.Exists(libraryDirectory)) return null;
string[] files = Directory.GetFiles(libraryDirectory);
string[] files = Directory.GetFiles(libraryDirectory, "*.acf");
foreach (string file in files)
{
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 installdir = result.Value.GetChild("installdir")?.ToString();

View file

@ -1,8 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using CreamInstaller.Utility;
@ -23,23 +20,22 @@ internal static class SteamStore
return dlcIds;
});
private static readonly ConcurrentDictionary<string, DateTime> lastQueries = new();
internal static async Task<AppData> QueryStoreAPI(string appId, int cooldown = 10)
private const int COOLDOWN_GAME = 600;
private const int COOLDOWN_DLC = 1200;
internal static async Task<AppData> QueryStoreAPI(string appId, bool isDlc = false)
{
if (Program.Canceled) return null;
Thread.Sleep(0);
string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json";
DateTime now = DateTime.UtcNow;
if (!lastQueries.TryGetValue(appId, out DateTime lastQuery) || (now - lastQuery).TotalSeconds > cooldown)
bool cachedExists = Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile);
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}");
if (response is not null)
{
IDictionary<string, JToken> apps = (dynamic)JsonConvert.DeserializeObject(response);
foreach (KeyValuePair<string, JToken> app in apps)
{
Thread.Sleep(0);
try
{
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
{

View file

@ -16,6 +16,8 @@ internal static class ProgramData
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(() =>
{
if (Directory.Exists(DirectoryPathOld))
@ -30,5 +32,45 @@ internal static class ProgramData
Directory.CreateDirectory(AppInfoPath);
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 { }
}
}