Using the C# SDK

Overview of the EOS C# SDK

Epic Online Services (EOS) features a C# SDK in addition to the C SDK. This document will help you integrate EOS with your project using the C# SDK, and will cover some of the implementation differences between the two. While the core functionality remains the same regardless of which SDK you use, the C# SDK differs in design in the following ways:

  • The C# SDK adheres to C# best practices and follows an object-oriented approach, such as using handle objects rather than C-style handles to manage asynchronous operations.

  • Naming conventions match typical C# patterns. For example, the EOS_Auth_Login in the C SDK is accessible in the C# SDK as Epic.OnlineServices.Auth.AuthInterface.Login.

  • Data structures in the C SDK require macro-based API version numbers to ensure compatibility; these values are pre-populated in the C# SDK.

The EOS C# SDK includes libraries for desktop and mobile platforms, such as Windows, Linux, Mac, iOS, and Android. Additional versions of the C# SDK for restricted platforms, such as Xbox, Nintendo Switch, and PlayStation are available after approval. For access to the restricted SDKs and related documentation, you must configure your organization access and the documentation will be part of your SDK download after First Party approval.

Ask your technical account manager for more information and availability.

C# SDK Dependencies

For the C# SDK, you must have...

  • .NET Framework 3.5 or later, or API compatible equivalent

  • Previous versions may work, but have not been tested

For the C# SDK Samples, you must have...

  • .NET Core 3.1

  • Visual Studio 2019 or higher

Getting Started

Before you begin, you'll need to download the C# SDK and set up a product on Epic's Developer Portal. You should have the following product information from your Product Settings on the Developer Portal available for the SDK:

  • Product ID

  • Sandbox ID

  • Deployment ID

  • Client ID

  • Client secret

When you start integrating the EOS SDK, these values will enable you to create the Platform Interface, which is required to access all other EOS SDK interfaces. Once you have this information, you can proceed to integrate the SDK with your product.

Integration

  1. Include the C# SDK source files in your project.

    • (Unity users) Place these files (including Core and Generated folders) into your Assets folder.

  2. Ensure that the appropriate library binary file for your target platform is accessible by your application.

    • (Unity users) copy the appropriate library binary file into your Assets folder for your target platform.

    • For example, a Windows x64 integration would use EOSSDK-Win64-Shipping.dll.

  3. The C# SDK determines the library binary name to target in Epic.OnlineServices.Config. Several platforms are preconfigured to point to the correct name for convenience.

    • If your target platform has not been preconfigured, make the appropriate changes to the configuration and your project symbols to avoid a build error. For example, you may need to set EOS_PLATFORM_WINDOWS_64 if targeting Windows x64 or EOS_PLATFORM_WINDOWS_32 if targeting Windows x86.

  4. (Unity users) Create a new script to control the SDK. For this example, we used the name EOSSDKComponent.

    New script Component

  5. (Unity users) To create a component when needed, select Add Component to assign it to an entity.For demonstration purposes, we added it to the Main Camera.

    Add Component

  6. Write your EOS SDK code in this component. See our sample code for examples.

Implementing the SDK

Once you have set up your product and integrated the C# SDK, you can begin writing code. You'll need to initialize the SDK, call its Tick method regularly to ensure that the SDK can execute and that callbacks can run, and shut it down when your application finishes.

Managing the SDK's Life Cycle

There are three main parts of the SDK's life cycle: Initialization, Ticking (normal operation), and Shutdown.

Initialization

The Platform Interface is the entry point to the SDK, and you will need an instance of this interface to use EOS. To create one, call Epic.OnlineServices.Platform.PlatformInterface.Initialize with some basic information about your application, then call Epic.OnlineServices.Platform.PlatformInterface.Create with the values you have obtained from the Developer Portal to get an Epic.OnlineServices.Platform.PlatformInterface instance. Store this instance; you will need it to interact with the SDK. For example, to perform authentication, you first need an instance of Epic.OnlineServices.Auth.AuthInterface, and you can retrieve this by calling GetAuthInterface() on your `Epic.OnlineServices.Platform.PlatformInterfac`e instance.

You can only initialize the SDK once; subsequent calls to Epic.OnlineServices.Platform.PlatformInterface.Initialize will return the AlreadyInitialized failure code. We recommend initializing the SDK when your application starts up and not releasing it until the application shuts down.

Ticking

In order for the SDK to operate, you must call Epic.OnlineServices.Platform.PlatformInterface.Tick on your Platform Interface instance regularly. These calls do not need to happen every frame, but should happen fairly often; one call every 100 milliseconds is considered reasonable, but you can adjust the exact frequency to your needs. SDK callbacks can only run when you call Tick, so all of your asynchronous operations depend on it.

Shutdown

When you no longer need the SDK — generally, at application shutdown time — you can shut it down by calling Epic.OnlineServices.Platform.PlatformInterface.Release to release your Platform Interface instance, and then Epic.OnlineServices.Platform.PlatformInterface.Shutdown to complete the shutdown process. This process is final; Epic.OnlineServices.Platform.PlatformInterface.Release puts the SDK into a finalized state, and you cannot acquire another Platform Interface handle or reinitialize the SDK after that point. For this reason, we advise against shutting down the EOS SDK until application shutdown.

Some editor environments, including Unity, load external libraries like EOS during editor startup and do not unload them until the editor shuts down. In these cases, you should not call Epic.OnlineServices.Platform.PlatformInterface.Release or Epic.OnlineServices.Platform.PlatformInterface.Shutdown at the end of an in-editor play session, because you will be unable to initialize the SDK successfully in any future session without restarting the editor. In addition, because these editor environments use one continuous instance of the SDK, operations that began right before the end of a play session could finish and trigger callbacks in the following session.

Results

Most callback data structures and some return values use Epic.OnlineServices.Result to convey the results of SDK operations. Make sure to use this to handle errors and ensure operations are performing as expected.

Logging

The SDK outputs useful debugging information through an internal interface. To enable this feature, set up Epic.OnlineServices.Logging.LoggingInterface as early as possible, preferably immediately after initializing the SDK, by calling Epic.OnlineServices.Logging.LoggingInterface.SetLogLevel with parameters indicating the level of detail you require, followed by Epic.OnlineServices.Logging.LoggingInterface.SetCallback with a callback function to receive log information. This feature can provide insight into internal operations and can help with identifying the causes of unexpected behaviors you may encounter.

Unmanaged Memory

In the C SDK, some APIs such as EOS_Leaderboards_CopyLeaderboardDefinitionByIndex pass out structs of data that must be manually released. In the C# SDK, you don't need to concern yourself with releasing these structs as they are automatically handled by the wrapper's marshaling code.

In the C SDK, some APIs such as EOS_Presence_CreatePresenceModification pass out handles that let you set data in memory that is owned by the SDK library. These handles must be manually released. In the C# SDK, these handles are represented by objects with an underlying type of Epic.OnlineServices.Handle, and contain functions that let you set the unmanaged data. They also contain a Release function that you must manually call when you are finished with it.

Custom memory delegates

On some platforms, such as consoles, the SDK requires you to implement your own allocate, reallocate, and release functions. Because the SDK will call these functions with very high frequency, it is highly performant for the SDK to be able to access them directly without being routed through delegates in managed code.

You have the option to pass these functions into the SDK when initializing the platform with Epic.OnlineServices.Platform.InitializeOptions. It is recommended that you:

  1. Create a native library and implement the 3 memory functions,

  2. For each of the 3 memory functions, implement an export function that returns a pointer to the memory function,

  3. Before initializing the platform in C#, call the export functions, and set the returned IntPtr values to the options struct.

The following sample code demonstrates the structure of what your native library may look like:

void* Allocate(size_t InSizeInBytes, size_t InAlignment)
{
}

void* Reallocate(void* InPointer, size_t InSizeInBytes, size_t InAlignment)
{
}

void Release(void* InPointer)
{
}

typedef void* (*AllocateFunctionPointer)(size_t InSizeInBytes, size_t InAlignment);
extern "C" __declspec(dllexport) AllocateFunctionPointer GetAllocateFunctionPointer();
{
    return Allocate;
}

typedef void* (*ReallocateFunctionPointer)(void* InPointer, size_t InSizeInBytes, size_t InAlignment);
extern "C" __declspec(dllexport) ReallocateFunctionPointer GetReallocateFunctionPointer()
{
    return Reallocate;
}

typedef void (*ReleaseFunctionPointer)(void* InPointer);
extern "C" __declspec(dllexport) ReleaseFunctionPointer GetReleaseFunctionPointer()
{
    return Release;
}

And the following sample code demonstrates what the C# bindings may look like:

[DllImport("MyNativeLibrary.dll")]
private static extern IntPtr GetAllocateFunctionPointer();

[DllImport("MyNativeLibrary.dll")]
private static extern IntPtr GetReallocateFunctionPointer();

[DllImport("MyNativeLibrary.dll")]
private static extern IntPtr GetReleaseFunctionPointer();

Threading

The SDK is not currently thread safe. We recommend that all calls to the SDK come from your application's main thread. At this time, we recommend against using async, await, Thread, Task, or similar patterns.

The EOS Overlay

To use the EOS Overlay in your application, complete the following before any graphics devices are created:

  1. Load the SDK library into memory

  2. Initialize with EOS_Initialize

  3. Create your platform with EOS_Platform_Create

In Unity, one way you can achieve this is by creating a low-level native rendering plugin. At a minimum, you will need to:

  1. Create a native library with a name prefixed with GfxPlugin,

  2. Add an export function named UnityPluginLoad(void*) that loads the SDK library and invokes EOS_Initialize and EOS_Platform_Create successfully,

  3. Add an export function to return the created platform interface handle back to C# when needed, where it can be used to construct a new instance of Epic.OnlineServices.Platform.PlatformInterface.

The EOS overlay is not supported in editor environments such as the Unity editor. It is recommended that you explicitly disable the overlay in editor environments to prevent unintended behavior.

As a best practice for Unity users, we recommend that your standalone build controls the lifetime of the SDK inside a low-level native rendering plugin, and your editor build should exclude the plugin to control the lifetime of the SDK inside a MonoBehaviour with dynamic bindings.

Dynamic bindings

Typically, the C# SDK uses extern DllImport bindings. However, you may need to use dynamic bindings.

To begin using dynamic bindings:

  1. Define EOS_DYNAMIC_BINDINGS

  2. Dynamically load the SDK library and retrieve a library handle

  3. When the application session begins, call Epic.OnlineServices.Bindings.Hook to pass in the library handle and function pointer loader delegate.

  4. When the application session ends, call Epic.OnlineServices.Bindings.Unhook and free the library to clean up.

For example, you may have the following extern functions defined on Windows:

[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string lpLibFileName);

[DllImport("Kernel32.dll")]
private static extern int FreeLibrary(IntPtr hLibModule);

[DllImport("Kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

Then to begin using the SDK, load the library and hook the dynamic bindings with:

var libraryPath = $"Assets/{Config.LibraryName}";

var libraryPointer = LoadLibrary(libraryPath);
if (libraryPointer == IntPtr.Zero)
{
    throw new Exception($"Failed to load library {libraryPath}");
}

Bindings.Hook(libraryPointer, GetProcAddress);

When the application is finished, unhook the dynamic bindings and free the library with:

Bindings.Unhook();
FreeLibrary(libraryPointer);

Some platforms have platform specific APIs. These platforms have their own bindings class.

To use platform specific APIs, you must hook the corresponding platform-specific bindings class, in addition to the base Epic.OnlineServices.Bindings class.

As of 1.12, the default behavior of the C# SDK is to require dynamic bindings for editor environments such as the Unity editor. This is to enable on-demand loading and unloading of the SDK library, and allow the SDK to be initialized each time the game is run in the editor. More detailed sample code for using dynamic bindings in Unity is provided below.

Sample Code and Projects

There are a variety of C# SDK samples to show you different Epic Online Services (EOS) functionality implementations, such as authentication, presence, the purchasing flow, and voice. Additionally, we have sample code for setting up the platform, logging in, and implementing the Unity EOS SDK component.

Sample Code

The sample code in this section is intended to demonstrate and help familiarize you with the C# SDK.

Setting up the Platform

    // Set these values as appropriate. For more information, see the Developer Portal documentation.
    string productName = "MyCSharpApplication";
    string productVersion = "1.0";
    string productId = "";
    string sandboxId = "";
    string deploymentId = "";
    string clientId = "";
    string clientSecret = "";

    var initializeOptions = new Epic.OnlineServices.Platform.InitializeOptions()
    {
        ProductName = productName,
        ProductVersion = productVersion
    };

    var initializeResult = Epic.OnlineServices.Platform.PlatformInterface.Initialize(initializeOptions);
    if (initializeResult != Epic.OnlineServices.Result.Success)
    {
        throw new System.Exception("Failed to initialize platform: " + initializeResult);
    }

    // The SDK outputs lots of information that is useful for debugging.
    // Make sure to set up the logging interface as early as possible: after initializing.
    Epic.OnlineServices.Logging.LoggingInterface.SetLogLevel(Epic.OnlineServices.Logging.LogCategory.AllCategories, Epic.OnlineServices.Logging.LogLevel.VeryVerbose);
    Epic.OnlineServices.Logging.LoggingInterface.SetCallback((Epic.OnlineServices.Logging.LogMessage logMessage) =>
    {
        System.Console.WriteLine(logMessage.Message);
    });

    var options = new Epic.OnlineServices.Platform.Options()
    {
        ProductId = productId,
        SandboxId = sandboxId,
        DeploymentId = deploymentId,
        ClientCredentials = new Epic.OnlineServices.Platform.ClientCredentials()
        {
            ClientId = clientId,
            ClientSecret = clientSecret
        }
    };

    var platformInterface = Epic.OnlineServices.Platform.PlatformInterface.Create(options);
    if (platformInterface == null)
    {
        throw new System.Exception("Failed to create platform");
    }

Logging In

    var loginCredentialType = Epic.OnlineServices.Auth.LoginCredentialType.AccountPortal;
    /// These fields correspond to <see cref="Epic.OnlineServices.Auth.Credentials.Id" /> and <see cref="Epic.OnlineServices.Auth.Credentials.Token" />,
    /// and their use differs based on the login type. For more information, see <see cref="Epic.OnlineServices.Auth.Credentials" />
    /// and the Auth Interface documentation.
    string loginCredentialId = null;
    string loginCredentialToken = null;

    var authInterface = platformInterface.GetAuthInterface();
    if (authInterface == null)
    {
        throw new System.Exception("Failed to get auth interface");
    }

    var loginOptions = new Epic.OnlineServices.Auth.LoginOptions()
    {
        Credentials = new Epic.OnlineServices.Auth.Credentials()
        {
            Type = loginCredentialType,
            Id = loginCredentialId,
            Token = loginCredentialToken
        }
    };

    // Ensure platform tick is called on an interval, or the following call will never callback.
    authInterface.Login(loginOptions, null, (Epic.OnlineServices.Auth.LoginCallbackInfo loginCallbackInfo) =>
    {
        if (loginCallbackInfo.ResultCode == Epic.OnlineServices.Result.Success)
        {
            System.Console.WriteLine("Login succeeded");
        }
        else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
        {
            System.Console.WriteLine("Login failed: " + loginCallbackInfo.ResultCode);
        }
    });

Unity EOSSDK Component

    // This code is provided for demonstration purposes and is not intended to represent ideal practices.
    using Epic.OnlineServices;
    using Epic.OnlineServices.Auth;
    using Epic.OnlineServices.Logging;
    using Epic.OnlineServices.Platform;
    using System;
    using System.Runtime.InteropServices;
    using UnityEngine;

    public class EOSSDKComponent : MonoBehaviour
    {
        // Set these values as appropriate. For more information, see the Developer Portal documentation.
        public string m_ProductName = "MyUnityApplication";
        public string m_ProductVersion = "1.0";
        public string m_ProductId = "";
        public string m_SandboxId = "";
        public string m_DeploymentId = "";
        public string m_ClientId = "";
        public string m_ClientSecret = "";
        public LoginCredentialType m_LoginCredentialType = LoginCredentialType.AccountPortal;
        /// These fields correspond to <see cref="Credentials.Id" /> and <see cref="Credentials.Token" />,
        /// and their use differs based on the login type. For more information, see <see cref="Credentials" />
        /// and the Auth Interface documentation.
        public string m_LoginCredentialId = null;
        public string m_LoginCredentialToken = null;

        private static PlatformInterface s_PlatformInterface;
        private const float c_PlatformTickInterval = 0.1f;
        private float m_PlatformTickTimer = 0f;

        // If we're in editor, we should dynamically load and unload the SDK between play sessions.
        // This allows us to initialize the SDK each time the game is run in editor.
    #if UNITY_EDITOR
        [DllImport("Kernel32.dll")]
        private static extern IntPtr LoadLibrary(string lpLibFileName);

        [DllImport("Kernel32.dll")]
        private static extern int FreeLibrary(IntPtr hLibModule);

        [DllImport("Kernel32.dll")]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

        private IntPtr m_LibraryPointer;
    #endif

        private void Awake()
        {
    #if UNITY_EDITOR
            var libraryPath = "Assets/" + Config.LibraryName;

            m_LibraryPointer = LoadLibrary(libraryPath);
            if (m_LibraryPointer == IntPtr.Zero)
            {
                throw new Exception("Failed to load library" + libraryPath);
            }

            Bindings.Hook(m_LibraryPointer, GetProcAddress);
    #endif
        }

        private void OnApplicationQuit()
        {
            if (s_PlatformInterface != null)
            {
                s_PlatformInterface.Release();
                s_PlatformInterface = null;
                PlatformInterface.Shutdown();
            }

    #if UNITY_EDITOR
            if (m_LibraryPointer != IntPtr.Zero)
            {
                            Bindings.Unhook();

                            // Free until the module ref count is 0
                while (FreeLibrary(m_LibraryPointer) != 0) { }
                m_LibraryPointer = IntPtr.Zero;
            }
    #endif
        }

        void Start()
        {
            var initializeOptions = new InitializeOptions()
            {
                ProductName = m_ProductName,
                ProductVersion = m_ProductVersion
            };

            var initializeResult = PlatformInterface.Initialize(initializeOptions);
            if (initializeResult != Result.Success)
            {
                throw new Exception("Failed to initialize platform: " + initializeResult);
            }

            // The SDK outputs lots of information that is useful for debugging.
            // Make sure to set up the logging interface as early as possible: after initializing.
            LoggingInterface.SetLogLevel(LogCategory.AllCategories, LogLevel.VeryVerbose);
            LoggingInterface.SetCallback((LogMessage logMessage) => Debug.Log(logMessage.Message));

            var options = new Options()
            {
                ProductId = m_ProductId,
                SandboxId = m_SandboxId,
                DeploymentId = m_DeploymentId,
                ClientCredentials = new ClientCredentials()
                {
                    ClientId = m_ClientId,
                    ClientSecret = m_ClientSecret
                }
            };

            s_PlatformInterface = PlatformInterface.Create(options);
            if (s_PlatformInterface == null)
            {
                throw new Exception("Failed to create platform");
            }

            var loginOptions = new LoginOptions()
            {
                Credentials = new Credentials()
                {
                    Type = m_LoginCredentialType,
                    Id = m_LoginCredentialId,
                    Token = m_LoginCredentialToken
                }
            };

            // Ensure platform tick is called on an interval, or this will not callback.
            s_PlatformInterface.GetAuthInterface().Login(loginOptions, null, (LoginCallbackInfo loginCallbackInfo) =>
            {
                if (loginCallbackInfo.ResultCode == Result.Success)
                {
                    Debug.Log("Login succeeded");
                }
                else if (Common.IsOperationComplete(loginCallbackInfo.ResultCode))
                {
                    Debug.Log("Login failed: " + loginCallbackInfo.ResultCode);
                }
            });
        }

        // Calling tick on a regular interval is required for callbacks to work.
        private void Update()
        {
            if (s_PlatformInterface != null)
            {
                m_PlatformTickTimer += Time.deltaTime;

                if (m_PlatformTickTimer >= c_PlatformTickInterval)
                {
                    m_PlatformTickTimer = 0;
                    s_PlatformInterface.Tick();
                }
            }
        }
    }

Sample Projects

These samples are in a folder of your C# SDK download containing the following libraries and applications.

Libraries

Common contains the SDK code and other useful shared functionality.

WpfCommon contains functionality that is common to all of the WPF samples.

VoiceCommon contains functionality that is common to all of the voice samples.

Applications

SimpleAuth is a WPF application that demonstrates how to perform authentication and presence related functions for the logged in user.

SimpleOverlayPurchasing is a managed DirectX11 application that demonstrates how to initiate and complete the purchasing flow by utilising the in-game overlay.

VoiceServer is an application that demonstrates how to implement a RESTful API service that acts as a trusted voice server between voice clients and EOS.

VoiceClient is a WPF application that demonstrates how to connect to a voice room, switch audio devices, transmit audio, client mute, server mute, and kick.

Before You Begin

To run the samples, you must install:

  • .NET Core 3.1

  • Visual Studio 2019 or higher

You must also set the following values associated with your application in Settings.cs:

  • ProductId

  • SandboxId

  • DeploymentId

  • ClientId

  • ClientSecret

You can also set the LoginType. Use of the Id and Token fields differs based on the login type. For more information, see Auth.Credentials and Auth Interface.

The sample application uses Epic Account Services to authenticate the local user for demonstration purposes. This requires that the Client Credentials used to initialize the SDK have been assigned to an Application used for Epic Account Services.

The demonstrated SDK functionality can be used with any of the supported identity providers for user authentication.

SimpleAuth

SimpleAuth is a sample application demonstrating how to perform authentication and presence related functions for a logged in user.

Logging in

To begin, you are presented with a login screen. You can select the type of login you want to use. We recommend using either the AccountPortal type to login directly with the Account Portal, or the Developer type to login with credentials assigned by the Developer Auth Tool.

SimpleAuth_LogIn.png

Viewing your presence

Once signed in, you are presented with a page showing your current presence, and a form that allows you to change it. It contains fields for the online status, rich text, and data entries associated with your current presence.

SimpleAuth_ViewPresence.png

OverlayPurchasing

This feature is only available for products that are part of the Epic Games store. You must be a store partner, and you must have offers set up with the store.

In order for the overlay to work, you must have done one of the following:

  • Installed the Epic Games Launcher and ran it for the first time.

  • Downloaded the EOSOVH dll file and set the registry key HKEY_CURRENT_USER\Software\Epic Games\EOS\OverlayPath to the directory of where the dll resides.

Initiating the purchasing flow

The sample will automatically proceed with logging in and the necessary ecom functions in order to launch the overlay into the purchasing flow. Keep an eye on the output to ensure there are no problems.

OverlayPurchase_InitiateFlow.png