Contents

Enhanced Experiences (E2) SDK Guide

Introduction

The Twitch Enhanced Experiences SDK is a library that provides access to Twitch’s Enhanced Experiences Service through an API targeted at client applications used by Twitch broadcasters and server applications used by game companies.

There are three versions of the Enhanced Experiences SDK:

This document describes all versions. Most of the classes and methods are similar between the versions, so familiarity with one will help you learn the others. For the first two, there are several library files, both static and dynamic, for these platforms in both debug and release builds.

The audience for this document is game developers. Game developers use the Enhanced Experiences SDK to send data to the Enhanced Experiences Service. The Enhanced Experiences Service, in turn, propagates the data to clients, such as Twitch Discovery and Twitch Extensions.

The Enhanced Experiences SDK

There is one class in the Enhanced Experiences SDK, the DataSource class. It will perform all of the Enhanced Experiences Service connection handling, error handling, and rate limiting. The class has one child structure, the Configuration structure. It contains all of the data necessary to begin an Enhanced Experiences Service session with a DataSource object. The usage of these types is the same for all supported platforms.

Connecting to the Enhanced Experiences Service

The DataSource class is in one of two states, connected or not connected. All newly-constructed DataSource objects start in the not connected state. In this state, the only method that will succeed is the Connect method. All other methods will throw a TwitchException object. In a debug build, there will be a message stating there is no active connection. In Visual Studio, such messages appear in the Output pane.

If the Connect method succeeds, the object is in the connected state. In this state, if given valid parameters, all other methods will succeed but the Connect method will throw a TwitchException object. If you want your application to reconnect, it must first disconnect by invoking the Disconnect method to transition to the not connected state. If the Connect method fails, for example, due to a transient network problem, your application may invoke it again to retry the connection.

To invoke the Connect method, first construct a Configuration object to hold the connection parameters. Populate the object with the necessary fields. Note that all fields must have a value. The debugFn, initialValue, and timeout fields have default values but you may change them to values you find more suitable to your needs. There are six required fields in a Configuration object:

The first required field of a Configuration object is the token field. This holds a Twitch OAuth token from Twitch Identity. See the Twitch Authentication documentation for more information. You can use the Authentication SDK or any other valid, supported means to get this token. There are two types of acceptable tokens, user access tokens and app access tokens. Which type you need depends on your runtime environment.

In a client environment, such as in a game application running on a user’s desktop, the token must be a user access token. In this case, do not populate the broadcasterIds field since the Enhanced Experiences Service will determine the broadcaster from the token. If you populate this field, the Enhanced Experiences Service will reject the connection.

In a server environment, such as in a back-end service, the token must be an app access token. In this case, you must populate the broadcasterIds field as described in the following paragraph or the Enhanced Experiences Service will reject the connection.

The second required field of a Configuration object is the broadcasterIds field. The Enhanced Experiences Service uses this to determine which broadcasters to associate with the data it receives from the DataSource object. As stated previously, do not specify this field in a client environment. In a server environment, populate this field with the list of broadcaster identifiers associated with the data you provide to the DataSource object. Note that you must obtain permission from those broadcasters to send their data.

The third required field of a Configuration object is the gameId field. The Enhanced Experiences Service uses this to determine which game to associate with the data it receives from the DataSource object. If the game identifier is not valid, the Connect method will not fail but the Enhanced Experiences Service will reject all messages. In a debug build, the Enhanced Experiences SDK will print messages describing the errors.

The fourth required field of a Configuration object is the environment field. This field has two acceptable values, prod and dev. Use prod if you are in production. The Enhanced Experiences Service will propagate data to all subscribed clients. Use dev if you are in development or testing. The Enhanced Experiences Service will accept the data but will not propagate it.

The fifth required field of a Configuration object is the onTokenExpired field. This is a call-back the Enhanced Experiences SDK will use in case the provided token is invalid or expires. This function’s only parameter is a call-back the Enhanced Experiences SDK will provide for your application to invoke when it has acquired a token. This function’s return value is a Boolean. Return true if your application will invoke the provided call-back or false if your application will not, perhaps because it is shutting down or a user has canceled the operation.

The sixth required field of a Configuration object is the initialData field. This is a string containing the JSON representation of your data object. For example, if your object expressed in JavaScript is {one: 1, two: ['a', 'b']}, the string to use to set this field is "{\"one\":1,\"two\":[\"a\",\"b\"]}". The Enhanced Experiences Service uses this to create its view of your data. The other methods operate on this data.

There are two optional fields in a Configuration object:

The timeout field has a default value of ten seconds. Your application may specify a different time-out to suit your purpose. If your application specifies zero, there will be no time-out.

The debugFn field has a default value, specific to the language, that prints to the console. Your application may specify its own function if it wants to perform a different action with the arguments, such as keeping a log of all debug activity.

If the Connect method succeeds, it will return a session identifier. If your application provided a non-empty value for the sessionId field of the Configuration object, this will be that value. Otherwise, it will be a unique value the DataSource object creates for you. If the Connect method fails, it will throw a TwitchException object.

Updating the Enhanced Experiences Service

The DataSource class has four methods to update the view the Enhanced Experiences Service has of your data object. They are described in more detail, with examples, in the API Reference. As mentioned above, all of these methods throw a TwitchException object if the DataSource object is not in the connected state.

Three of the methods take a path parameter. The value of this parameter is the same as what you might type in JavaScript to access a particular property of your data object. For instance, suppose a data object has the following structure.

{
  "foo": {
    "bar": [
      {
        "baz": 1
      }
    ]
  }
}

A path of "foo.bar[0].baz" refers to the field whose value is 1. A path of "foo.bar[0]" refers to the first element of the bar array, the object containing the baz field. A path of "foo.bar" refers to the entire array. A path of "foo" refers to the object containing the bar field.

There are restrictions on what constitutes a valid value for the path parameter. It may not be empty. It must parse as a valid JavaScript path. The methods that have a path parameter have additional restrictions.

The path parameter of the RemoveField method must specify a field that is in the object. It may not specify an array element. The Enhanced Experiences Service does not allow removing array elements. The only way to do so is to update the array field with a new array value.

The path parameter of the UpdateField method must specify either a field or array element that is in the object or a new field whose parent is in the object. For instance, given the above data object, a path of "foo.bar[0].baz" will update the current baz field value while a path of "foo.baz" will add a new baz field to the foo object. Providing a path of "foo.baz.bar" will cause the UpdateField method to throw a TwitchException object. You may use this method to modify array elements but not to modify arrays. For instance, a path of "foo.bar[0]" will update the first element of the bar array while a path of "foo.bar[1]" or a path of "foo.baz[0]" will cause the UpdateField method to throw a TwitchException object.

The path parameter of the AppendToArrayField method must specify a field or array element that is in the object. That field or array element must be an array. The value parameter of the AppendToArrayField method must be a JSON representation of an array. For instance, given the above data object, a path of "foo.bar" and a value of "[null,3,\"four\"]" will update the data object as follows.

{
  "foo": {
    "bar": [
      {
        "baz": 1
      },
      null,
      3,
      "four"
    ]
  }
}

There are function templates you can use to perform the task of converting your data into a JSON string representation. There are function templates for all primitive types and all C++ container types. It is also possible to provide arbitrary structures of data. For instance, consider the following structure.

struct MyStruct {
    unsigned u;
    std::array<uint8_t, 9> a;
};

You may pass an instance of this structure as the second parameter of the UpdateField method if you provide an implementation of the Stringify function for it, such as follows.

void Stringify(std::ostream& os, MyStruct const& m) {
    Twitch::StringifyObject(os, _T("u"), m.u, _T("a"), m.a);
}

Note that due to pointer decay you must declare arrays using std::array if you want to use this mechanism. Also, due to a computer’s inherent imprecision representing real numbers, invoking UpdateField with a value of, say, 9.9f will result in a JSON string similar to "9.89999961853027".

Disconnecting from the Enhanced Experiences Service

To disconnect from the Enhanced Experiences Service, invoke the Disconnect method of the DataSource object. You need not invoke this method if you are destroying the DataSource object; it is invoked automatically in this case.

C++

SDK Types

All types in the Enhanced Experiences SDK are in the Twitch namespace.

Aliases

Several C++ built-in types and standard library classes have aliases (typedefs) since their types can vary based on the character type (i.e., char or wchar_t).

On Windows-based platforms (i.e., Windows and Xbox One), the Enhanced Experiences SDK assumes the use of Unicode. In this case, the string types and types dependent on those strings (e.g., std::wstringstream) use wide (16-bit) characters.

On other platforms (e.g., PS4), those types use single-byte (8-bit) characters.

The common header file, BaseSDK.h, contains the aliases, notably:

Threading

Several parts of the Enhanced Experiences SDK run asynchronously due to long-running network activities. To ensure clients are in control of the execution of their applications, the Enhanced Experiences SDK uses C++ std::future objects to provide asynchronous execution. This enables three different threading modes, described below.

Client Multi-Threading

In this mode, the client has multiple active threads and controls all threads.

Methods returning std::future objects create such objects with std::async, with a launch policy of std::launch::deferred. This means the part of the invoked method that runs asynchronously will not run until the client invokes either the get or wait method of the returned std::future object. To prevent blocking the main thread, the client will invoke the get or wait method on a separate thread. This provides the greatest control over the timing of the deferred execution, since the client thread chooses when to invoke the get or wait method.

If an exception occurs during the deferred invocation, the get method throws that exception.

The client is responsible for synchronizing access to data shared among any threads it creates.

SDK Multi-Threading

In this mode, the client uses only one thread directly but is aware of and accounts for additional threads.

The client wraps methods returning std::future objects with Callback objects, providing call-back functions to those objects. Such objects invoke the call-back functions passed to them on a thread different from the one which invoked the method which created the Callback object, since the Callback objects run the asynchronous part of such methods on a thread launched with std::async with a launch policy of std::launch::async.

The client is responsible for synchronizing access to data shared among the call-back thread, the invoking thread, and any other threads the client creates.

The lifetime of a Callback object must encompass the entire activity, so do not destroy a Callback object until its associated call-back function has run.

Single Threading

In this mode, the client has one thread and performs no data synchronization.

The client wraps methods returning std::future objects with Pollable objects. These objects create internal threads. Such threads are not exposed to the client and do not access client data, so no synchronization is required.

Clients running a “game loop,” such as an update-render loop, can check the IsReady property of the Pollable object to determine if the result of the operation is available. When it is true, the client can use the other properties of the Pollable object to get the value (if the method was successful) or the exception (if it was not). All such methods are guaranteed to return quickly, making them suitable for use in a game loop.

The lifetime of a Pollable object must encompass the entire activity. In other words, do not destroy a Pollable object until its IsReady property returns true.

Methods returning void do not create internal threads. If they have long-running components, the object will provide properties to check the status (e.g., IsReady) and get the result (e.g., Value), much like the Pollable objects.

C#

SDK Types

All types in the Enhanced Experiences SDK are in the Twitch namespace.

Threading

As of Unity 2018.4, Unity defaults to version 4.0 of the .NET Framework. As a result, it is now possible to use asynchronous code by employing the async and await keywords and Task<T> return type in C#. This implementation makes use of this feature instead of using Unity’s coroutines. All asynchronous methods have Async as a suffix on their names.

JSON

The Enhanced Experiences SDK uses but does not come with the Newtonsoft.Json library. There is a third-party version for Unity you can include in your Unity projects.

TypeScript

The TypeScript implementation has all of the methods described in the Enhanced Experiences Service section above but uses a different mechanism for creating an instance of the interface to access the Enhanced Experiences Service. The default export of the Node module is a creator function, createDataSource, that creates the object implementing the IDataSource interface.

The TypeScript implementation has an additional method not included in the other versions of the Enhanced Experiences SDK, updateData. This method accepts a data object and automatically generates the differences between it and the previous data object and sends those differences to the Enhanced Experiences Service in its expected format. This means you may use this method instead of the other methods if it is more convenient for you to do so.

Another difference between the implementation described above and the TypeScript implementation is the exceptions the methods might throw. In the TypeScript implementation, all exceptions are of type Error. Also, since Node is a memory-managed environment, there is no destructor. This means your application must invoke the disconnect method when it is done communicating with the Enhanced Experiences Service. In all other respects, the usage of the Enhanced Experiences SDK is the same as that described above.