From c5009dad31d15980cffbfce004b91a41adae2b3a Mon Sep 17 00:00:00 2001 From: Archi Date: Fri, 22 Mar 2024 16:34:46 +0100 Subject: [PATCH] Decrease itemsCountPerRequest to 5000, optimize performance a bit, closes #3171 --- .../Steam/Integration/ArchiHandler.cs | 13 +++++++----- .../Steam/Integration/ArchiWebHandler.cs | 21 +++++++++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs index c3a0fd8310493..0fd1017aa26fe 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs @@ -173,7 +173,7 @@ public async Task AddFriend(ulong steamID) { } [PublicAPI] - public async IAsyncEnumerable GetMyInventoryAsync(uint appID = Asset.SteamAppID, ulong contextID = Asset.SteamCommunityContextID, bool tradableOnly = false, bool marketableOnly = false, ushort itemsCountPerRequest = 10000) { + public async IAsyncEnumerable GetMyInventoryAsync(uint appID = Asset.SteamAppID, ulong contextID = Asset.SteamCommunityContextID, bool tradableOnly = false, bool marketableOnly = false, ushort itemsCountPerRequest = ArchiWebHandler.MaxItemsInSingleInventoryRequest) { ArgumentOutOfRangeException.ThrowIfZero(appID); ArgumentOutOfRangeException.ThrowIfZero(contextID); ArgumentOutOfRangeException.ThrowIfZero(itemsCountPerRequest); @@ -189,6 +189,8 @@ public async IAsyncEnumerable GetMyInventoryAsync(uint appID = Asset.Stea // We need to store asset IDs to make sure we won't get duplicate items HashSet? assetIDs = null; + Dictionary<(ulong ClassID, ulong InstanceID), InventoryDescription>? descriptions = null; + while (true) { ulong currentStartAssetID = startAssetID; @@ -226,12 +228,13 @@ public async IAsyncEnumerable GetMyInventoryAsync(uint appID = Asset.Stea assetIDs ??= new HashSet((int) response.total_inventory_count); - if ((response.assets.Count == 0) || (response.descriptions.Count == 0)) { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorObjectIsNull, $"{nameof(response.assets)} || {nameof(response.descriptions)}")); + if (descriptions == null) { + descriptions = new Dictionary<(ulong ClassID, ulong InstanceID), InventoryDescription>(); + } else { + // We don't need descriptions from the previous request + descriptions.Clear(); } - Dictionary<(ulong ClassID, ulong InstanceID), InventoryDescription> descriptions = new(); - foreach (CEconItem_Description? description in response.descriptions) { if (description.classid == 0) { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorObjectIsNull, nameof(description.classid))); diff --git a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs index 3c00af46610ff..c9f382e2073e4 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs @@ -51,9 +51,11 @@ namespace ArchiSteamFarm.Steam.Integration; public sealed class ArchiWebHandler : IDisposable { + // Steam network (ArchiHandler) works unstable with more items than this (throwing upon description details), while Steam web (ArchiWebHandler) silently limits to this value maximum + internal const ushort MaxItemsInSingleInventoryRequest = 5000; + private const string EconService = "IEconService"; private const string LoyaltyRewardsService = "ILoyaltyRewardsService"; - private const ushort MaxItemsInSingleInventoryRequest = 5000; private const byte MinimumSessionValidityInSeconds = 10; private const string SteamAppsService = "ISteamApps"; @@ -261,6 +263,8 @@ public async IAsyncEnumerable GetInventoryAsync(ulong steamID = 0, uint a // We need to store asset IDs to make sure we won't get duplicate items HashSet? assetIDs = null; + Dictionary<(ulong ClassID, ulong InstanceID), InventoryDescription>? descriptions = null; + int rateLimitingDelay = (ASF.GlobalConfig?.InventoryLimiterDelay ?? GlobalConfig.DefaultInventoryLimiterDelay) * 1000; while (true) { @@ -328,23 +332,28 @@ public async IAsyncEnumerable GetInventoryAsync(ulong steamID = 0, uint a throw new HttpRequestException(!string.IsNullOrEmpty(response.Content.ErrorText) ? string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content.ErrorText) : response.Content.Result.HasValue ? string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, response.Content.Result) : Strings.WarningFailed); } - if (response.Content.TotalInventoryCount == 0) { + if ((response.Content.TotalInventoryCount == 0) || (response.Content.Assets.Count == 0)) { // Empty inventory yield break; } + if (response.Content.Descriptions.Count == 0) { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(response.Content.Descriptions))); + } + if (response.Content.TotalInventoryCount > Array.MaxLength) { throw new InvalidOperationException(nameof(response.Content.TotalInventoryCount)); } assetIDs ??= new HashSet((int) response.Content.TotalInventoryCount); - if ((response.Content.Assets.Count == 0) || (response.Content.Descriptions.Count == 0)) { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorObjectIsNull, $"{nameof(response.Content.Assets)} || {nameof(response.Content.Descriptions)}")); + if (descriptions == null) { + descriptions = new Dictionary<(ulong ClassID, ulong InstanceID), InventoryDescription>(); + } else { + // We don't need descriptions from the previous request + descriptions.Clear(); } - Dictionary<(ulong ClassID, ulong InstanceID), InventoryDescription> descriptions = new(); - foreach (InventoryDescription description in response.Content.Descriptions) { if (description.ClassID == 0) { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorObjectIsNull, nameof(description.ClassID)));