Integrating Steamworks

Overview

The purpose of this guide is for game makers to integrate Steamworks IAP (in-app purchasing) into Beamable.

Integration

Beamable integrates with this technology.

1000

Valve's Steamworks is a set of tools and services that help game developers and publishers build their games and get the most out of distributing on Steam.

FeatureBeamable Support?Detail
Steamworks' Microtransactions✔️See the steps below

Game Maker User Experience

During development, the game maker's user experience is as follows:

Steps

Follow these steps to get started: This process will integrate Steamworks IAP into Beamable.

Goal: The player is able to purchase a Beamable item via Steamworks.

Step 1. Setup 3rd Party

NameDetail
1. Sign-in / Register to Steamworks• See partner.steamgames.com for more info

Note: Choose partnership type of "Game Developer or Publisher"
2. Complete Steamworks' "Getting Started"• See partner.steamgames.com/doc/gettingstarted for more info

Note: Complete all the steps
3. Capture Account DetailsLocate the following account details values which are needed for subsequent steps below...

AppId - The value is located by selecting the application on your homepage in Steamworks
Web API - The value is located at steamcommunity.com/dev/apikey

Step 2. Import SDK

NameDetail
1. Set Unity's Target Platform to Standalone • See Unity's docs.unity3d.com/Manual/BuildSettings.html for more info

Note: Steamworks for Unity works only for this target platform
2. Download Steamworks.NET's SDK• See steamworks.github.io for more info

Note: Steamworks.NET is a C# Wrapper for Valve’s Steamworks API and is completely free and open source under the permissive MIT license. It makes it easy to get started with Unity-based projects
3. Complete Steamworks.NET's "Installation"• See steamworks.github.io/installation for more info

Note: Complete all the "Unity Instructions" steps
4. Configure Steamworks' Appid

• Create/open the steam_appid.txt which now resides in the root of your Unity project and replace the contents with your own AppId value (e.g. 480)

Step 3. Setup Beamable

NameDetail
1. Complete Beamable's "Getting Started" Guide• See Getting Started for more info

Note: Complete all steps
2. Optional, Complete Beamable's "Using Store" Guide• See Stores - Guide for more info

Note: For game makers who are new to Beamable, all steps are recommended. However, advanced game makers make skip this step entirely
3. Configure Beamable's Content

• Open the Content Manager
• For each SkuContent item, populate each Product IDs → Steam value with any unique integer value desired (e.g. '1')
4. Open Beamable's Portal

• See db-portal-prod.disruptorbeam.com for more info
• Populate all values and click "Save"...

cid - See the config-defaults.txt file in Unity's Project Window
email - Use your own value
password - Use your own value

Note: The specific url above is required for this usage of Beamable Portal
5. Configure Beamable's Portal• Click "+Config" and choose "Steam"
• Populate all values and click "Save"

sandbox - Use true for development and false for production
appid - Use the Steamworks AppId from Step 1 above
key - Use the Steamworks Web API from Step 1 above

Example

Here are examples which cover common programming needs.

📘

Beamable SDK Examples

The following example code is available for download at GitHub.com/Beamable_Steamworks_Examples

Project Window

This /Steamworks/ folder demonstrates a complete integration solution including;

  1. SteamworksProvider.cs - Adds a new service to the Beamable 'ServiceManager`
  2. SteamworksService.cs - Defines the new Beamable service
  3. SteamworksExample.cs - Demonstrates successful usage of Beamable with Steamworks integration

514

Scene Hierarchy

This SteamworksExample.unity scene contains;

  • The setup for Steamworks integration
  • The Beamable Store Flow prefab
485

Code

1. SteamworksProvider

This SteamworksProvider.cs demonstrates adding a new ISteamService to the Beamable ServiceManager.

Note: Game makers can use this class as-is or as inspiration to create a custom solution.

using UnityEngine;
#if USE_STEAMWORKS
using Beamable.Common.Steam;
using Beamable.Service;
using Steamworks;
#endif

namespace Beamable.Examples.Integrations.Steamworks
{
    /// <summary>
    /// Provider for <see cref="Steamworks"/>.
    /// </summary>
    public class SteamworksProvider : MonoBehaviour
    {
        
#if USE_STEAMWORKS
        //  Unity Methods  --------------------------------
        private void Awake()
        {
            ServiceManager.ProvideWithDefaultContainer<ISteamService>(new SteamworksService());
            DontDestroyOnLoad(this.gameObject);
        }
            
        public void Start()
        {
            if(SteamManager.Initialized)
            {
                string personaName = SteamFriends.GetPersonaName();
                var appId = SteamUtils.GetAppID();
                Debug.Log($"Steam User Name = {personaName}, Steam App ID = {appId.m_AppId}");
            }
        }
#endif
        
    }
}

2. SteamworksService

This SteamworksService.cs demonstrates the implementation of the needed Beamable service.

Note: Game makers can use this class as-is.

#if USE_STEAMWORKS
using System;
using System.Collections.Generic;
using UnityEngine;
using Beamable.Common;
using Beamable.Common.Steam;
using Steamworks;

namespace Beamable.Examples.Integrations.Steamworks
{
    /// <summary>
    /// Service for <see cref="Steamworks"/>.
    /// </summary>
    public class SteamworksService : ISteamService
 
        //  Fields  ---------------------------------------
        private bool _transactionRegistered = false;
        private List<Action<SteamTransaction>> _callbacks;
        
        //  Methods  --------------------------------------
        Promise<Unit> ISteamService.RegisterAuthTicket()
        {
            var promise = new Promise<Unit>();

            if (!SteamManager.Initialized)
            {
                promise.CompleteError(new Exception("Steamworks not initialized."));
                return promise;
            }

            byte[] steamAuthTicketBuffer = new byte[1024];
            uint steamAuthTicketBufferSize = 1024;

            Callback<GetAuthSessionTicketResponse_t>.Create(_ =>
            {
                byte[] usedBytes = new List<byte>(steamAuthTicketBuffer).GetRange(0, (int) steamAuthTicketBufferSize)
                    .ToArray();
                string ticket = BitConverter.ToString(usedBytes).Replace("-", string.Empty);

                Beamable.API.Instance.FlatMap(beamable =>
                    {
                        return beamable.Requester.Request<Beamable.Common.Api.EmptyResponse>(
                            Beamable.Common.Api.Method.POST,
                            $"/basic/payments/steam/auth",
                            new SteamTicketRequest(ticket));
                    })
                    .Then(f => promise.CompleteSuccess(PromiseBase.Unit))
                    .Error(ex => promise.CompleteError(ex));
            });

            SteamUser.GetAuthSessionTicket(steamAuthTicketBuffer, (int) steamAuthTicketBufferSize,
                out steamAuthTicketBufferSize);

            return promise;
        }

        
        Promise<SteamProductsResponse> ISteamService.GetProducts()
        {
            if (!SteamManager.Initialized)
            {
                var promise = new Promise<SteamProductsResponse>();
                promise.CompleteError(new Exception("Steamworks not initialized."));
                return promise;
            }

            long steamID = (long) SteamUser.GetSteamID().m_SteamID;
            return Beamable.API.Instance.FlatMap(beamable =>
            {
                return beamable.Requester.Request<SteamProductsResponse>(
                    Beamable.Common.Api.Method.GET,
                    $"/basic/payments/steam/products?steamId={steamID}");
            });
        }

        
        void ISteamService.RegisterTransactionCallback(Action<SteamTransaction> callback)
        {
            if (!SteamManager.Initialized)
            {
                Debug.LogError("Steamworks not initialized.");
                return;
            }

            if (callback != null)
            {
                if (_callbacks == null)
                {
                    _callbacks = new List<Action<SteamTransaction>>();
                }

                _callbacks.Add(callback);

                if (!_transactionRegistered)
                {
                    Callback<MicroTxnAuthorizationResponse_t>.Create(OnTransactionAuthorized);
                    _transactionRegistered = true;
                }
            }
        }

        
        private void OnTransactionAuthorized(MicroTxnAuthorizationResponse_t data)
        {
            if (_callbacks != null)
            {
                var authorized = Convert.ToBoolean(data.m_bAuthorized);
                var steamTransaction = new SteamTransaction(authorized, data.m_ulOrderID.ToString());
                foreach (var callback in _callbacks)
                {
                    callback?.Invoke(steamTransaction);
                }
            }
        }
    }
}
#endif

3. SteamworksExample

This SteamworksExample.cs demonstrates successful usage of Beamable with Steamworks integration.

Note: Game makers can use this class as inspiration to create a custom solution.

using UnityEngine;
using Steamworks;

namespace Beamable.Examples.Integrations.Steamworks
{
    /// <summary>
    /// Demonstrates <see cref="Steamworks"/>.
    /// </summary>
    public class SteamworksExample : MonoBehaviour
    {
        //  Unity Methods  --------------------------------
        protected void Start()
        {
            Debug.Log($"Start() Instructions...\n" + 
                      " * Complete steps: https://docs.beamable.com/docs/integrating-steamworks\n" + 
                      " * Run The Scene\n" +
                      " * See Unity Console Window for success\n");

            SetupBeamable();
        }
        
        //  Methods  --------------------------------------
        private async void SetupBeamable()
        {
            var beamableAPI = await Beamable.API.Instance;
            Debug.Log($"beamableAPI.User.id = {beamableAPI.User.id}");
       
            if(SteamManager.Initialized) 
            {
                // Successfully fetch arbitrary Steamworks data
                string personaName = SteamFriends.GetPersonaName();
                Debug.Log($"Success! SteamFriends.GetPersonaName = {personaName}");
            }
            else
            {
                Debug.Log($"Failure! SteamManager.Initialized = {SteamManager.Initialized}");
            }
        }
    }
}

Game Window

Success! The player is able to purchase a Beamable item via Steamworks.

Advanced

This section contains any advanced configuration options and workflows.