Skip to content

Instantly share code, notes, and snippets.

@phosphoer
Last active January 12, 2024 21:21
Show Gist options
  • Save phosphoer/28eb8e7da61cb25ea3472fe432f2cda8 to your computer and use it in GitHub Desktop.
Save phosphoer/28eb8e7da61cb25ea3472fe432f2cda8 to your computer and use it in GitHub Desktop.
Epic Game SDK Unity Integration
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if BUILD_EPIC
using PlayEveryWare.EpicOnlineServices;
using Epic.OnlineServices;
using Epic.OnlineServices.Achievements;
using Epic.OnlineServices.Auth;
using Epic.OnlineServices.Presence;
public class EpicGameClient : MonoBehaviour, IEOSCoroutineOwner
{
private static Dictionary<string, string> _commandLineArgs = new Dictionary<string, string>();
private static readonly AuthScopeFlags kScopeFlags = AuthScopeFlags.FriendsList | AuthScopeFlags.Presence;
private static bool _isOverlayBlockingInput;
public static void UnlockAchievement(string achievementName)
{
QAG.ConsoleDebug.Log($"EpicGameClient: Unlocking achievement {achievementName}...");
var platformInterface = EOSManager.Instance.GetEOSPlatformInterface();
if (platformInterface == null)
return;
var localUserId = EOSManager.Instance.GetProductUserId();
if (localUserId == null)
{
QAG.ConsoleDebug.Log("EpicGameClient: Local User is null! Aborting achievement unlock.");
return;
}
var eosAchievementOption = new Epic.OnlineServices.Achievements.UnlockAchievementsOptions
{
UserId = localUserId,
AchievementIds = new Utf8String[] { achievementName }
};
var achievementsInterface = platformInterface.GetAchievementsInterface();
achievementsInterface.UnlockAchievements(ref eosAchievementOption, null, OnUnlockAchievementsCompleteCallback);
}
public static void SetPresence(string localizedText)
{
QAG.ConsoleDebug.Log($"EpicGameClient: Setting Presence to {localizedText}...");
var platformInterface = EOSManager.Instance.GetEOSPlatformInterface();
if (platformInterface == null)
return;
var localUserId = EOSManager.Instance.GetLocalUserId();
if (localUserId == null)
{
QAG.ConsoleDebug.Log("EpicGameClient: Local User is null! Aborting presence update.");
return;
}
var modificationOptions = new CreatePresenceModificationOptions();
modificationOptions.LocalUserId = localUserId;
var presenceInterface = platformInterface.GetPresenceInterface();
Result result = presenceInterface.CreatePresenceModification(ref modificationOptions, out PresenceModification presenceModification);
if (result == Result.Success)
{
var rawTextOptions = new PresenceModificationSetRawRichTextOptions();
rawTextOptions.RichText = localizedText;
presenceModification.SetRawRichText(ref rawTextOptions);
var options = new SetPresenceOptions();
options.LocalUserId = localUserId;
options.PresenceModificationHandle = presenceModification;
presenceInterface.SetPresence(ref options, null, OnSetPresenceComplete);
presenceModification.Release();
}
}
private void Awake()
{
EOSManager.Instance.Init(this);
}
private IEnumerator Start()
{
yield return Tween.WaitForTime(1);
#if UNITY_EDITOR
TryPersistentLogin();
#else
TryEpicLauncherLogin();
#endif
}
private void Update()
{
EOSManager.Instance.Tick();
if (EOSManager.Instance.IsOverlayOpenWithExclusiveInput() && !_isOverlayBlockingInput)
{
_isOverlayBlockingInput = true;
PlayerMenuInput.ForceDisableAllFocus = true;
CanvasCursor.PushVisible();
}
else if (!EOSManager.Instance.IsOverlayOpenWithExclusiveInput() && _isOverlayBlockingInput)
{
_isOverlayBlockingInput = false;
PlayerMenuInput.ForceDisableAllFocus = false;
CanvasCursor.PopVisible();
}
}
private void OnApplicationQuit()
{
EOSManager.Instance.OnShutdown();
}
private void OnApplicationFocus(bool hasFocus)
{
EOSManager.Instance.OnApplicationFocus(hasFocus);
}
private void OnApplicationPause(bool pauseStatus)
{
EOSManager.Instance.OnApplicationPause(pauseStatus);
}
#if UNITY_EDITOR
[UnityEditor.MenuItem("Boat Game/Clear Epic Persistent Auth")]
private static void DeletePersistentAuth()
{
var authInterface = EOSManager.Instance.GetEOSAuthInterface();
DeletePersistentAuthOptions deleteAuthOptions = new DeletePersistentAuthOptions();
authInterface.DeletePersistentAuth(ref deleteAuthOptions, null, OnPersistentDataDeleteCallback);
}
#endif
// This parses commandline args in the format "app.exe paramName=paramValue param2Name=param2Value
// which is what Epic launcher does
private static void ParseCommandLine()
{
_commandLineArgs.Clear();
string[] commandLineArgs = System.Environment.GetCommandLineArgs();
string authToken = string.Empty;
for (int i = 0; i < commandLineArgs.Length; i++)
{
string arg = commandLineArgs[i];
string[] argParts = arg.Split('=');
if (argParts.Length == 2)
{
string paramName = argParts[0];
string paramValue = argParts[1];
_commandLineArgs[paramName] = paramValue;
QAG.ConsoleDebug.Log($"Got commandline arg {paramName} with value {paramValue}");
}
}
}
private static void OnSetPresenceComplete(ref Epic.OnlineServices.Presence.SetPresenceCallbackInfo callbackInfo)
{
QAG.ConsoleDebug.Log($"EpicGameClient: Set Presence result {callbackInfo.ResultCode}");
}
private static void OnUnlockAchievementsCompleteCallback(ref OnUnlockAchievementsCompleteCallbackInfo data)
{
QAG.ConsoleDebug.Log($"EpicGameClient: Unlock Achievement result {data.ResultCode}");
}
private static void OnPersistentDataDeleteCallback(ref DeletePersistentAuthCallbackInfo callbackInfo)
{
QAG.ConsoleDebug.Log($"EpicGameClient: Delete persistent login result: {callbackInfo.ResultCode}");
}
private void TryLoginWithOptions(LoginOptions options, bool fallbackToAccountPortal)
{
EOSManager.Instance.StartLoginWithLoginOptions(options, callbackInfo =>
{
QAG.ConsoleDebug.Log($"EpicGameClient: Login result: {callbackInfo.ResultCode}");
// If successful, the credentials were stored on local keychain but we may still need to link this
// login with an epic account id or something?
if (callbackInfo.ResultCode == Result.Success)
{
TryLoginWithEpicAccount(callbackInfo.LocalUserId, tryCreateUserOnInvalidUser: true);
}
// Otherwise, we need to log in with account portal?
else if (fallbackToAccountPortal)
{
TryAccountPortalLogin();
}
else
{
QAG.ConsoleDebug.LogError("EpicGameClient: Login failed and fallbackToAccountPortal is false...giving up.");
}
});
}
private void TryEpicLauncherLogin()
{
QAG.ConsoleDebug.Log("EpicGameClient: Attempting Epic Launcher Login...");
ParseCommandLine();
string authToken = string.Empty;
if (!_commandLineArgs.TryGetValue("-AUTH_PASSWORD", out authToken))
{
QAG.ConsoleDebug.LogError("EpicGameClient: No auth token provided by Epic Launcher, login will fallback to account portal!");
}
LoginOptions options = new LoginOptions();
var credentials = new Credentials();
credentials.Type = LoginCredentialType.ExchangeCode;
credentials.ExternalType = ExternalCredentialType.Epic;
credentials.Token = authToken;
options.Credentials = credentials;
options.ScopeFlags = kScopeFlags;
TryLoginWithOptions(options, fallbackToAccountPortal: true);
}
private void TryPersistentLogin()
{
QAG.ConsoleDebug.Log("EpicGameClient: Attempting Persistent Login...");
LoginOptions options = new LoginOptions();
var credentials = new Credentials();
credentials.Type = LoginCredentialType.PersistentAuth;
credentials.ExternalType = ExternalCredentialType.Epic;
options.Credentials = credentials;
options.ScopeFlags = kScopeFlags;
TryLoginWithOptions(options, fallbackToAccountPortal: true);
}
private void TryAccountPortalLogin()
{
QAG.ConsoleDebug.Log("EpicGameClient: Attempting Account Portal Login...");
LoginOptions options = new LoginOptions();
var credentials = new Credentials();
credentials.Type = LoginCredentialType.AccountPortal;
credentials.ExternalType = ExternalCredentialType.Epic;
options.Credentials = credentials;
options.ScopeFlags = kScopeFlags;
TryLoginWithOptions(options, fallbackToAccountPortal: false);
}
private void TryLoginWithEpicAccount(EpicAccountId epicAccountId, bool tryCreateUserOnInvalidUser)
{
// Try to login with the epic account, which might fail because it hasn't been connected or something?
EOSManager.Instance.StartConnectLoginWithEpicAccount(epicAccountId, onConnectInfo =>
{
QAG.ConsoleDebug.Log($"EpicGameClient: Epic Account Login Result: {onConnectInfo.ResultCode}");
// If this succeeds, we are good to go
if (onConnectInfo.ResultCode == Result.Success)
{
}
// If it fails, we have to do this other connect thing to turn my epic account id into...an epic account id??
else if (onConnectInfo.ResultCode == Result.InvalidUser)
{
if (tryCreateUserOnInvalidUser)
{
QAG.ConsoleDebug.Log("EpicGameClient: Attempting to create user product Id...");
EOSManager.Instance.CreateConnectUserWithContinuanceToken(onConnectInfo.ContinuanceToken, createUserCallbackInfo =>
{
QAG.ConsoleDebug.Log($"EpicGameClient: Create user callback result: {createUserCallbackInfo.ResultCode}");
// Now try again logging in with the epic account
if (createUserCallbackInfo.ResultCode == Result.Success)
{
TryLoginWithEpicAccount(epicAccountId, tryCreateUserOnInvalidUser: false);
}
// If we fail to connect the account...not sure what to do
else
{
QAG.ConsoleDebug.LogError("EpicGameClient: Failed to log in to epic account and creating connect user has already failed");
}
});
}
// If we've already tried creating the user and logging in fails...not sure what to do
else
{
QAG.ConsoleDebug.LogError("EpicGameClient: Failed to log in to epic account and creating connect user has already failed");
}
}
});
}
void IEOSCoroutineOwner.StartCoroutine(IEnumerator routine)
{
StartCoroutine(routine);
}
}
#else
// Stubbed out implementation for non epic builds where the sdk is not included
public class EpicGameClient : MonoBehaviour
{
public static void UnlockAchievement(string achievementName)
{
// Do nothing
}
public static void SetPresence(string localizedText)
{
// Do nothing
}
}
#endif
@sampenguin
Copy link

Thank you so much for posting this code! I was having a related issue with OpenID validation (going from EOS to UGS) and seeing how you fetched
var authInterface = EOSManager.Instance.GetEOSAuthInterface();
put me on the path to stumble across how to generate and send Unity a token properly! Prob saved me days of digging, thanks again!

Also for posterity if anyone else googles for this problem, this code worked for my issue:

// immediately after you successfully authenticate with EOS directly, do this to auth with UGS:
AuthInterface auth = EOSManager.Instance.GetEOSAuthInterface();
if (null == auth)
{
    Debug.LogError($"[Auth] Unable to GetEOSAuthInterface, is EOSManager present?");
    OnEOSLoginFailed();
    return;
}

CopyIdTokenOptions options = new CopyIdTokenOptions();
options.AccountId = selectedAccountId; // from loginCallbackInfo.SelectedAccountId;
Result result = auth.CopyIdToken(ref options, out localToken);
if (result != Result.Success)
{
    Debug.LogError($"[Auth] Error copying ID token: [{result}]");
    
    OnEOSLoginFailed();
    return;
}

await AuthenticationService.Instance.SignInWithOpenIdConnectAsync(openIDProviderName, localToken?.JsonWebToken);

Debug.Log($"[AuthenticationManager] ... Sign In Success! PlayerID: {AuthenticationService.Instance.PlayerId}"); //Display the Unity Authentication PlayerID

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment