This commit is contained in:
JustArchi
2017-12-30 21:28:01 +01:00
parent 325e7a1ff2
commit 434edd6667
5 changed files with 122 additions and 107 deletions

View File

@@ -22,7 +22,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
@@ -356,7 +355,8 @@ namespace ArchiSteamFarm {
}
if ((hoursPlayed < CardsFarmer.HoursForRefund) && !BotConfig.IdleRefundableGames) {
if (!Program.GlobalDatabase.AppIDsToPackageIDs.TryGetValue(appID, out ConcurrentHashSet<uint> packageIDs)) {
HashSet<uint> packageIDs = Program.GlobalDatabase.GetPackageIDs(appID);
if (packageIDs == null) {
return (0, DateTime.MaxValue);
}
@@ -497,85 +497,6 @@ namespace ArchiSteamFarm {
return (appID, DateTime.MinValue);
}
internal async Task<Dictionary<uint, HashSet<uint>>> GetAppIDsToPackageIDs(IReadOnlyCollection<uint> packageIDs) {
if ((packageIDs == null) || (packageIDs.Count == 0)) {
ArchiLogger.LogNullError(nameof(packageIDs));
return null;
}
AsyncJobMultiple<SteamApps.PICSProductInfoCallback>.ResultSet productInfoResultSet = null;
for (byte i = 0; (i < WebBrowser.MaxTries) && (productInfoResultSet == null); i++) {
if (!IsConnectedAndLoggedOn) {
return null;
}
await PICSSemaphore.WaitAsync().ConfigureAwait(false);
try {
productInfoResultSet = await SteamApps.PICSGetProductInfo(Enumerable.Empty<uint>(), packageIDs);
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);
} finally {
PICSSemaphore.Release();
}
}
if (productInfoResultSet == null) {
return null;
}
Dictionary<uint, HashSet<uint>> result = new Dictionary<uint, HashSet<uint>>();
foreach (KeyValuePair<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo> productInfoPackages in productInfoResultSet.Results.SelectMany(productInfoResult => productInfoResult.Packages)) {
KeyValue productInfo = productInfoPackages.Value.KeyValues;
if (productInfo == KeyValue.Invalid) {
ArchiLogger.LogNullError(nameof(productInfo));
return null;
}
KeyValue apps = productInfo["appids"];
if (apps == KeyValue.Invalid) {
continue;
}
if (apps.Children.Count == 0) {
if (!result.TryGetValue(0, out HashSet<uint> packages)) {
packages = new HashSet<uint>();
result[0] = packages;
}
packages.Add(productInfoPackages.Key);
continue;
}
foreach (string app in apps.Children.Select(app => app.Value)) {
if (!uint.TryParse(app, out uint appID) || (appID == 0)) {
ArchiLogger.LogNullError(nameof(appID));
return null;
}
if (!result.TryGetValue(appID, out HashSet<uint> packages)) {
packages = new HashSet<uint>();
result[appID] = packages;
}
packages.Add(productInfoPackages.Key);
}
}
foreach (uint packageID in productInfoResultSet.Results.SelectMany(productInfoResult => productInfoResult.UnknownPackages)) {
if (!result.TryGetValue(0, out HashSet<uint> packages)) {
packages = new HashSet<uint>();
result[0] = packages;
}
packages.Add(packageID);
}
return result;
}
internal static HashSet<Bot> GetBots(string args) {
if (string.IsNullOrEmpty(args)) {
ASF.ArchiLogger.LogNullError(nameof(args));
@@ -629,6 +550,68 @@ namespace ArchiSteamFarm {
return result;
}
internal async Task<Dictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)>> GetPackagesData(IReadOnlyCollection<uint> packageIDs) {
if ((packageIDs == null) || (packageIDs.Count == 0)) {
ArchiLogger.LogNullError(nameof(packageIDs));
return null;
}
AsyncJobMultiple<SteamApps.PICSProductInfoCallback>.ResultSet productInfoResultSet = null;
for (byte i = 0; (i < WebBrowser.MaxTries) && (productInfoResultSet == null); i++) {
if (!IsConnectedAndLoggedOn) {
return null;
}
await PICSSemaphore.WaitAsync().ConfigureAwait(false);
try {
productInfoResultSet = await SteamApps.PICSGetProductInfo(Enumerable.Empty<uint>(), packageIDs);
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);
} finally {
PICSSemaphore.Release();
}
}
if (productInfoResultSet == null) {
return null;
}
Dictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)> result = new Dictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)>();
foreach (SteamApps.PICSProductInfoCallback.PICSProductInfo productInfo in productInfoResultSet.Results.SelectMany(productInfoResult => productInfoResult.Packages).Select(productInfoPackages => productInfoPackages.Value)) {
if (productInfo.KeyValues == KeyValue.Invalid) {
ArchiLogger.LogNullError(nameof(productInfo));
return null;
}
(uint ChangeNumber, HashSet<uint> AppIDs) value = (productInfo.ChangeNumber, null);
try {
KeyValue appIDs = productInfo.KeyValues["appids"];
if (appIDs == KeyValue.Invalid) {
continue;
}
value.AppIDs = new HashSet<uint>();
foreach (string appIDText in appIDs.Children.Select(app => app.Value)) {
if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) {
ArchiLogger.LogNullError(nameof(appID));
return null;
}
value.AppIDs.Add(appID);
}
} finally {
result[productInfo.ID] = value;
}
}
return result;
}
internal async Task IdleGame(CardsFarmer.Game game) {
if (game == null) {
ArchiLogger.LogNullError(nameof(game));
@@ -1808,16 +1791,28 @@ namespace ArchiSteamFarm {
}
OwnedPackageIDs.Clear();
bool refreshData = !BotConfig.IdleRefundableGames || (BotConfig.FarmingOrder == BotConfig.EFarmingOrder.RedeemDateTimesAscending) || (BotConfig.FarmingOrder == BotConfig.EFarmingOrder.RedeemDateTimesDescending);
Dictionary<uint, uint> packagesToRefresh = new Dictionary<uint, uint>();
foreach (SteamApps.LicenseListCallback.License license in callback.LicenseList) {
OwnedPackageIDs[license.PackageID] = (license.PaymentMethod, license.TimeCreated);
if (!refreshData) {
continue;
}
if (OwnedPackageIDs.Count > 0) {
if (!BotConfig.IdleRefundableGames || (BotConfig.FarmingOrder == BotConfig.EFarmingOrder.RedeemDateTimesAscending) || (BotConfig.FarmingOrder == BotConfig.EFarmingOrder.RedeemDateTimesDescending)) {
Program.GlobalDatabase.RefreshPackageIDs(this, OwnedPackageIDs.Keys.ToImmutableHashSet()).Forget();
if (!Program.GlobalDatabase.PackagesData.TryGetValue(license.PackageID, out (uint ChangeNumber, HashSet<uint> _) packageData) || (packageData.ChangeNumber < license.LastChangeNumber)) {
packagesToRefresh[license.PackageID] = (uint) license.LastChangeNumber;
}
}
if (packagesToRefresh.Count > 0) {
ArchiLogger.LogGenericInfo(Strings.BotRefreshingPackagesData);
await Program.GlobalDatabase.RefreshPackageIDs(this, packagesToRefresh).ConfigureAwait(false);
ArchiLogger.LogGenericInfo(Strings.Done);
}
// Wait a second for eventual PlayingSessionStateCallback or SharedLibraryLockStatusCallback
await Task.Delay(1000).ConfigureAwait(false);

View File

@@ -969,7 +969,9 @@ namespace ArchiSteamFarm {
foreach (Game game in GamesToFarm) {
DateTime redeemDate = DateTime.MinValue;
if (Program.GlobalDatabase.AppIDsToPackageIDs.TryGetValue(game.AppID, out ConcurrentHashSet<uint> packageIDs)) {
HashSet<uint> packageIDs = Program.GlobalDatabase.GetPackageIDs(game.AppID);
if (packageIDs != null) {
foreach (uint packageID in packageIDs) {
if (!Bot.OwnedPackageIDs.TryGetValue(packageID, out (EPaymentMethod PaymentMethod, DateTime TimeCreated) packageData)) {
continue;

View File

@@ -31,10 +31,10 @@ using Newtonsoft.Json;
namespace ArchiSteamFarm {
internal sealed class GlobalDatabase : IDisposable {
[JsonProperty(Required = Required.DisallowNull)]
internal readonly ConcurrentDictionary<uint, ConcurrentHashSet<uint>> AppIDsToPackageIDs = new ConcurrentDictionary<uint, ConcurrentHashSet<uint>>();
internal readonly Guid Guid = Guid.NewGuid();
[JsonProperty(Required = Required.DisallowNull)]
internal readonly Guid Guid = Guid.NewGuid();
internal readonly ConcurrentDictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)> PackagesData = new ConcurrentDictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)>();
[JsonProperty(Required = Required.DisallowNull)]
internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider();
@@ -69,6 +69,16 @@ namespace ArchiSteamFarm {
PackagesRefreshSemaphore.Dispose();
}
internal HashSet<uint> GetPackageIDs(uint appID) {
if (appID == 0) {
ASF.ArchiLogger.LogNullError(nameof(appID));
return null;
}
HashSet<uint> result = new HashSet<uint>(PackagesData.Where(package => (package.Value.AppIDs != null) && package.Value.AppIDs.Contains(appID)).Select(package => package.Key));
return result;
}
internal static GlobalDatabase Load(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
ASF.ArchiLogger.LogNullError(nameof(filePath));
@@ -97,34 +107,30 @@ namespace ArchiSteamFarm {
return globalDatabase;
}
internal async Task RefreshPackageIDs(Bot bot, IReadOnlyCollection<uint> packageIDs) {
if ((bot == null) || (packageIDs == null) || (packageIDs.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(packageIDs));
internal async Task RefreshPackageIDs(Bot bot, IReadOnlyDictionary<uint, uint> packages) {
if ((bot == null) || (packages == null) || (packages.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(packages));
return;
}
await PackagesRefreshSemaphore.WaitAsync().ConfigureAwait(false);
try {
HashSet<uint> missingPackageIDs = new HashSet<uint>(packageIDs.AsParallel().Where(packageID => AppIDsToPackageIDs.Values.All(packages => !packages.Contains(packageID))));
if (missingPackageIDs.Count == 0) {
HashSet<uint> packageIDs = new HashSet<uint>();
foreach (KeyValuePair<uint, uint> package in packages) {
if (!PackagesData.TryGetValue(package.Key, out (uint ChangeNumber, HashSet<uint> _) packageData) || (packageData.ChangeNumber < package.Value)) {
packageIDs.Add(package.Key);
}
}
Dictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)> packagesData = await bot.GetPackagesData(packageIDs).ConfigureAwait(false);
if ((packagesData == null) || (packagesData.Count == 0)) {
return;
}
Dictionary<uint, HashSet<uint>> appIDsToPackageIDs = await bot.GetAppIDsToPackageIDs(missingPackageIDs).ConfigureAwait(false);
if ((appIDsToPackageIDs == null) || (appIDsToPackageIDs.Count == 0)) {
return;
}
foreach (KeyValuePair<uint, HashSet<uint>> appIDtoPackageID in appIDsToPackageIDs) {
if (!AppIDsToPackageIDs.TryGetValue(appIDtoPackageID.Key, out ConcurrentHashSet<uint> packages)) {
packages = new ConcurrentHashSet<uint>();
AppIDsToPackageIDs[appIDtoPackageID.Key] = packages;
}
foreach (uint package in appIDtoPackageID.Value) {
packages.Add(package);
}
foreach (KeyValuePair<uint, (uint ChangeNumber, HashSet<uint> AppIDs)> packageData in packagesData) {
PackagesData[packageData.Key] = packageData.Value;
}
await Save().ConfigureAwait(false);

View File

@@ -528,6 +528,15 @@ namespace ArchiSteamFarm.Localization {
}
}
/// <summary>
/// Looks up a localized string similar to Refreshing packages data....
/// </summary>
internal static string BotRefreshingPackagesData {
get {
return ResourceManager.GetString("BotRefreshingPackagesData", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Removed expired login key!.
/// </summary>

View File

@@ -662,4 +662,7 @@ StackTrace:
<value>There are {0}/{1} bots that already own all of the games being checked.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
<data name="BotRefreshingPackagesData" xml:space="preserve">
<value>Refreshing packages data...</value>
</data>
</root>