Platform-specific reference
Details for behavior and API usage unique to the Platform Toolkit Steamworks package.
Note
Refer to Get started with Platform Toolkit for Steamworks for detailed setup instructions.
Steam achievement system behavior
The Platform Toolkit package doesn't support the Steamworks Stats API. This causes limited behavior for progressive achievements. Attempts to set a progressive achievement to a value lower than its target value are ignored. To unlock a progressive achievement, the value must be set to the target value.
Steam saving system
The Steamworks Platform Toolkit saving system requires Steam Cloud to store save data. Ensure Steam Cloud is configured correctly by following the Initial Setup guide.
When saving data via Platform Toolkit, the following limitations apply:
- Because the Steam saving system uses both archives and Steam file APIs, it doesn't support streamed archive operations like the local saving system. You must fully load the data into memory for read and write operations.
- Steam Cloud sets a maximum size for a single save as 100 MiB. This is equal to
100 × 1024 × 1024bytes, referenced in the Steamworks k_unMaxCloudFileChunkSize documentation. This limitation applies to the save itself as save files are written as a single archive file.
Dispatch loops
Platform Toolkit manages the Steam dispatch loop internally. Don't call SteamAPI_ManualDispatch_RunFrame, SteamAPI_ManualDispatch_GetNextCallback, SteamAPI_ManualDispatch_FreeLastCallback, or any other dispatch loop function. Running a second loop in parallel creates a race condition where either loop can consume a callback or call result first. This makes delivery unpredictable and can cause both systems to behave incorrectly.
Steam callbacks and call results
Platform Toolkit handles several common Steam callbacks internally, including avatar loading, persona state changes, and stats updates. If you need access to callbacks or asynchronous call results that Platform Toolkit doesn't expose directly, use SteamCallbacks.
Define Steamworks structs
Both OnCallback and GetCallResult<T> require you to define your own structs that match the Steamworks callback or call result layout. Refer to the Steamworks API reference for the fields of each struct. Use [StructLayout(LayoutKind.Sequential)] or [StructLayout(LayoutKind.Explicit)] to match the struct layout of the Steamworks SDK. Use a pack size of 4 if targeting macOS and pack size 8 for Windows. These sizes match the VALVE_CALLBACK_PACK_SMALL and VALVE_CALLBACK_PACK_LARGE definitions in the Steamworks SDK headers.
For example, PersonaStateChange_t is defined as:
// Use pack = 4 on macOS; pack = 8 on Windows.
[StructLayout(LayoutKind.Sequential, Pack = 8)]
struct PersonaStateChange_t
{
public const int k_iCallback = 304;
public ulong m_ulSteamID;
public int m_nChangeFlags;
}
Listen for callbacks
Subscribe to SteamCallbacks.OnCallback to receive all Steam callbacks (excluding CallResults). Filter by m_iCallback and deserialize with GetMsg<T>(). For example:
SteamCallbacks.OnCallback += (msg) =>
{
if (msg.m_iCallback != PersonaStateChange_t.k_iCallback) return;
var change = msg.GetMsg<PersonaStateChange_t>();
};
Warning
SteamCallbackMsg contains a native pointer that's valid only for the lifetime of the OnCallback handler. Don't store the struct or call GetMsg<T>() after the handler returns. The handler must not be async, because any await might resume execution after the native pointer has been freed.
Await async call results
Use SteamCallbacks.GetCallResult<T> for Steam APIs that return a SteamAPICall_t. The type parameter T must be the Steamworks call result struct that corresponds to the API you're calling. Pass a method that invokes the Steamworks API and returns the SteamAPICall_t handle value.
For example, using NumberOfCurrentPlayers_t:
// Use pack = 4 on macOS; pack = 8 on Windows.
[StructLayout(LayoutKind.Sequential, Pack = PackSize)]
struct NumberOfCurrentPlayers_t
{
public const int k_iCallback = 1107;
public byte m_bSuccess;
public int m_cPlayers;
}
[DllImport(k_SteamLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SteamAPI_SteamUserStats_v013();
[DllImport(k_SteamLibrary, CallingConvention = CallingConvention.Cdecl)]
private static extern ulong SteamAPI_ISteamUserStats_GetNumberOfCurrentPlayers(IntPtr self);
//...
var result = await SteamCallbacks.GetCallResult<NumberOfCurrentPlayers_t>(
() => SteamAPI_ISteamUserStats_GetNumberOfCurrentPlayers(SteamAPI_SteamUserStats_v013()));
Note
PlatformToolkit.Initialize() must complete before using either API.