SteamRE / SteamKit

SteamKit2 is a .NET library designed to interoperate with Valve's Steam network. It aims to provide a simple, yet extensible, interface to perform various actions on the network.
GNU Lesser General Public License v2.1
2.63k stars 497 forks source link

Weird behaviour accessing KeyValues from WebAPI #594

Closed JustArchi closed 6 years ago

JustArchi commented 6 years ago

Consider following code:

KeyValue response;

using (dynamic iSteamApps = WebAPI.GetAsyncInterface("ISteamApps")) {
    try {
        response = await iSteamApps.GetAppList2(secure: true);
    } catch {
        // Do something with failure
        return;
    }
}

if (response == null) {
    // Do something with failure
    return;
}

List<KeyValue> apps = response["applist"]["apps"].Children;
// Do something with apps

GetAppList2() accesses this standard web API endpoint available without API key.

Until recently everything worked properly with this code and apps was a list composed out of 60k+ KeyValues that represented apps on Steam. However, right now I'm getting an empty list in its place.

Logically first thing was checking API endpoint if perhaps something changed but no, the structure is exactly the same, so there is no reason why this code should fail. After digging deeper and trying various different combinations I've achieved what I had initially by removing applist entirely and calling apps directly:

List<KeyValue> apps = response["apps"].Children;
// This makes no sense but works (?)

image

I don't know if this is intended or some bug in KV that got triggered by some specific VDF output (maybe size of it? Name of some app?), but I'm 100% sure that this is breaking behaviour as it used to work just fine in the past.

I've reproduced it on latest stable SK2 2.1.0 as well as master as of today.

Please take a look, I'm pretty sure that this is unwanted behaviour.

yaakov-h commented 6 years ago

Until recently everything worked properly

How recently?

Also, are you certain that the VDF structure (in particular) of the WebAPI response has not changed?

JustArchi commented 6 years ago

I can't say I'm certain because I don't know when it broke exactly but I'm certain that this code has worked when I wrote it for the first time. I mean, look at the response yourself, there is no reason why it shouldn't work, applist is a valid key value there, it's even more visible in the json format. If you asked me where the bug is I'd say "somewhere in the parser", but this is a blind guess based on analyzing the response SK2 is receiving versus the object I'm creating from that response.

I think Valve wouldn't do breaking change over VDF format in public API, would they?

JustArchi commented 6 years ago

A custom call requesting app list in JSON properly deserializes to object that the request returns, so JSON deserialization works alright but this doesn't offer us any help in this case. I verified that the VDF response that API returns is in expected (and unchanged) structure since the moment I've started using this endpoint for the first time, and applist exists there as a first KV object within a response. I've briefly checked SK2 code and I also don't see anything specific there, I'll do a bit more digging and maybe find out why exactly it stopped working the way it should, assuming you won't be the first 🙂

For now I'm certain that .NET Core doesn't have anything to say here, I've reproduced it in netcoreapp 2.1, 2.0 and net 472, so at least that part is out of the picture.

JustArchi commented 6 years ago

I've digged a bit deeper, compared various API endpoints and it might be a Valve fuckup after all.

https://api.steampowered.com/ISteamDirectory/GetSteamPipeDomains/v1?format=vdf

"response"
{
    "domainlist"
    {
        "0" "*.steamcontent.com"
        "1" "cs.steampowered.com"
        ...
    }
    "result"    "1"
    "message"   ""
}

https://api.steampowered.com/ISteamDirectory/GetCMList/v1?cellid=0&format=vdf

"response"
{
    "serverlist"
    {
        "0" "155.133.230.34:27017"
        "1" "155.133.230.34:27019"
        ...
    }
    "serverlist_websockets"
    {
        "0" "CM-02-WAW1.cm.steampowered.com:27021"
        "1" "CM-01-WAW1.cm.steampowered.com:443"
        ...
    }
    "result"    "1"
    "message"   ""
}

Basically all API endpoints return response object which is a root for anything deeper.

EXCEPT our black sheep that returns:

"applist"
{
    "apps"
    {
        "0"
        {
            "appid" "5"
            "name"  "Dedicated Server"
        }
        "1"
        {
            "appid" "7"
            "name"  "Steam Client"
        }
        ...
    }
}

I'm certain that my code has worked before, which means that either I have some memory/mental issues from coding around Steam platform or they actually did include response there and applist inside before, and no longer do.

Nothing to do with SK2, but in case somebody else stumbles upon this, feel free to let me know that I actually should cancel my visit with psychiatrist, thanks 🙂

And of course sorry for bothering you for no reason.

yaakov-h commented 6 years ago

I had a sneaky suspicion that it would be a serialization change.