SwiftlyS2
Development

Steamworks

SwiftlyS2 includes server-side Steam API bindings under SwiftlyS2.Shared.SteamAPI.

This page focuses on practical game-server workflows: Steam identity, authorization signals, server metadata, Workshop downloads, and async call results.

Getting Started

Import the Steam API namespace in your plugin code:

using SwiftlyS2.Shared.SteamAPI;

Steam APIs are exposed as static classes, including:

  • SteamGameServer
  • SteamGameServerUtils
  • SteamGameServerUGC
  • SteamGameServerStats

Detecting Steam API Activation

Use Core.Event.OnSteamAPIActivated to run logic when the Steam API is ready.

private EventDelegates.OnSteamAPIActivated? _onSteamApiActivated;

public override void Load(bool hotReload)
{
    _onSteamApiActivated = OnSteamApiActivated;
    Core.Event.OnSteamAPIActivated += _onSteamApiActivated;
}

public override void Unload()
{
    if (_onSteamApiActivated != null)
    {
        Core.Event.OnSteamAPIActivated -= _onSteamApiActivated;
    }
}

private void OnSteamApiActivated()
{
    AppId_t appId = SteamGameServerUtils.GetAppID();
    Core.Logger.LogInformation("Steam API activated. AppId={AppId}", appId.m_AppId);
}

Working with Steam IDs

Convert framework player IDs (ulong) into Steam API IDs (CSteamID) before Steam calls.

IPlayer? player = Core.PlayerManager.GetPlayer(playerId);
if (player == null)
{
    return;
}

CSteamID steamId = new CSteamID(player.SteamID);

if (!steamId.IsValid() || !steamId.BIndividualAccount())
{
    Core.Logger.LogWarning("Invalid Steam ID for playerId={PlayerId}", playerId);
    return;
}

ulong steamId64 = steamId.m_SteamID;
AccountID_t accountId = steamId.GetAccountID();

Steam Authorization Signals

Swiftly core events expose Steam authorization success and failure:

  • Core.Event.OnClientSteamAuthorize
  • Core.Event.OnClientSteamAuthorizeFail
Core.Event.OnClientSteamAuthorize += @event =>
{
    Core.Logger.LogInformation("Steam authorize success for playerId={PlayerId}", @event.PlayerId);
};

Core.Event.OnClientSteamAuthorizeFail += @event =>
{
    Core.Logger.LogWarning("Steam authorize failed for playerId={PlayerId}", @event.PlayerId);
};

Server Status and Metadata

Use SteamGameServer to set server browser metadata and inspect Steam connection state.

using System.Net;

SteamGameServer.SetServerName("My Swiftly Server");
SteamGameServer.SetMapName("de_dust2");
SteamGameServer.SetMaxPlayerCount(32);
SteamGameServer.SetPasswordProtected(false);
SteamGameServer.SetGameTags("swiftly,casual");
SteamGameServer.SetGameData("region:EU;mode:retakes");

bool loggedOn = SteamGameServer.BLoggedOn();
bool secure = SteamGameServer.BSecure();

CSteamID serverSteamId = SteamGameServer.GetSteamID();
IPAddress publicIp = SteamGameServer.GetPublicIP().ToIPAddress();

Ownership and License Checks

Check whether a player owns the target app or DLC with UserHasLicenseForApp.

CSteamID steamId = new CSteamID(player.SteamID);
AppId_t appId = new AppId_t(730);

EUserHasLicenseForAppResult license = SteamGameServer.UserHasLicenseForApp(steamId, appId);

if (license == EUserHasLicenseForAppResult.k_EUserHasLicenseResultHasLicense)
{
    Core.Logger.LogInformation("Player owns app {AppId}", appId.m_AppId);
}
else
{
    Core.Logger.LogWarning("Player missing license or auth for app {AppId}: {Result}", appId.m_AppId, license);
}

Workshop Downloads with SteamGameServerUGC

Use SteamGameServerUGC (not SteamUGC) for Workshop operations in this API surface.

PublishedFileId_t fileId = new PublishedFileId_t(3070212801);

EItemState state = (EItemState)SteamGameServerUGC.GetItemState(fileId);
if ((state & EItemState.k_EItemStateInstalled) != 0)
{
    Core.Logger.LogInformation("Workshop item already installed: {ItemId}", fileId.m_PublishedFileId);
}
else
{
    bool started = SteamGameServerUGC.DownloadItem(fileId, true);
    Core.Logger.LogInformation("Workshop download start for {ItemId}: {Started}", fileId.m_PublishedFileId, started);
}

Track completion through DownloadItemResult_t:

private Callback<DownloadItemResult_t>? _downloadItemResult;

public override void Load(bool hotReload)
{
    _downloadItemResult = Callback<DownloadItemResult_t>.Create(OnDownloadItemResult);
}

public override void Unload()
{
    _downloadItemResult?.Dispose();
}

private void OnDownloadItemResult(DownloadItemResult_t callback)
{
    if (callback.m_eResult != EResult.k_EResultOK)
    {
        Core.Logger.LogWarning(
            "Workshop download failed for {ItemId}: {Result}",
            callback.m_nPublishedFileId.m_PublishedFileId,
            callback.m_eResult);
        return;
    }

    if (SteamGameServerUGC.GetItemInstallInfo(
        callback.m_nPublishedFileId,
        out ulong sizeOnDisk,
        out string folder,
        1024,
        out uint timestamp))
    {
        Core.Logger.LogInformation(
            "Workshop item {ItemId} installed at {Folder}, size={Size}, timestamp={Timestamp}",
            callback.m_nPublishedFileId.m_PublishedFileId,
            folder,
            sizeOnDisk,
            timestamp);
    }
}

Async Call Results (SteamAPICall_t)

For async APIs that return SteamAPICall_t, use CallResult&lt;T&gt;.

private CallResult<GSStatsReceived_t>? _statsReceivedResult;

public void RequestStats(CSteamID steamId)
{
    SteamAPICall_t call = SteamGameServerStats.RequestUserStats(steamId);

    _statsReceivedResult?.Dispose();
    _statsReceivedResult = CallResult<GSStatsReceived_t>.Create(call.m_SteamAPICall, OnStatsReceived);
}

private void OnStatsReceived(GSStatsReceived_t result, bool ioFailure)
{
    if (ioFailure || result.m_eResult != EResult.k_EResultOK)
    {
        Core.Logger.LogWarning(
            "Stats request failed for {SteamId}. IO={IoFailure}, Result={Result}",
            result.m_steamIDUser.m_SteamID,
            ioFailure,
            result.m_eResult);
        return;
    }

    if (SteamGameServerStats.GetUserStat(result.m_steamIDUser, "total_kills", out int kills))
    {
        Core.Logger.LogInformation("Stats for {SteamId}: kills={Kills}", result.m_steamIDUser.m_SteamID, kills);
    }
}

Auth Session APIs (Preferred)

Avoid deprecated methods SendUserConnectAndAuthenticate_DEPRECATED and SendUserDisconnect_DEPRECATED. Prefer BeginAuthSession and EndAuthSession.

EBeginAuthSessionResult authResult = SteamGameServer.BeginAuthSession(ticket, ticket.Length, steamId);

if (authResult != EBeginAuthSessionResult.k_EBeginAuthSessionResultOK)
{
    Core.Logger.LogWarning("BeginAuthSession failed for {SteamId}: {Result}", steamId.m_SteamID, authResult);
    return;
}

// ... player is active ...

SteamGameServer.EndAuthSession(steamId);

API Surface Quick Reference

APIPurpose
SteamGameServerServer auth, metadata, license checks, server state
SteamGameServerUtilsUtility data (GetAppID, GetServerRealTime, GetIPCountry)
SteamGameServerUGCWorkshop query/download/install state
SteamGameServerStatsServer-managed user stats and achievements
CSteamID, AppId_t, PublishedFileId_tCore Steam identifier and value types
Callback&lt;T&gt;Register callback handlers for Steam callback structs
CallResult&lt;T&gt;Handle SteamAPICall_t async completion results

Reference

See Steam API index for all Steam types.

See SteamGameServer, SteamGameServerUtils, SteamGameServerUGC, and SteamGameServerStats.

See Callback<T> and CallResult<T> for async handling patterns.

On this page