The official Twitch game engine plugins are now available

Twitch has always been a place where communities come together and interact live. Over the years, many game developers have recognized and leaned into community interactivity; creating functionality that allows an audience to affect gameplay, collaborate, and even participate.

To encourage more interactive game elements, we’ve been building new tools in collaboration with DataWisp. After an extended closed beta period, we are pleased to announce that the official Twitch game engine plugins are now publicly available for all game developers.

Why provide plugins?

Game development itself is not an easy task. The plugins are designed to simplify the integration of Twitch functionality, allowing developers to build unique experiences on Twitch without dedicating an extraordinary amount of time learning how to build such interactions from scratch. Experimenting with Twitch interactivity can happen at a more rapid pace without the burden of building or understanding how the underlying web technology works. Independent developers in particular will benefit from the game engine plugins as it allows them to utilize functionality which is usually prohibitively expensive to implement.

Hosting a backend service should also not be a requirement for building Twitch integrations. The plugins alleviate this need by handling everything inside of the client from EventSub subscriptions to calling Twitch API endpoints. Connections are actively maintained for you, freeing up your time to focus on the broader Twitch experience. Device Code flow (DCF) was also recently released in preparation for the plugins; it provides an authentication method for applications with limited input methods like set-top boxes, Electron apps, and of course games.

What are some examples?

During the closed beta for the game engine plugins, we worked with a number of developers for feedback and testing. To get an idea of what’s possible, check out Happy Volcano’s Chaos Mode for You Suck at Parking and Glossbird’s streamer edition for Fitment: Move With Me!

What’s next?

There are a number of additional features we are considering for future versions. For example, the addition of Twitch Drops support, which would allow developers of all sizes to add Twitch Drops support to their games without an account linking system.

How do I get started?

We provide a guide for both Unity and Unreal developers. Head over to our overview page and select which engine suits your needs.

If you do not use a game engine that our plugins currently support, you can wrap your own version. You are welcome to submit a request through this form and we will provide the base C++ version. Note that additional support is not provided for the base SDK.

We would love to hear your feedback on UserVoice regarding how we can continue to improve the plugins and answer any of your initial questions in the thread below.

8 Likes

Hey. I’ve been trying to get this working in Unity but I’m unable to build or run after adding the plugin, or click on fields in the SDK Settings inspector. Unity is giving me the following error: DllNotFoundException: R66_core.dll assembly:<unknown assembly> type:<unknown_type> member:(null)

I’m guessing this might be somehow related to me building and running on a Mac and the included libraries have only been built for Windows. I did initially think it was just related to running on an Apple Silicon Mac but it also seems to affect Intel Macs too (and the lack of ARM build will likely impact anyone wanting to build for iOS or Android too).

Is this a known issue, or is it expected to work on Macs and there’s just some missing configuration step in Unity?

2 Likes

Very excited to have an officially endorsed plugin for Unity! I was just about to add Twitch integration for my game project so the timing is perfect.

There were a few issues I ran into when using the docs. Minor thing, but I noticed the link in the Overview page for “Unity Guide” is a 404 (looks like it should be pointing to the “unity-guide” page instead of “unityguide”. There are also a lot of typos in the sample code in the Getting Started guide. For example, updateAuthState instead of UpdateAuthState, start() instead of void Start(), etc. which was disorienting at first.

Something that would be incredibly helpful, given the Unity plugin’s heavy reliance on asynchronous tasks, is support for System.Threading.CancellationToken. Many of the user-initiated operations in my game are asynchronous in nature and can be cancelled or skipped by the user if they run for too long, so I make heavy use of Unity 2023’s async/await support and cancellation tokens. As it stands, I can check the cancellation token’s status before and after each call to the Twitch API, but if the calling method is cancelled externally during that time, the GameTask stays running in the background, and the result is never disposed when it completes, causing a memory leak.

@jbulava Is mac not supported for the Unity plugins? Getting the same error as @pilky

1 Like

Mo from Datawisp here - awesome to see this finally live! It’s been a pleasure working with Twitch and we’re excited to see all the amazing experiences developers create with these tools!

This is something we are looking into.

This is pretty cool! Very happy to see plugins that simplify Twitch integration in Unity and Unreal. Currently testing out the Unity version.
Where does the generated token get stored? Is it possible to control where the token is stored?
I’ve only ever used Implicit grant flow so I’m a bit new to Device code grant flow. The docs mention that in Device code grant flow a JSON string is returned containing access_token, expires_in, refresh_token, etc. But in this instance after authorizing the app, it jumps to the Connections page. Can you explain what’s happening between clicking Authorize and landing on the Connections page?

Hello, trying to get this working in Unity, but when I type in my Client ID in the Twitch SDK Settings, the Status says “Unable to check if the ClientId is valid”. Any thoughts on why this could be?

2 Likes

This is an erroneous error that we are aware of. You should be able to use your client id even if it shows invalid.

We will be correcting this in the next version of the plugins

The plugins handle all of this for you, you shouldn’t ever need to directly interact with the token or refresh token.

As for the 2nd question, that’s because the plugins utilize DCF (device code flow) which uses a different flow. This is documented here: Getting OAuth Access Tokens | Twitch Developers

I have the same problem. Is there any solution?

I have the same problem and it cant run.
1.RunInUnityEditor. When i check it status , it show AuthStatus.LoggedOut Or Loading. Then i retry to run the method – TwitchSDKApi.GetAuthenticationInfo, it still in LoggedOut or Loading status.
2.RunInApp. Error!
MissingMethodException: Default constructor not found for type TwitchSDK.Interop.LogRequest
at System.RuntimeType.CreateInstanceMono (System.Boolean nonPublic) [0x00000] in <00000000000000000000000000000000>:0
at System.Activator.CreateInstance (System.Type type, System.Boolean nonPublic) [0x00000] in <00000000000000000000000000000000>:0
at TwitchSDK.Interop.Types+TypeMarshallingInfo.Deserialize (System.IntPtr ptr, System.Type type) [0x00000] in <00000000000000000000000000000000>:0
at TwitchSDK.Interop.Types+TypeMarshallingInfo.Deserialize[T] (System.IntPtr ptr) [0x00000] in <00000000000000000000000000000000>:0
at TwitchSDK.TwitchSDKApi…ctor (System.String clientId, System.Boolean useEventSubProxy) [0x00000] in <00000000000000000000000000000000>:0
at TwitchLiveStreamManager.Init (System.Action success, System.Action fail) [0x00000] in <00000000000000000000000000000000>:0
at GameRiver.Client.GameFacade.RegisterProxy (System.Type proxyType) [0x00000] in <00000000000000000000000000000000>:0
at GameRiver.Client.ProxyGroup.Register () [0x00000] in <00000000000000000000000000000000>:0
at GameRiver.Client.LocalPlayerProxy.RequestEnterGame () [0x00000] in <00000000000000000000000000000000>:0
at System.Action.Invoke () [0x00000] in <00000000000000000000000000000000>:0
at GameRiver.Client.LoginWindowMediator.DoRequestData () [0x00000] in <00000000000000000000000000000000>:0
at System.Action.Invoke () [0x00000] in <00000000000000000000000000000000>:0
at System.EventHandler1[TEventArgs].Invoke (System.Object sender, TEventArgs e) [0x00000] in <00000000000000000000000000000000>:0 at EventPool.EventPool1[T].HandleEvent (System.Object sender, T e) [0x00000] in <00000000000000000000000000000000>:0
at GameRiver.LoginModel.OnGateAuthSucc (System.Object sender, GameEvent e) [0x00000] in <00000000000000000000000000000000>:0
at System.EventHandler1[TEventArgs].Invoke (System.Object sender, TEventArgs e) [0x00000] in <00000000000000000000000000000000>:0 at EventPool.EventPool1[T].HandleEvent (System.Object sender, T e) [0x00000] in <00000000000000000000000000000000>:0
at EventPool.EventPool`1[T].Update () [0x00000] in <00000000000000000000000000000000>:0

How to solve this problem

I’ve been playing with the Unity plugin, and the documentation is pretty rough around the edges, but I’ve been making progress with my app.

However, while I have been able to run my app with the plugin in the unity editor, a production build crashes with this seeming to be part of the problem… (From the crash log)

0x7B762CB0 (mono-2.0-bdwgc) GC_malloc_uncollectable

0x7B567990 (mono-2.0-bdwgc) mono_win32_compat_ZeroMemory

0x19BF87A8 (Mono JIT Code) (wrapper managed-to-native) System.Runtime.InteropServices.Marshal:PtrToStringUni (intptr,int)

0x19BF71B8 (Mono JIT Code) TwitchSDK.Interop.Types/TypeMarshallingInfo:Deserialize (intptr,System.Type)

0x19BF7054 (Mono JIT Code) TwitchSDK.Interop.Types/TypeMarshallingInfo:Deserialize<T_REF> (intptr)

0x19BF6BD8 (Mono JIT Code) TwitchSDK.Interop.Types:Unmarshal (intptr)

0x19BF3BC4 (Mono JIT Code) TwitchSDK.Interop.PlatformAbstractionLayer:DoPALCall (int,intptr)

0x19BF390C (Mono JIT Code) TwitchSDK.Interop.PlatformAbstractionLayer:DoPALCall (intptr,int,intptr)

0x19BF36C8 (Mono JIT Code) (wrapper native-to-managed) TwitchSDK.Interop.PlatformAbstractionLayer:DoPALCall (intptr,int,intptr)

0x7B94B075 (R66_core) R66_GetVersion

0x7B9547D5 (R66_core) R66Api_WaitForPredictionUpdate

0x7B9569E3 (R66_core) R66Api_WaitForPredictionUpdate

0x7B954C8C (R66_core) R66Api_WaitForPredictionUpdate

0x7B950937 (R66_core) R66Api_GetAuthenticationInfo

0x1A4D3E34 (Mono JIT Code) (wrapper managed-to-native) TwitchSDK.Interop.NativeApi:R66Api_GetAuthenticationInfo (intptr,intptr,TwitchSDK.Interop.Types/MarshallableTaskCallback,intptr)

0x19EB0CE2 (Mono JIT Code) TwitchSDK.Interop.Types/<InvokeMarshallable>d__3`2<P_REF, R_REF>:MoveNext ()

0x1A4D3D84 (Mono JIT Code) System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<TwitchSDK.Interop.AuthenticationInfo>:Start<TwitchSDK.Interop.Types/<InvokeMarshallable>d__3`2<TwitchSDK.Interop.PlainString, TwitchSDK.Interop.AuthenticationInfo>> (TwitchSDK.Interop.Types/<InvokeMarshallable>d__3`2<TwitchSDK.Interop.PlainString, TwitchSDK.Interop.AuthenticationInfo>&)

0x19BFFF1B (Mono JIT Code) TwitchSDK.Interop.Types:InvokeMarshallable<P_REF, R_REF> (System.Action`4<intptr, intptr, TwitchSDK.Interop.Types/MarshallableTaskCallback, intptr>,intptr,P_REF)

0x1A4D3C64 (Mono JIT Code) TwitchSDK.Interop.CoreLibrary:GetAuthenticationInfo (string)

0x1A4D3304 (Mono JIT Code) TwitchSDK.TwitchSDKApi/<>c__DisplayClass4_0/<<GetAuthenticationInfo>b__0>d:MoveNext ()

0x1A4D3148 (Mono JIT Code) System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<TwitchSDK.Interop.AuthenticationInfo>:Start<TwitchSDK.TwitchSDKApi/<>c__DisplayClass4_0/<<GetAuthenticationInfo>b__0>d> (TwitchSDK.TwitchSDKApi/<>c__DisplayClass4_0/<<GetAuthenticationInfo>b__0>d&)

0x1A4D3014 (Mono JIT Code) TwitchSDK.TwitchSDKApi/<>c__DisplayClass4_0:<GetAuthenticationInfo>b__0 ()

0x1A4D2EF5 (Mono JIT Code) TwitchSDK.ResultCache`1/<>c__DisplayClass4_0<Value_REF>:<GetOrInsert>b__0 ()

0x1A4D2E2F (Mono JIT Code) TwitchSDK.ResultCache`1<Value_REF>:GetOrInsert (System.Func`1<TwitchSDK.GameTask`1<Value_REF>>)

0x1A4D2CEC (Mono JIT Code) TwitchSDK.ResultCache`1<Value_REF>:GetOrInsert (System.Func`1<System.Threading.Tasks.Task`1<Value_REF>>)

0x1A4D2C08 (Mono JIT Code) TwitchSDK.TwitchSDKApi:GetAuthenticationInfo (TwitchSDK.TwitchOAuthScope[])

0x1A4D287C (Mono JIT Code) AuthManager:GetAuthInformation ()

Any idea how I can move forward?

In case anyone else runs into the same issue I did…

Using the x86_64 architecture option in the unity did the trick for me.

Looks like there might be an issue with the x86 r66_core.dll?

@Thawco any progress with this problem?

DllNotFoundException: R66_core.dll assembly:<unknown assembly> type:<unknown type> member:(null)

Word of warning for anyone else.
The documentation for the Unity plugin is VERY rough.
Every piece of sample code so far is incorrect in some way and will not work without changes. The first bit of example code is even missing semicolons. Its clear nobody has ever tested these samples and probably hasn’t event tried compiling them. The supplied scope types don’t include all the possible scopes. I’ve got it to work for what I need, but it takes some wrangling.

Twitch I would be more than happy to come fix this all up for you. :stuck_out_tongue:

Anyone have any luck with follow events? I get authentication errors when I try to use SubscribeToChannelFollowEvents().

AuthState is logged in, GetMyUserInfo returns my channel name.

GameTask<EventStream<ChannelFollowEvent>> followEvents = Twitch.API.SubscribeToChannelFollowEvents();

var followEvent = await FollowEvents.MaybeResult.WaitForEvent();

Debug.Log($"{followEvent.UserDisplayName} is now following!");

Error: Closing down ES socket due to registration error: 403 Forbidden: subscription missing proper authorization

The example code for AuthManager uses incorrect scopes.

Try something like this instead:

public void GetAuthInformation()
{
	if (AuthInfoTask == null)
	{
		TwitchOAuthScope[] tscopes = GetScopes();
		AuthInfoTask = Twitch.API.GetAuthenticationInfo(tscopes);
	}
}

private TwitchOAuthScope[] GetScopes()
{
	TwitchOAuthScope[] scopes = {
		TwitchOAuthScope.Bits.Read,
		TwitchOAuthScope.Channel.ManageBroadcast,
		TwitchOAuthScope.Channel.ManagePolls,
		TwitchOAuthScope.Channel.ManageRedemptions,
		TwitchOAuthScope.Channel.ReadHypeTrain,
		new TwitchOAuthScope("moderator:read:followers"),
		new TwitchOAuthScope("channel:read:subscriptions")
	};
	return scopes;
}

Thanks! Didn’t realize the plugin would allow scopes beyond the hard coded ones in the TwitchOAuthScope class