Choose a callback dispatch mode
Select a dispatch mode to allow the package or your application code to read the callback queue.
A dispatch mode determines whether the package or your application code reads callbacks and call results from the callback queue. By default, the package reads the queue on the background thread and delivers each message to your handlers with SteamCallbacks.
If you want your application code to read the queue, select a different dispatch mode when you initialize the Steamworks API. To select a callback dispatch mode, pass a SteamDispatchMode value to SteamAPI.InitEx, or GameServer.InitEx.
For more information on how the package reads and delivers messages, refer to Callback delivery.
Compare dispatch modes
The following table shows whether the package or your application code reads the callback queue in each dispatch mode and whether you can use SteamCallbacks to subscribe to callbacks and await call results.
| Dispatch mode | Who reads the message queue | Subscribe and await with SteamCallbacks | When to use |
|---|---|---|---|
Package (default) |
The package, on a background thread | Supported | Most applications. The package reads the message queue and delivers callbacks and call results on the main thread. You don't need to write code to read callbacks. The Platform Toolkit package for Steamworks uses this mode. |
ExternalPoll |
Your application code, when it calls SteamCallbacks.RunFrameCallbacks or SteamCallbacks.SubmitCallback |
Supported | When you need to control when callbacks run, such as at a fixed point in your game loop or at a regular interval on a server while still using Subscribe, CallResultAsync and the Async methods. |
External |
Your application code, through its own bindings to the Steamworks SDK | Not supported. Subscribe throws exceptions, and awaiting an Async method fails with an InvalidOperationException. |
When another library or your own code already handles Steamworks callbacks through its own bindings, and you only want to use this package to call the Steamworks API. |
Read callbacks at your own schedule
Use ExternalPoll mode to read callbacks at a time that you choose, such as in your own update loop or at a regular interval. You can still use Subscribe, CallResultAsync, and the Async methods.
In ExternalPoll mode, you can read callbacks in one of two ways:
- Call
SteamCallbacks.RunFrameCallbacksand the package delivers the callbacks. - Read the messages with your own bindings and pass each message to
SteamCallbacks.SubmitCallback.
To read callbacks on your own schedule:
- Initialize the Steamworks API with
SteamDispatchMode.ExternalPoll. - Call
SteamCallbacks.RunFrameCallbackswhere you want to read callbacks.
SteamAPI.InitEx(out var errMsg, SteamDispatchMode.ExternalPoll);
...
// Call this wherever you want to read callbacks
SteamCallbacks.RunFrameCallbacks();
If you call RunFrameCallbacks on the main thread, the handlers run before the method returns. If you call it from another thread, the handlers run later on the main thread. To run the handlers immediately on the calling thread, call SteamCallbacks.DeliverQueuedCallbacks.
If you already read the callback queue with your own bindings, pass each message to SubmitCallback instead. Include SteamAPICallCompleted_t messages, because they complete an awaited call result.
while (SteamAPI_ManualDispatch_GetNextCallback(pipe, ref msg))
{
// SteamCallbacks copies the payload
SteamCallbacks.SubmitCallback(pipe, in msg);
SteamAPI_ManualDispatch_FreeLastCallback(pipe);
}
Note
Use either RunFrameCallbacks or your own read loop to read the callback queue, not both. Each pipe allows only one of them to read it.
Handle callbacks in your own code
To handle callbacks in your own code, pass SteamDispatchMode.External to SteamAPI.InitEx or GameServer.InitEx.
The package sets up the interface pointers, which are the addresses of the native Steamworks interface, such as ISteamUser, that the package's C# classes use to call the Steamworks API. The package doesn't start a background thread or call SteamAPI_ManualDispatch_Init. Because the package never receives callbacks, the following methods throw an exception for the client and game server:
SubscribeOnCallbackOnGameServerCallbackCallResultAsync- Every
Asyncmethod
Dispatch modes for client and game server
Set a dispatch mode separately for the client and the game server. Use SteamAPI.InitEx to set the client mode and GameServer.InitEx for the game server. RunFrameCallbacks, SubmitCallback, and DeliverQueuedCallbacks accept the same gameServer parameter as the other SteamCallbacks methods.
Don't combine External with Package or ExternalPoll across the client and the game server. The client and the game server each have their own Steamworks API pipe.
The package receives callbacks through manual dispatch in the Steamworks API. In manual dispatch, the package or your application code fetches callbacks from the callback queue one at a time, instead of SteamAPI_RunCallbacks calling registered handlers.
The dispatch modes differ in how they use manual dispatch:
PackageandExternalPoll: These modes switch the whole application to manual dispatch.External: This mode doesn't switch the application to manual dispatch. Your code reads the callbacks itself, either by callingSteamAPI_RunCallbacksor by enabling manual dispatch and reading the queue.SteamCallbackssubscription methods andCallResultAsyncthrow exceptions.
When you use both the client and the game server, the following combinations of dispatch modes are valid:
- Client and game server both use
External. - Client and game server both use
PackageorExternalPollin any combination.
InitEx throws an exception before it initializes, if one of the client and the game server uses External and the other doesn't. To avoid the exception, use External for both the client and the game server, or for neither. You can combine Package and ExternalPoll.