docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Handle callbacks and call results

    Subscribe to callbacks to respond to Steamworks events and await call results to get responses to your API requests.

    Use a callback to respond to an event that the Steamworks API broadcasts and use a call result to get the response to a specific Steamworks call. For more information about how the package delivers callbacks and call results, refer to Callback delivery.

    Prerequisites

    Initialize the Steamworks API. For more information, refer to Initialize and shut down the Steamworks API.

    Subscribe to callbacks

    To subscribe to a callback:

    1. Call SteamCallbacks.Subscribe with the callback struct type and the handler method.
    2. (Optional) To receive events from a game server, pass gameServer: true in your subscription request.
    3. When you no longer need the events, call SteamCallbacks.Unsubscribe with the same callback struct type and handler.
    Note

    If you subscribed with gameServer: true, pass gameServer: true to SteamCallbacks.Unsubscribe as well. If you omit it, SteamCallbacks.Unsubscribe only looks for the handler on the client's queue, and returns false.

    You can subscribe and unsubscribe at any point per your application needs, for example when your application initializes and when it unloads. You can use OnEnable and Start, or any other suitable points in your application.

    using UnityEngine;
    using Unity.Steamworks.Interop;
    
    public class FriendStatusWatcher : MonoBehaviour
    {
        void OnEnable()
        {
            SteamCallbacks.Subscribe<PersonaStateChange_t>(OnPersonaStateChange);
        }
    
        void OnDisable()
        {
            SteamCallbacks.Unsubscribe<PersonaStateChange_t>(OnPersonaStateChange);
        }
    
        void OnPersonaStateChange(PersonaStateChange_t data)
        {
            var friend = new CSteamID(data.m_ulSteamID);
            Debug.Log($"{SteamFriends.GetFriendPersonaName(friend)} changed: {data.m_nChangeFlags}");
        }
    }
    

    If you don't unsubscribe, the handler continues to run each time the callback triggers.

    If you initialize the Steamworks API on the main thread, handler methods also run on the main thread. This means you can interact directly with scene elements, such as GameObjects and UI elements. By default, a background thread polls the Steamworks API for callbacks and copies the data of each callback. This ensures that the data remains valid for the entire duration of your handler.

    Await call results

    A call result is a response to an asynchronous Steamworks call, identified by a SteamAPICall_t handle.

    To await a call result:

    1. Call SteamCallbacks.CallResultAsync, with the call result struct type, such as NumberOfCurrentPlayers_t.
    2. Pass a lambda expression that calls the Steamworks method. For example, () => SteamUserStats.GetNumberOfCurrentPlayers().
    3. Await the call result using the standard C# await keyword, as demonstrated in the following example.
    var players = await SteamCallbacks.CallResultAsync<NumberOfCurrentPlayers_t>( () => SteamUserStats.GetNumberOfCurrentPlayers());
    
    if (players.m_bSuccess != 0)
        Debug.Log($"{players.m_cPlayers} players online");
    

    Almost every method that returns a call result has an Async version that starts the request and lets you await the result, so you don't need to call SteamCallbacks.CallResultAsync. SteamHTTP.SendHTTPRequest and SteamHTTP.SendHTTPRequestAndStreamResponse have no Async version.

    For example, the following code is equivalent to the previous example.

    // The Async version starts the request and awaits its call result, the same as the previous example for CallResultAsync.
    
    var players = await SteamUserStats.GetNumberOfCurrentPlayersAsync();
    

    To await SendHTTPRequest and SendHTTPRequestAndStreamResponse, which have no Async version, pass them to CallResultAsync:

    var completed = await SteamCallbacks.CallResultAsync<HTTPRequestCompleted_t>(() => { SteamHTTP.SendHTTPRequest(h, out var c); return c; });
    
    var streamed = await SteamCallbacks.CallResultAsync<HTTPRequestCompleted_t>(() => { SteamHTTP.SendHTTPRequestAndStreamResponse(h, out var c); return c; });
    

    When you await a call result, an exception or a cancellation can interrupt a Task in the following scenarios:

    • If the Steamworks API can't start the request, for example, when the method returns k_uAPICallInvalid, CallResultAsync throws a SteamCallResultException.
    • If the Steamworks API reports an IO failure, the Task throws a SteamCallResultException.
    • If a Steam pipe your call uses shuts down, the package cancels the Task and awaiting it throws a TaskCanceledException. A pipe is your application's connection to the Steamworks API. The client pipe goes through the Steam client. The game-server pipe connects to Steam's servers directly, without the Steam client. The client application and game server operate on separate pipes. Shutting down one pipe doesn't cancel or disturb tasks that are awaiting responses on the other.

    Even when the Task completes without an exception, the request can still fail. Check the field that reports the outcome in the returned struct, such as m_bSuccess or m_eResult.

    Awaiting a call result has no timeout. If the result never arrives, the Task never completes.

    Additional resources

    • Initialize and shut down the Steamworks API
    • Choose a callback dispatch mode
    • Steam Game Servers (Valve)
    • Callbacks (Valve)
    • CallResults (Valve)
    • Asynchronous programming in C# (Microsoft)
    In This Article
    Back to top
    Copyright © 2026 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)