From 5a07f8a2a399da4821310054a82929f10ad4e2b9 Mon Sep 17 00:00:00 2001 From: Archi Date: Mon, 18 Mar 2024 11:53:14 +0100 Subject: [PATCH] Make descriptions optional, open constructors for plugins In rare occurances, we might not have a description assigned to the item. This is most notable in inactive trade offers, but we permit this to happen even in inventory fetches. Assigning "default" description is unwanted if caller wants to have a way to determine that description wasn't there to begin with. It makes more sense to make it nullable and *expect* it to be null, then caller can do appropriate checking and decide what they want to do with that. Also open constructors for plugins usage in case they'd like to construct assets manually, e.g. for sending. --- ArchiSteamFarm/Steam/Data/Asset.cs | 24 +++-------------- .../Steam/Data/InventoryDescription.cs | 5 ++-- .../Steam/Integration/ArchiHandler.cs | 21 +++------------ .../Steam/Integration/ArchiWebHandler.cs | 26 ++++--------------- 4 files changed, 15 insertions(+), 61 deletions(-) diff --git a/ArchiSteamFarm/Steam/Data/Asset.cs b/ArchiSteamFarm/Steam/Data/Asset.cs index 21d8d76f8..96659cec6 100644 --- a/ArchiSteamFarm/Steam/Data/Asset.cs +++ b/ArchiSteamFarm/Steam/Data/Asset.cs @@ -28,45 +28,34 @@ using JetBrains.Annotations; namespace ArchiSteamFarm.Steam.Data; // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset +[PublicAPI] public sealed class Asset { - [PublicAPI] public const uint SteamAppID = 753; - - [PublicAPI] public const ulong SteamCommunityContextID = 6; - - [PublicAPI] public const ulong SteamPointsShopInstanceID = 3865004543; [JsonIgnore] - [PublicAPI] public bool IsSteamPointsShopItem => !Tradable && (InstanceID == SteamPointsShopInstanceID); [JsonIgnore] - [PublicAPI] public bool Marketable => Description?.Marketable ?? false; [JsonIgnore] - [PublicAPI] public EAssetRarity Rarity => Description?.Rarity ?? EAssetRarity.Unknown; [JsonIgnore] - [PublicAPI] public uint RealAppID => Description?.RealAppID ?? 0; [JsonIgnore] - [PublicAPI] public bool Tradable => Description?.Tradable ?? false; [JsonIgnore] - [PublicAPI] public EAssetType Type => Description?.Type ?? EAssetType.Unknown; [JsonInclude] [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] [JsonPropertyName("amount")] [JsonRequired] - [PublicAPI] public uint Amount { get; internal set; } [JsonInclude] @@ -76,29 +65,24 @@ public sealed class Asset { [JsonInclude] [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] [JsonPropertyName("assetid")] - [PublicAPI] public ulong AssetID { get; private init; } [JsonInclude] [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] [JsonPropertyName("classid")] - [PublicAPI] public ulong ClassID { get; private init; } [JsonInclude] [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] [JsonPropertyName("contextid")] - [PublicAPI] public ulong ContextID { get; private init; } [JsonIgnore] - [PublicAPI] public InventoryDescription? Description { get; internal set; } [JsonInclude] [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] [JsonPropertyName("instanceid")] - [PublicAPI] public ulong InstanceID { get; private init; } [JsonInclude] @@ -109,19 +93,19 @@ public sealed class Asset { init => AssetID = value; } - internal Asset(uint appID, ulong contextID, ulong classID, uint amount, InventoryDescription description, ulong assetID = 0, ulong instanceID = 0) { + [PublicAPI] + public Asset(uint appID, ulong contextID, ulong classID, uint amount, InventoryDescription? description = null, ulong assetID = 0, ulong instanceID = 0) { ArgumentOutOfRangeException.ThrowIfZero(appID); ArgumentOutOfRangeException.ThrowIfZero(contextID); ArgumentOutOfRangeException.ThrowIfZero(classID); ArgumentOutOfRangeException.ThrowIfZero(amount); - ArgumentNullException.ThrowIfNull(description); AppID = appID; ContextID = contextID; ClassID = classID; Amount = amount; - Description = description; + Description = description; AssetID = assetID; InstanceID = instanceID; } diff --git a/ArchiSteamFarm/Steam/Data/InventoryDescription.cs b/ArchiSteamFarm/Steam/Data/InventoryDescription.cs index ffb4ff05c..0d407361f 100644 --- a/ArchiSteamFarm/Steam/Data/InventoryDescription.cs +++ b/ArchiSteamFarm/Steam/Data/InventoryDescription.cs @@ -512,14 +512,13 @@ public sealed class InventoryDescription { private uint? CachedRealAppID; private EAssetType? CachedType; - internal InventoryDescription(CEconItem_Description description) { + public InventoryDescription(CEconItem_Description description) { ArgumentNullException.ThrowIfNull(description); Body = description; } - // For self-created stubs - internal InventoryDescription(uint appID, ulong classID, ulong instanceID = 0, bool marketable = false, bool tradable = false, uint realAppID = 0, EAssetType type = EAssetType.Unknown, EAssetRarity rarity = EAssetRarity.Unknown) { + public InventoryDescription(uint appID, ulong classID, ulong instanceID = 0, bool marketable = false, bool tradable = false, uint realAppID = 0, EAssetType type = EAssetType.Unknown, EAssetRarity rarity = EAssetRarity.Unknown) { ArgumentOutOfRangeException.ThrowIfZero(appID); ArgumentOutOfRangeException.ThrowIfZero(classID); diff --git a/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs index 863b1eb25..5cb49c39e 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs @@ -230,28 +230,15 @@ public sealed class ArchiHandler : ClientMsgHandler { descriptions.Add(key, new InventoryDescription(description)); } - foreach (CEcon_Asset? asset in response.assets) { - if (!assetIDs.Add(asset.assetid)) { - continue; - } - - (ulong ClassID, ulong InstanceID) key = (asset.classid, asset.instanceid); - - if (!descriptions.TryGetValue(key, out InventoryDescription? description)) { - // Best effort only - description = new InventoryDescription(appID, asset.classid, asset.instanceid); - - descriptions.Add(key, description); - } + foreach (CEcon_Asset? asset in response.assets.Where(asset => assetIDs.Add(asset.assetid))) { + InventoryDescription? description = descriptions.GetValueOrDefault((asset.classid, asset.instanceid)); // Extra bulletproofing against Steam showing us middle finger - if ((tradableOnly && !description.Tradable) || (marketableOnly && !description.Marketable)) { + if ((tradableOnly && (description?.Tradable != true)) || (marketableOnly && (description?.Marketable != true))) { continue; } - Asset convertedAsset = new(asset.appid, asset.contextid, asset.classid, (uint) asset.amount, description, asset.assetid, asset.instanceid); - - yield return convertedAsset; + yield return new Asset(asset.appid, asset.contextid, asset.classid, (uint) asset.amount, description, asset.assetid, asset.instanceid); } if (!response.more_items) { diff --git a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs index 0ebe0f04b..02095b6ba 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs @@ -362,22 +362,11 @@ public sealed class ArchiWebHandler : IDisposable { descriptions.TryAdd(key, description); } - foreach (Asset asset in response.Content.Assets) { - if (!assetIDs.Add(asset.AssetID)) { - continue; + foreach (Asset asset in response.Content.Assets.Where(asset => assetIDs.Add(asset.AssetID))) { + if (descriptions.TryGetValue((asset.ClassID, asset.InstanceID), out InventoryDescription? description)) { + asset.Description = description; } - (ulong ClassID, ulong InstanceID) key = (asset.ClassID, asset.InstanceID); - - if (!descriptions.TryGetValue(key, out InventoryDescription? description)) { - // Best effort only - description = new InventoryDescription(appID, asset.ClassID, asset.InstanceID); - - descriptions.Add(key, description); - } - - asset.Description = description; - yield return asset; } @@ -2390,14 +2379,9 @@ public sealed class ArchiWebHandler : IDisposable { foreach (Asset asset in assets) { (uint AppID, ulong ClassID, ulong InstanceID) key = (asset.AppID, asset.ClassID, asset.InstanceID); - if (!descriptions.TryGetValue(key, out InventoryDescription? description)) { - // Best effort only - we can guarantee tradable property at best, and only at the time of the trade offer - description = new InventoryDescription(asset.AppID, asset.ClassID, asset.InstanceID, tradable: true); - - descriptions.Add(key, description); + if (descriptions.TryGetValue(key, out InventoryDescription? description)) { + asset.Description = description; } - - asset.Description = description; } }