Convert Figma logo to code with AI

Facepunch logoFacepunch.Steamworks

Another fucking c# Steamworks implementation

2,844
343
2,844
187

Top Related Projects

Steamworks wrapper for Unity / C#

An open-source and fully functional Steamworks SDK / API module and plug-in for the Godot Game Engine.

2,009

The new future of dotnet/reactive and UniRx.

Quick Overview

Facepunch.Steamworks is a C# wrapper for the Steamworks API, designed to make it easier for developers to integrate Steam functionality into their games and applications. It provides a more user-friendly interface to interact with Steam's features, such as achievements, leaderboards, and multiplayer networking.

Pros

  • Easy to use and well-documented API
  • Actively maintained and regularly updated
  • Supports both Unity and non-Unity projects
  • Provides a more C#-friendly approach to Steamworks integration

Cons

  • Requires Steam to be installed and running on the user's system
  • Limited to Steam platform, not suitable for multi-platform projects
  • May have a learning curve for developers new to Steam integration
  • Potential performance overhead compared to direct Steamworks API usage

Code Examples

  1. Initializing the Steam client:
using Steamworks;

try
{
    SteamClient.Init(480);
    Console.WriteLine("Steam initialized successfully!");
}
catch (System.Exception e)
{
    Console.WriteLine("Error initializing Steam: " + e.Message);
}
  1. Unlocking an achievement:
if (SteamClient.IsValid)
{
    SteamUserStats.SetAchievement("ACH_WIN_ONE_GAME");
    SteamUserStats.StoreStats();
}
  1. Fetching the player's Steam name:
if (SteamClient.IsValid)
{
    string playerName = SteamClient.Name;
    Console.WriteLine($"Welcome, {playerName}!");
}
  1. Sending a P2P packet:
using Steamworks.Data;

byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello, Steam!");
SteamNetworking.SendP2PPacket(steamId, data, data.Length, P2PSend.Reliable);

Getting Started

  1. Install the Facepunch.Steamworks NuGet package:

    dotnet add package Facepunch.Steamworks
    
  2. Initialize Steam in your application:

    using Steamworks;
    
    try
    {
        SteamClient.Init(YOUR_APP_ID);
    }
    catch (System.Exception e)
    {
        Console.WriteLine("Couldn't initialize Steam: " + e.Message);
        return;
    }
    
  3. Use Steam features in your code:

    if (SteamClient.IsValid)
    {
        Console.WriteLine($"Logged in as: {SteamClient.Name}");
        // Add your Steam-related code here
    }
    
  4. Shut down Steam when your application closes:

    SteamClient.Shutdown();
    

Competitor Comparisons

Steamworks wrapper for Unity / C#

Pros of Steamworks.NET

  • More lightweight and focused solely on Steam integration
  • Easier to use for developers who prefer a direct wrapper around the Steamworks API
  • Better documentation and examples available

Cons of Steamworks.NET

  • Less actively maintained compared to Facepunch.Steamworks
  • Fewer high-level abstractions and convenience methods
  • May require more manual work for certain Steam features

Code Comparison

Steamworks.NET:

SteamAPI.Init();
var friendCount = SteamFriends.GetFriendCount(EFriendFlags.k_EFriendFlagImmediate);
for (int i = 0; i < friendCount; i++)
{
    var friendId = SteamFriends.GetFriendByIndex(i, EFriendFlags.k_EFriendFlagImmediate);
    var friendName = SteamFriends.GetFriendPersonaName(friendId);
}

Facepunch.Steamworks:

using (var client = new SteamClient())
{
    var friends = client.Friends.AllFriends;
    foreach (var friend in friends)
    {
        Console.WriteLine($"Friend: {friend.Name}");
    }
}

The code comparison shows that Facepunch.Steamworks provides a more abstracted and easier-to-use API, while Steamworks.NET offers a more direct mapping to the underlying Steamworks API.

An open-source and fully functional Steamworks SDK / API module and plug-in for the Godot Game Engine.

Pros of GodotSteam

  • Specifically designed for the Godot game engine, providing seamless integration
  • Regularly updated with new Steamworks SDK features
  • Extensive documentation and examples tailored for Godot developers

Cons of GodotSteam

  • Limited to Godot engine, not suitable for other game development frameworks
  • Smaller community compared to Facepunch.Steamworks
  • May have a steeper learning curve for developers new to Godot

Code Comparison

GodotSteam:

var steam = Engine.get_singleton("Steam")
steam.steamInit()
var user_name = steam.getPersonaName()

Facepunch.Steamworks:

using Steamworks;
SteamClient.Init(480);
var userName = SteamClient.Name;

Both libraries provide similar functionality, but GodotSteam uses GDScript and is tailored for Godot's architecture, while Facepunch.Steamworks uses C# and is more versatile across different game engines and frameworks.

2,009

The new future of dotnet/reactive and UniRx.

Pros of R3

  • Focuses on reactive programming and asynchronous operations
  • Provides a more comprehensive set of reactive extensions
  • Offers better performance for complex reactive scenarios

Cons of R3

  • Steeper learning curve for developers new to reactive programming
  • May be overkill for simple Steam integration tasks
  • Less focused on Steam-specific functionality

Code Comparison

R3:

var observable = Observable.Interval(TimeSpan.FromSeconds(1))
    .Take(5)
    .Select(x => x * 2);
observable.Subscribe(Console.WriteLine);

Facepunch.Steamworks:

using Steamworks;
if (SteamClient.IsValid)
{
    Console.WriteLine(SteamClient.Name);
}

Summary

R3 is a powerful reactive programming library, while Facepunch.Steamworks is specifically designed for Steam integration. R3 offers more flexibility for complex asynchronous operations but may be more challenging to learn. Facepunch.Steamworks provides a straightforward approach to Steam-related tasks but is limited in scope compared to R3's broader reactive capabilities. The choice between the two depends on the project's specific requirements and the development team's familiarity with reactive programming concepts.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Facepunch.Steamworks

Another fucking c# Steamworks implementation

Build All

Features

FeatureSupported
Windows✔
Linux✔
MacOS✔
Unity Support✔
Unity IL2CPP Support✔
Async Callbacks (steam callresults)✔
Events (steam callbacks)✔
Single C# dll (no native requirements apart from Steam)✔
Open Source✔
MIT license✔
Any 32bit OS✔

Why

The Steamworks C# implementations I found that were compatible with Unity have worked for a long time. But I hate them all. For a number of different reasons.

  • They're not C#, they're just a collection of functions.
  • They're not up to date.
  • They require a 3rd party native dll.
  • They can't be compiled into a standalone dll (in Unity).
  • They're not free
  • They have a restrictive license.

C# is meant to make things easier. So lets try to wrap it up in a way that makes it all easier.

What

Get your own information

    SteamClient.SteamId // Your SteamId
    SteamClient.Name // Your Name

View your friends list

foreach ( var friend in SteamFriends.GetFriends() )
{
    Console.WriteLine( $"{friend.Id}: {friend.Name}" );
    Console.WriteLine( $"{friend.IsOnline} / {friend.SteamLevel}" );
    
    friend.SendMessage( "Hello Friend" );
}

App Info

    Console.WriteLine( SteamApps.GameLanguage ); // Print the current game language
    var installDir = SteamApps.AppInstallDir( 4000 ); // Get the path to the Garry's Mod install folder

    var fileinfo = await SteamApps.GetFileDetailsAsync( "hl2.exe" ); // async get file details
    DoSomething( fileinfo.SizeInBytes, fileinfo.Sha1 );

Get Avatars

    var image = await SteamFriends.GetLargeAvatarAsync( steamid );
    if ( !image.HasValue ) return DefaultImage;

    return MakeTextureFromRGBA( image.Value.Data, image.Value.Width, image.Value.Height );

Get a list of servers

using ( var list = new ServerList.Internet() )
{
    list.AddFilter( "map", "de_dust" );
    await list.RunQueryAsync();

    foreach ( var server in list.Responsive )
    {
        Console.WriteLine( $"{server.Address} {server.Name}" );
    }
}

Achievements

List them

    foreach ( var a in SteamUserStats.Achievements )
    {
        Console.WriteLine( $"{a.Name} ({a.State})" );
    }	

Unlock them

    var ach = new Achievement( "GM_PLAYED_WITH_GARRY" );
    ach.Trigger();

Voice

    SteamUser.VoiceRecord = KeyDown( "V" );

    if ( SteamUser.HasVoiceData )
    {
        var bytesrwritten = SteamUser.ReadVoiceData( stream );
        // Send Stream Data To Server or Something
    }

Auth

    // Client sends ticket data to server somehow
    var ticket = SteamUser.GetAuthSessionTicket();

    // server listens to event
    SteamServer.OnValidateAuthTicketResponse += ( steamid, ownerid, rsponse ) =>
    {
        if ( rsponse == AuthResponse.OK )
            TellUserTheyCanBeOnServer( steamid );
        else
            KickUser( steamid );
    };

    // server gets ticket data from client, calls this function.. which either returns
    // false straight away, or will issue a TicketResponse.
    if ( !SteamServer.BeginAuthSession( ticketData, clientSteamId ) )
    {
        KickUser( clientSteamId );
    }

    //
    // Client is leaving, cancels their ticket OnValidateAuth is called on the server again
    // this time with AuthResponse.AuthTicketCanceled
    //
    ticket.Cancel();

Utils

    SteamUtils.SecondsSinceAppActive;
    SteamUtils.SecondsSinceComputerActive;
    SteamUtils.IpCountry;
    SteamUtils.UsingBatteryPower;
    SteamUtils.CurrentBatteryPower;
    SteamUtils.AppId;
    SteamUtils.IsOverlayEnabled;
    SteamUtils.IsSteamRunningInVR;
    SteamUtils.IsSteamInBigPictureMode;

Workshop

Download a workshop item by ID

    SteamUGC.Download( 1717844711 );

Get a workshop item information

    var itemInfo = await Ugc.Item.Get( 1720164672 );

    Console.WriteLine( $"Title: {itemInfo?.Title}" );
    Console.WriteLine( $"IsInstalled: {itemInfo?.IsInstalled}" );
    Console.WriteLine( $"IsDownloading: {itemInfo?.IsDownloading}" );
    Console.WriteLine( $"IsDownloadPending: {itemInfo?.IsDownloadPending}" );
    Console.WriteLine( $"IsSubscribed: {itemInfo?.IsSubscribed}" );
    Console.WriteLine( $"NeedsUpdate: {itemInfo?.NeedsUpdate}" );
    Console.WriteLine( $"Description: {itemInfo?.Description}" );

Query a list of workshop items

    var q = Ugc.Query.All
                    .WithTag( "Fun" )
                    .WithTag( "Movie" )
                    .MatchAllTags();

    var result = await q.GetPageAsync( 1 );

    Console.WriteLine( $"ResultCount: {result?.ResultCount}" );
    Console.WriteLine( $"TotalCount: {result?.TotalCount}" );

    foreach ( Ugc.Item entry in result.Value.Entries )
    {
        Console.WriteLine( $"{entry.Title}" );
    }

Query items created by friends

    var q = Ugc.UserQuery.All
                        .CreatedByFriends();

Query items created by yourself

    var q = Ugc.UserQuery.All
                        .FromSelf();

Publish your own file

    var result = await Ugc.Editor.NewCommunityFile
                      .WithTitle( "My New FIle" )
                      .WithDescription( "This is a description" )
                      .WithContent( "c:/folder/addon/location" )
                      .WithTag( "awesome" )
                      .WithTag( "small" )
                      .SubmitAsync( iProgressBar );

Steam Cloud

Write a cloud file

    SteamRemoteStorage.FileWrite( "file.txt", fileContents );

Read a cloud file

    var fileContents = SteamRemoteStorage.FileRead( "file.txt" );

List all files

    foreach ( var file in SteamRemoteStorage.Files )
    {
        Console.WriteLine( $"{file} ({SteamRemoteStorage.FileSize(file)} {SteamRemoteStorage.FileTime( file )})" );
    }

Steam Inventory

Get item definitions

    foreach ( InventoryDef def in SteamInventory.Definitions )
    {
        Console.WriteLine( $"{def.Name}" );
    }

Get items that are for sale in the item shop

    var defs = await SteamInventory.GetDefinitionsWithPricesAsync();

    foreach ( var def in defs )
    {
        Console.WriteLine( $"{def.Name} [{def.LocalPriceFormatted}]" );
    }

Get a list of your items

    var result = await SteamInventory.GetItems();

    // result is disposable, good manners to dispose after use
    using ( result )
    {
        var items = result?.GetItems( bWithProperties );

        foreach ( InventoryItem item in items )
        {
            Console.WriteLine( $"{item.Id} / {item.Quantity} / {item.Def.Name} " );
        }
    }

Getting Started

Client

To initialize a client you can do this.

using Steamworks;

// ...

try 
{
    SteamClient.Init( 4000 );
}
catch ( System.Exception e )
{
    // Couldn't init for some reason (steam is closed etc)
}

Replace 4000 with the appid of your game. You shouldn't call any Steam functions before you initialize.

When you're done, when you're closing your game, just shutdown.

SteamClient.Shutdown();

Server

To create a server do this.

var serverInit = new SteamServerInit( "gmod", "Garry Mode" )
{
    GamePort = 28015,
    Secure = true,
    QueryPort = 28016
};

try
{
    Steamworks.SteamServer.Init( 4000, serverInit );
}
catch ( System.Exception )
{
    // Couldn't init for some reason (dll errors, blocked ports)
}

Help

Wanna help? Go for it, pull requests, bug reports, yes, do it.

You can also hit up the Steamworks Thread for help/discussion.

We also have a wiki you can read and help fill out with examples and advice.

License

MIT - do whatever you want.