Async setting of Steam user stats / achievements? Maybe HTTP req?

The use-case is setting Steam stats (e.g. coins earned), achievements and using Steam leaderboards.

This is done at the server-side to avoid cheating. This is possible with a HTTP POST request.

function SetSteamStat(nk: nkruntime.Nakama, steamId: string, statName: string, statValue: number) {
    const url = "https://partner.steam-api.com/ISteamUserStats/SetUserStatsForGame/v1/";
    const body = {
        key: steamApiKey,
        steamid: steamId,
        appid: Number(appId),
        count: 1,
        "name[0]": statName,
        "value[0]": statValue
    };
    const formBody = Object.entries(body)
        .map(([k, v]) => encodeURIComponent(k) + "=" + encodeURIComponent(String(v)))
        .join("&");
    nk.httpRequest(url, "post", { "Content-Type": "application/x-www-form-urlencoded" }, formBody, 5000);
}

However, generally I want this request to be best-effort.

Which of these possible solutions work (do all of them work)? Which is best ranked in order?

  • Using an after hook on the RPC.
  • Or is there a way to do nk.asyncHttpRequest(...)
  • Is it safe to do setTimeout(() => nk.httpRequest(...), 0) to make it async or is that not good?

I also saw that Nakama uses a lot of the Steam API e.g. for the friend features etc, I wonder if there can be anything re-used inside Nakama to set Steam stats.

Thanks!

Hello @aDu,

In the JS runtime, it’s not possible to use `setTimeout` and other functions that are provided either by the browser or a Node environment.

If you’d like this to be a best effort, just run off the back of the standard APIs as an after-hook, or if it should run as part of a custom RPC, make it part of the RPC itself (custom RPCs don’t have before/after hooks as the custom logic can be implemented as part of the RPC itself), and wrap it in a try catch block where you simply log or ignore the error altogether.

Hope this helps.

I see, thank you. One issue is even if it’s best-effort this way, it still increases the latency of the response due to the http request being synchronous. Good to know that the hooks cannot be put in the custom RPCs (though an after-hook would’ve still been useful to make post-processing without increasing the RPC latency to the client).

After thinking, for now I can just provide a server side RPC called “RefreshSteamStats(statName)” that the Unity game/client can call asynchronously whenever the client notices their stats are not synced with their expected Steam user stats.

Another way could be to store pending Steam stat that requires setting on a storage, and having a background job to set the steam achievements.

I’m not sure if Nakama supports background jobs, but a hack would be to put it in the Afterhook of something like the chatroom or put a “fake” match.

There’s no built in background workers, but you could, as you said, use a match dedicated to flush those periodically, or if using the Go runtime, you could just have a background Goroutine that does it.

But I think your suggestion of doing it asynchronously from the client may be the simpler option, and just have the server relay it to Steam.

1 Like