Jump to content
McKay Development

Item Craftability from GC


KTVS
 Share

Recommended Posts

I was hoping to get an elaboration on this post that was made a year ago. Similar to this fellow, I'm wondering how to get the craftability & tradability of an item sent from the GC, but, as you eluded to, am struggling with the various conditions. At first I thought it was just a matter of the the 5th bit being set in the flag attribute for non-craftable items, but this lead to all supply crate keys being flagged as non-craftable. I pretty much gave up after realizing this and started reloading from the web, but I noticed that steam's inventory endpoints have some lag to them which caused issues when reloading soon after crafting metal (old metal would still show and new metal wouldn't).

I've effectively resolved the issues by using the GC just to get the asset ids of items that are in my inventory, and the names get filled in whenever they're seen by the web inventory. That solves the problems, but it would be fantastic to not have to reload the inventory from the web at all.

Is it actually possible to accurately get the craftability & tradability of an item just from the info the GC gives back? If so, is it possible to get (to your best knowledge) the key attributes I need to be looking at?

Your help is deeply appreciated.

Link to comment
Share on other sites

It's entirely possible that keys have the cannot-craft flag set. They're necessarily purchased from the store, after all. The web and in-game UIs might just hide that text for keys.

It is actually possible to accurately get craftability and tradability from the GC data (after all, that's the same data the official game client gets and it has to be able to determine if items are tradable or craftable), but it's a complete mess and you need to check a handful of different conditions and attributes because it's Valve and Valve can't write good code.

For example, there's an attribute "cannot trade" but then there's also an attribute "always tradable" and I have no idea which takes precedence. My wild guess would be that "always tradable" trumps the achievement origin but is itself trumped by the flag and other untradable attributes (e.g. "tradable after date") but this is likely a really deep rabbit hole to dive into and I've never been insane enough to try.

What is it you're doing? How frequently do you need to retrieve item data?

Link to comment
Share on other sites

12 hours ago, Dr. McKay said:

...but this is likely a really deep rabbit hole to dive into and I've never been insane enough to try.

I'm satisfied enough with this answer; if you say it's that bad then I'm sure the enormous headache outweighs the benefit.

The main issue I'm facing is re-finding items after they've been acquired in a trade. Trades that don't require confirmation can have the new item's asset ids determined by looking at the trade receipt, but I haven't seen a way to get the trade ID for the trade receipt when the offer required confirmation or was a sent offer that was accepted. Luckily enough, the asset ids are right there in the message from the GC... but I don't know to what items they belong and I don't want to be making educated guesses. 

Due to the nature of my program, I run right up against Steam's web inventory rate limit (100 calls every 2 minutes). I would definitely benefit from trimming unnecessary inventory calls, especially those that are just updating my own inventory, but I still need them anyway to determine which items are currently tradable and which aren't based on the inventory cache. It seems like an unnecessary hassle forcing steam to uncache my inventory every time I want to send a trade offer, so for those two reasons (the headache of parsing the craftability/tradability and handling cached items), it looks like the web calls must stay. 
 

12 hours ago, Dr. McKay said:

The web and in-game UIs might just hide that text for keys.

This sounds like a nightmare that, as you suggested, probably isn't worth trying to figure out.

My core issue of Steam's inventory rate limit actually brings me to another question, but it's unrelated to this topic so I think it's best to start a new one for clarity.

As always, thank you for your help!

Edited by KTVS
Link to comment
Share on other sites

8 hours ago, KTVS said:

Trades that don't require confirmation can have the new item's asset ids determined by looking at the trade receipt, but I haven't seen a way to get the trade ID for the trade receipt when the offer required confirmation or was a sent offer that was accepted.

If you know the ID of the trade offer you're trying to map new item IDs for, this is actually really easy. GetTradeOffer includes the trade_id of the completed trade, and GetTradeStatus includes the mapping of old to new asset IDs for all items. It even has the new asset IDs in the original user's inventory if the trade was rolled back (due to an error in the middle of committing, or canceled escrow).

Link to comment
Share on other sites

  • 1 month later...
On 12/26/2021 at 6:57 PM, Dr. McKay said:

It is actually possible to accurately get craftability and tradability from the GC data (after all, that's the same data the official game client gets and it has to be able to determine if items are tradable or craftable)

Looking into this more, I don't actually believe this is the case. I strongly suspect detailed item information is provided to the client through a separate message which is not the same as the one which triggers the backpackLoaded event. I obviously don't know what message that is, since there don't seem to be that many messages being sent to the client, but I believe it exists.

My main reason for suspecting this is related to gifted and crafted items. Crafted items will always show the *current* username of the crafter, not their username when the item was crafted. Additionally, their name isn't provided as an item attribute from the GC, their Steam3ID is. So somewhere, somehow, the crafter's username is being provided to the client. Interestingly though, when the crafter's name is changed, the client doesn't update for a while. Even the inventory api will reflect the name change before the client, so the player info could be cached locally instead of fetched on each backpack load. It's possible that this is a completely separate mechanism which is responsible solely for updating these player names, in which case my whole theory is bunk.

Even if there were some elaborate rules for determining craftibility just from the GC info, there is without a doubt *some* additional information being obtained for items before they're displayed. I'd love to hear any thoughts you have about this, since you're in a much better position to speculate what messages are being sent for what purpose.

Edited by KTVS
Link to comment
Share on other sites

On 2/14/2022 at 12:05 AM, KTVS said:

It's possible that this is a completely separate mechanism which is responsible solely for updating these player names, in which case my whole theory is bunk.

I've never personally confirmed it, though I all but guarantee that after the game client receives info from the GC about an item that's crafted or gifted by a user that's not in the local cache, it separately requests the user's persona name using the SteamWorks RequestUserInformation function. This request goes directly to Steam and bypasses the GC. This function uses the same CM message as steam-user's getPersonas method.

I promise you that all of the information necessary to tell if an item is tradable or craftable is present in the backpack property. The problem is just that Valve is good at spaghetti code and so there are a handful of different things you need to check to tell if an item is untradable or uncraftable. For tradability, there's origin (for achievement items and untradable free contract rewards), a few different item attributes (cannot trade, purchased, tradable after date, non economy, but also there's the always tradable attribute which surely overrides something but lord knows what), and a flag in the flags bitfield, and probably something else I missed too.

Non-craftability is a bit easier, and you probably just need to check the purchased, never craftable, and non economy attributes in addition to the flag in the flags bitfield.

But there's also the Preview and NotEcon flags to contend with. Logic dictates that both of those flags should also indicate that an item is untradable and uncraftable, but maybe that's not the case. It's just a complete mess.

Link to comment
Share on other sites

23 minutes ago, Dr. McKay said:

I all but guarantee that after the game client receives info from the GC about an item that's crafted or gifted by a user that's not in the local cache, it separately requests the user's persona name using the SteamWorks RequestUserInformation function

As time went by since posting, I had a worse and worse feeling that this was the case.

I've tried comparing craftable/non-craftable versions of the same item to each other, but the only difference is the origin and, consequently, those two flags. As you just mentioned in my other post, those two flags could actually be named differently than the enums (I had originally suspected they were all reversed by mistake, which actually prompted that other post about them in the first place, but I of course have no solid reason to believe that) but the flags are still not unique to craftable or non-craftable items. The item attributes you mentioned are visible when requesting an inventory from the tf2 api, but are not visible to the GC. With my limited number of testing items, the only two item attributes I ever saw were the "gifted by" and "crafted by" attributes.

An item's origin does not necessarily dictate which items are displayed as non-craftable, and neither does the existence of those two flags (whatever they might be). Based on everything we've discussed, I can't help but feel like there is simply a list of items which are destined to display as non-craftable when they have a purchased origin. Unfortunately, no such list exists within the schema, and items capable of displaying "not usable in crafting" don't line up with the items which are listed for sale in the store at the GetAssetPrices endpoint.

I give up. Maybe in a few months I'll give it another swing. Thank you for your patience and, as always, your endless insight.

Link to comment
Share on other sites

3 minutes ago, Dr. McKay said:

All item attributes are visible in the data received from the GC. They're in the attributes array.

My mistake, I spoke too hastily. It looks like some attributes are given by the GC per item but some attributes are simply looked up in the schema based on the item's defindex. The GetPlayerItems endpoint redundantly provides both while the GC only provides attributes which are unique to that item instance. That should include the attributes you mentioned, but I haven't encountered them or any others except the "crafted by", "gifted by", and "gifted at" attributes.

In which case, literally the only other thing missing from the GC which is present in the api response is the flag_cannot_craft and flag_cannot_trade. The API will also resolve the username of a gifter or crafter, so I guess whatever magic is happening on the client to determine craftability also happens behind the scenes with the API before the response is sent.

Link to comment
Share on other sites

You got me curious, so I dumped my backpack and checked out some craftable and uncraftable items.

  • Tour of Duty Ticket
    • Craftable: Origin 8 (Found in Crate)
    • Uncraftable: Origin 2 (Purchased), flags +16 (unknown flag)
  • Killstreak Kits
    • Inherit "never craftable" attribute from their schema definitions
  • Uncraftable weapon
    • Origin 2 (Purchased), same flags as craftable ToD ticket (4)
  • World Traveler's Hat
    • Inherits "cannot trade" attribute from its schema definition
    • Flags 16
    • Origin 5 (Store Promotion)
  • Sir Hootsalot
    • Flags 0
    • Origin 12 (Halloween Drop)
    • Has "never craftable" attribute on the item
  • Mann Co. Cap
    • Flags 0
    • Origin 5 (Store Promotion)
    • Inherits "cannot trade" attribute from its schema definition
  • Gun Mettle Campaign Coin
    • This one is weird. I haven't found a way to determine that it's uncraftable using only the schema from the WebAPI and the GC data
    • Flags 0
    • Origin 0 (Timed Drop)
    • The only attributes listed in GetSchemaItems are kill eater attributes
    • items_game.txt also includes static_attrs for this defindex, which contains "never craftable" and "cannot trade", among other things
    • I also went back to check if the Mann Co. Cap also includes "never craftable" in items_game.txt, but it only has "cannot trade". My suspicion is that origin 5 (Store Promotion) also makes items uncraftable. I only have 3 items with this origin (World Traveler's Hat, Mann Co. Cap, Mann Co. Online Cap) and all are uncraftable.
  • Pyrovision Goggles
    • I don't have a tradable pair to compare with, but the only identifying characteristic in the GC backpack data is origin 1 (Achievement). Flags are 0 and there are no attributes, and it shares the same defindex as a tradable pair of goggles. So as far as I'm concerned, that's proof that origin can determine tradability or craftability.
  • SpaceChem Pin
    • My instance has the "cannot trade" attribute, but the schema definition has the "always tradable" attribute, and my item is tradable. It seems that "always tradable" overrides the "cannot trade" attribute.
    • I suspect that "always tradable" would also override the Achievement origin, but I can't confirm. The Ghastlierest Gibus has the always tradable attribute, and that's an achievement item. I wouldn't be surprised if the always tradable attribute was added specifically for the Ghastly Gibus Grab achievement.

Notably, some item have a capability can_craft_if_purchased, which backs up my suspicion that items with origin 2 (Purchased) are uncraftable unless their schema definition defines otherwise.

 

24 minutes ago, KTVS said:

so I guess whatever magic is happening on the client to determine craftability also happens behind the scenes with the API before the response is sent

Yeah, I believe this is what's happening. There are flags that determine if items are untradable or uncraftable, but the WebAPI also resolves the various other conditions that make an item untradable and stuffs them into those props as well.

For the most part, the only attributes that are sent in your actual backpack data from the GC are those that are specific to an item instance. That would be stuff like maker's mark (crafter name), gifter name, craft index, kill eater (strange kills), tradable after date, stuff like that.

Edited by Dr. McKay
Link to comment
Share on other sites

That's hugely helpful; you certainly have a more diverse backpack to test with than I do. I had missed the can_craft_if_purchased attribute in the schema, which would explain the inconsistency I saw a while ago with items with a purchased origin.

Unfortunately though, still no explanation as to why Mann Co. Supply Crate Keys remain craftable despite being purchased. Now I just assume it's something like "if(item in some_secret_list_of_very_special_boys){ craftable = true; }

I also just got a preview item from the store to check out the GC "Preview" flag, but the entire flag value is still 0.

🙃

A mess indeed.

Link to comment
Share on other sites

Maybe items with craft_class "tool" are always craftable. I don't remember seeing any uncraftable name tags or decal tools, for example.

Also, it's probably worth mentioning that I have a recollection of seeing the NotEcon flag on contracts. Contracts are sent to the game client as CSOEconItems (because adding another SO type for contracts would be way too logical, and we can't have that), but they have the NotEcon flag which hides them from the backpack and other econ views. I could be mistaken on this, but I don't think so. I don't recall ever seeing NotEcon anywhere else.

Based on this minimal research, here is my best (likely incorrect) guess at pseudocode to match Valve's actual code:

function IsItemCraftable(EconItem item) {
	if (item.flags & NotEcon) {
		return false;
	}
	
	if (item.schemaDefinition.craft_class == "tool") {
		return true;
	}
	
	if (item.hasAttribute("never craftable")) {
		return false;
	}
	
	if (
		(item.origin == Purchased || item.origin == StorePromotion)
		&& !item.schemaDefinition.capabilities.can_craft_if_purchased
	) {
		return false;
	}
	
	if (item.flags & CannotCraft) {
		return false;
	}
	
	return true;
}

function IsItemTradable(EconItem item) {
	if (item.flags & NotEcon) {
		return false;
	}
	
	if (item.flags & CannotTrade) {
		return false;
	}
	
	if (item.hasAttribute("tradable after date") && item.getAttribute("tradable after date") > time()) 
		return false;
	}
	
	if (item.hasAttribute("always tradable")) {
		return true;
	}
	
	if (item.hasAttribute("cannot trade")) {
		return false;
	}
	
	if (item.origin == Achievement) {
		return false;
	}
	
	return true;
}

(hasAttribute and getAttribute also check the schema definition if the appropriate attribute is not present on the CSOEconItem instance)

Edited by Dr. McKay
Link to comment
Share on other sites

Alas, non-craftable tools do exist. That was one of my first thoughts too, but Name Tags, Description Tags, Decal Tools, paint cans, and even the special variants of the crate keys all come in non-craftable flavors.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...