mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2024-12-29 12:21:25 +00:00
465 lines
16 KiB
C#
465 lines
16 KiB
C#
using Epic.OnlineServices;
|
|
using Epic.OnlineServices.Logging;
|
|
using Epic.OnlineServices.Platform;
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// Manages the Epic Online Services SDK
|
|
/// Do not destroy this component!
|
|
/// The Epic Online Services SDK can only be initialized once,
|
|
/// after releasing the SDK the game has to be restarted in order to initialize the SDK again.
|
|
/// In the unity editor the OnDestroy function will not run so that we dont have to restart the editor after play.
|
|
/// </summary>
|
|
namespace EpicTransport;
|
|
|
|
[DefaultExecutionOrder(-32000)]
|
|
public class EOSSDKComponent : MonoBehaviour
|
|
{
|
|
// Unity Inspector shown variables
|
|
|
|
[SerializeField]
|
|
public EosApiKey apiKeys;
|
|
|
|
[Header("User Login")]
|
|
public bool authInterfaceLogin = false;
|
|
|
|
public Epic.OnlineServices.Auth.LoginCredentialType authInterfaceCredentialType = Epic.OnlineServices.Auth.LoginCredentialType.AccountPortal;
|
|
public uint devAuthToolPort = 7878;
|
|
public string devAuthToolCredentialName = "";
|
|
public ExternalCredentialType connectInterfaceCredentialType = ExternalCredentialType.DeviceidAccessToken;
|
|
public string deviceModel = "PC Windows 64bit";
|
|
[SerializeField] private string displayName = "User";
|
|
|
|
public static string DisplayName
|
|
{
|
|
get => Instance.displayName;
|
|
set => Instance.displayName = value;
|
|
}
|
|
|
|
[Header("Misc")]
|
|
public LogLevel epicLoggerLevel = LogLevel.Error;
|
|
|
|
[SerializeField]
|
|
private bool collectPlayerMetrics = true;
|
|
|
|
public static bool CollectPlayerMetrics => Instance.collectPlayerMetrics;
|
|
|
|
public bool checkForEpicLauncherAndRestart = false;
|
|
public bool delayedInitialization = false;
|
|
public float platformTickIntervalInSeconds = 0.0f;
|
|
private float platformTickTimer = 0f;
|
|
public uint tickBudgetInMilliseconds = 0;
|
|
|
|
// End Unity Inspector shown variables
|
|
|
|
private ulong authExpirationHandle;
|
|
|
|
private string authInterfaceLoginCredentialId = null;
|
|
public static void SetAuthInterfaceLoginCredentialId(string credentialId) => Instance.authInterfaceLoginCredentialId = credentialId;
|
|
private string authInterfaceCredentialToken = null;
|
|
public static void SetAuthInterfaceCredentialToken(string credentialToken) => Instance.authInterfaceCredentialToken = credentialToken;
|
|
private string connectInterfaceCredentialToken = null;
|
|
public static void SetConnectInterfaceCredentialToken(string credentialToken) => Instance.connectInterfaceCredentialToken = credentialToken;
|
|
|
|
private PlatformInterface EOS;
|
|
|
|
// Interfaces
|
|
public static Epic.OnlineServices.Achievements.AchievementsInterface GetAchievementsInterface() => Instance.EOS.GetAchievementsInterface();
|
|
public static Epic.OnlineServices.Auth.AuthInterface GetAuthInterface() => Instance.EOS.GetAuthInterface();
|
|
public static Epic.OnlineServices.Connect.ConnectInterface GetConnectInterface() => Instance.EOS.GetConnectInterface();
|
|
public static Epic.OnlineServices.Ecom.EcomInterface GetEcomInterface() => Instance.EOS.GetEcomInterface();
|
|
public static Epic.OnlineServices.Friends.FriendsInterface GetFriendsInterface() => Instance.EOS.GetFriendsInterface();
|
|
public static Epic.OnlineServices.Leaderboards.LeaderboardsInterface GetLeaderboardsInterface() => Instance.EOS.GetLeaderboardsInterface();
|
|
public static Epic.OnlineServices.Lobby.LobbyInterface GetLobbyInterface() => Instance.EOS.GetLobbyInterface();
|
|
public static Epic.OnlineServices.Metrics.MetricsInterface GetMetricsInterface() => Instance.EOS.GetMetricsInterface(); // Handled by the transport automatically, only use this interface if Mirror is not used for singleplayer
|
|
public static Epic.OnlineServices.Mods.ModsInterface GetModsInterface() => Instance.EOS.GetModsInterface();
|
|
public static Epic.OnlineServices.P2P.P2PInterface GetP2PInterface() => Instance.EOS.GetP2PInterface();
|
|
public static Epic.OnlineServices.PlayerDataStorage.PlayerDataStorageInterface GetPlayerDataStorageInterface() => Instance.EOS.GetPlayerDataStorageInterface();
|
|
public static Epic.OnlineServices.Presence.PresenceInterface GetPresenceInterface() => Instance.EOS.GetPresenceInterface();
|
|
public static Epic.OnlineServices.Sessions.SessionsInterface GetSessionsInterface() => Instance.EOS.GetSessionsInterface();
|
|
public static Epic.OnlineServices.TitleStorage.TitleStorageInterface GetTitleStorageInterface() => Instance.EOS.GetTitleStorageInterface();
|
|
public static Epic.OnlineServices.UI.UIInterface GetUIInterface() => Instance.EOS.GetUIInterface();
|
|
public static Epic.OnlineServices.UserInfo.UserInfoInterface GetUserInfoInterface() => Instance.EOS.GetUserInfoInterface();
|
|
|
|
protected EpicAccountId localUserAccountId;
|
|
public static EpicAccountId LocalUserAccountId => Instance.localUserAccountId;
|
|
|
|
protected string localUserAccountIdString;
|
|
public static string LocalUserAccountIdString => Instance.localUserAccountIdString;
|
|
|
|
protected ProductUserId localUserProductId;
|
|
public static ProductUserId LocalUserProductId => Instance.localUserProductId;
|
|
|
|
protected string localUserProductIdString;
|
|
public static string LocalUserProductIdString => Instance.localUserProductIdString;
|
|
|
|
protected bool initialized;
|
|
public static bool Initialized => Instance.initialized;
|
|
|
|
protected bool isConnecting;
|
|
public static bool IsConnecting => Instance.isConnecting;
|
|
|
|
protected static EOSSDKComponent instance;
|
|
|
|
protected static EOSSDKComponent Instance
|
|
{
|
|
get
|
|
{
|
|
if (instance == null)
|
|
{
|
|
return new GameObject("EOSSDKComponent").AddComponent<EOSSDKComponent>();
|
|
}
|
|
else
|
|
{
|
|
return instance;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void Tick()
|
|
{
|
|
instance.platformTickTimer -= Time.deltaTime;
|
|
instance.EOS.Tick();
|
|
}
|
|
|
|
// 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_WIN
|
|
[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 libraryPointer;
|
|
#endif
|
|
|
|
#if UNITY_EDITOR_LINUX
|
|
[DllImport("libdl.so", EntryPoint = "dlopen")]
|
|
private static extern IntPtr LoadLibrary(String lpFileName, int flags = 2);
|
|
|
|
[DllImport("libdl.so", EntryPoint = "dlclose")]
|
|
private static extern int FreeLibrary(IntPtr hLibModule);
|
|
|
|
[DllImport("libdl.so")]
|
|
private static extern IntPtr dlsym(IntPtr handle, String symbol);
|
|
|
|
[DllImport("libdl.so")]
|
|
private static extern IntPtr dlerror();
|
|
|
|
private static IntPtr GetProcAddress(IntPtr hModule, string lpProcName) {
|
|
// clear previous errors if any
|
|
dlerror();
|
|
var res = dlsym(hModule, lpProcName);
|
|
var errPtr = dlerror();
|
|
if (errPtr != IntPtr.Zero) {
|
|
throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
|
|
}
|
|
return res;
|
|
}
|
|
private IntPtr libraryPointer;
|
|
#endif
|
|
|
|
private void Awake()
|
|
{
|
|
// Initialize Java version of the SDK with a reference to the VM with JNI
|
|
// See https://eoshelp.epicgames.com/s/question/0D54z00006ufJBNCA2/cant-get-createdeviceid-to-work-in-unity-android-c-sdk?language=en_US
|
|
if (Application.platform == RuntimePlatform.Android)
|
|
{
|
|
var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
|
|
var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
|
|
var context = activity.Call<AndroidJavaObject>("getApplicationContext");
|
|
var EOS_SDK_JAVA = new AndroidJavaClass("com.epicgames.mobile.eossdk.EOSSDK");
|
|
EOS_SDK_JAVA.CallStatic("init", context);
|
|
}
|
|
|
|
// Prevent multiple instances
|
|
if (instance != null)
|
|
{
|
|
Destroy(gameObject);
|
|
return;
|
|
}
|
|
|
|
instance = this;
|
|
|
|
#if UNITY_EDITOR
|
|
var libraryPath = "Assets/Mirror/Runtime/Transport/EpicOnlineTransport/EOSSDK/" + Config.LibraryName;
|
|
|
|
libraryPointer = LoadLibrary(libraryPath);
|
|
if (libraryPointer == IntPtr.Zero) {
|
|
throw new Exception("Failed to load library" + libraryPath);
|
|
}
|
|
|
|
Bindings.Hook(libraryPointer, GetProcAddress);
|
|
#endif
|
|
|
|
if (!delayedInitialization)
|
|
{
|
|
Initialize();
|
|
}
|
|
}
|
|
|
|
protected void InitializeImplementation()
|
|
{
|
|
isConnecting = true;
|
|
|
|
var initializeOptions = new InitializeOptions()
|
|
{
|
|
ProductName = apiKeys.epicProductName,
|
|
ProductVersion = apiKeys.epicProductVersion
|
|
};
|
|
|
|
var initializeResult = PlatformInterface.Initialize(initializeOptions);
|
|
|
|
// This code is called each time the game is run in the editor, so we catch the case where the SDK has already been initialized in the editor.
|
|
var isAlreadyConfiguredInEditor = Application.isEditor && initializeResult == Result.AlreadyConfigured;
|
|
if (initializeResult != Result.Success && !isAlreadyConfiguredInEditor)
|
|
{
|
|
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.
|
|
LoggingInterface.SetLogLevel(LogCategory.AllCategories, epicLoggerLevel);
|
|
LoggingInterface.SetCallback(message => Logger.EpicDebugLog(message));
|
|
|
|
var options = new Options()
|
|
{
|
|
ProductId = apiKeys.epicProductId,
|
|
SandboxId = apiKeys.epicSandboxId,
|
|
DeploymentId = apiKeys.epicDeploymentId,
|
|
ClientCredentials = new ClientCredentials()
|
|
{
|
|
ClientId = apiKeys.epicClientId,
|
|
ClientSecret = apiKeys.epicClientSecret
|
|
},
|
|
TickBudgetInMilliseconds = tickBudgetInMilliseconds
|
|
};
|
|
|
|
EOS = PlatformInterface.Create(options);
|
|
if (EOS == null)
|
|
{
|
|
throw new System.Exception("Failed to create platform");
|
|
}
|
|
|
|
if (checkForEpicLauncherAndRestart)
|
|
{
|
|
var result = EOS.CheckForLauncherAndRestart();
|
|
|
|
// If not started through epic launcher the app will be restarted and we can quit
|
|
if (result != Result.NoChange)
|
|
{
|
|
// Log error if launcher check failed, but still quit to prevent hacking
|
|
if (result == Result.UnexpectedError)
|
|
{
|
|
Debug.LogError("Unexpected Error while checking if app was started through epic launcher");
|
|
}
|
|
|
|
Application.Quit();
|
|
}
|
|
}
|
|
|
|
// If we use the Auth interface then only login into the Connect interface after finishing the auth interface login
|
|
// If we don't use the Auth interface we can directly login to the Connect interface
|
|
if (authInterfaceLogin)
|
|
{
|
|
if (authInterfaceCredentialType == Epic.OnlineServices.Auth.LoginCredentialType.Developer)
|
|
{
|
|
authInterfaceLoginCredentialId = "localhost:" + devAuthToolPort;
|
|
authInterfaceCredentialToken = devAuthToolCredentialName;
|
|
}
|
|
|
|
// Login to Auth Interface
|
|
var loginOptions = new Epic.OnlineServices.Auth.LoginOptions()
|
|
{
|
|
Credentials = new Epic.OnlineServices.Auth.Credentials()
|
|
{
|
|
Type = authInterfaceCredentialType,
|
|
Id = authInterfaceLoginCredentialId,
|
|
Token = authInterfaceCredentialToken
|
|
},
|
|
ScopeFlags = Epic.OnlineServices.Auth.AuthScopeFlags.BasicProfile | Epic.OnlineServices.Auth.AuthScopeFlags.FriendsList | Epic.OnlineServices.Auth.AuthScopeFlags.Presence
|
|
};
|
|
|
|
EOS.GetAuthInterface().Login(loginOptions, null, OnAuthInterfaceLogin);
|
|
}
|
|
else
|
|
{
|
|
// Login to Connect Interface
|
|
if (connectInterfaceCredentialType == ExternalCredentialType.DeviceidAccessToken)
|
|
{
|
|
var createDeviceIdOptions = new Epic.OnlineServices.Connect.CreateDeviceIdOptions();
|
|
createDeviceIdOptions.DeviceModel = deviceModel;
|
|
EOS.GetConnectInterface().CreateDeviceId(createDeviceIdOptions, null, OnCreateDeviceId);
|
|
}
|
|
else
|
|
{
|
|
ConnectInterfaceLogin();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
if (Instance.initialized || Instance.isConnecting)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Instance.InitializeImplementation();
|
|
}
|
|
|
|
private void OnAuthInterfaceLogin(Epic.OnlineServices.Auth.LoginCallbackInfo loginCallbackInfo)
|
|
{
|
|
if (loginCallbackInfo.ResultCode == Result.Success)
|
|
{
|
|
Debug.Log("Auth Interface Login succeeded");
|
|
|
|
string accountIdString;
|
|
var result = loginCallbackInfo.LocalUserId.ToString(out accountIdString);
|
|
if (Result.Success == result)
|
|
{
|
|
Debug.Log("EOS User ID:" + accountIdString);
|
|
|
|
localUserAccountIdString = accountIdString;
|
|
localUserAccountId = loginCallbackInfo.LocalUserId;
|
|
}
|
|
|
|
ConnectInterfaceLogin();
|
|
}
|
|
else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
|
|
{
|
|
Debug.Log("Login returned " + loginCallbackInfo.ResultCode);
|
|
}
|
|
}
|
|
|
|
private void OnCreateDeviceId(Epic.OnlineServices.Connect.CreateDeviceIdCallbackInfo createDeviceIdCallbackInfo)
|
|
{
|
|
if (createDeviceIdCallbackInfo.ResultCode == Result.Success || createDeviceIdCallbackInfo.ResultCode == Result.DuplicateNotAllowed)
|
|
{
|
|
ConnectInterfaceLogin();
|
|
}
|
|
else if (Epic.OnlineServices.Common.IsOperationComplete(createDeviceIdCallbackInfo.ResultCode))
|
|
{
|
|
Debug.Log("Device ID creation returned " + createDeviceIdCallbackInfo.ResultCode);
|
|
}
|
|
}
|
|
|
|
private void ConnectInterfaceLogin()
|
|
{
|
|
var loginOptions = new Epic.OnlineServices.Connect.LoginOptions();
|
|
|
|
if (connectInterfaceCredentialType == ExternalCredentialType.Epic)
|
|
{
|
|
Epic.OnlineServices.Auth.Token token;
|
|
var result = EOS.GetAuthInterface().CopyUserAuthToken(new Epic.OnlineServices.Auth.CopyUserAuthTokenOptions(), localUserAccountId, out token);
|
|
|
|
if (result == Result.Success)
|
|
{
|
|
connectInterfaceCredentialToken = token.AccessToken;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Failed to retrieve User Auth Token");
|
|
}
|
|
}
|
|
else if (connectInterfaceCredentialType == ExternalCredentialType.DeviceidAccessToken)
|
|
{
|
|
loginOptions.UserLoginInfo = new Epic.OnlineServices.Connect.UserLoginInfo();
|
|
loginOptions.UserLoginInfo.DisplayName = displayName;
|
|
}
|
|
|
|
loginOptions.Credentials = new Epic.OnlineServices.Connect.Credentials();
|
|
loginOptions.Credentials.Type = connectInterfaceCredentialType;
|
|
loginOptions.Credentials.Token = connectInterfaceCredentialToken;
|
|
|
|
EOS.GetConnectInterface().Login(loginOptions, null, OnConnectInterfaceLogin);
|
|
}
|
|
|
|
private void OnConnectInterfaceLogin(Epic.OnlineServices.Connect.LoginCallbackInfo loginCallbackInfo)
|
|
{
|
|
if (loginCallbackInfo.ResultCode == Result.Success)
|
|
{
|
|
Debug.Log("Connect Interface Login succeeded");
|
|
|
|
string productIdString;
|
|
var result = loginCallbackInfo.LocalUserId.ToString(out productIdString);
|
|
if (Result.Success == result)
|
|
{
|
|
Debug.Log("EOS User Product ID:" + productIdString);
|
|
|
|
localUserProductIdString = productIdString;
|
|
localUserProductId = loginCallbackInfo.LocalUserId;
|
|
}
|
|
|
|
initialized = true;
|
|
isConnecting = false;
|
|
|
|
var authExpirationOptions = new Epic.OnlineServices.Connect.AddNotifyAuthExpirationOptions();
|
|
authExpirationHandle = EOS.GetConnectInterface().AddNotifyAuthExpiration(authExpirationOptions, null, OnAuthExpiration);
|
|
}
|
|
else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
|
|
{
|
|
Debug.Log("Login returned " + loginCallbackInfo.ResultCode + "\nRetrying...");
|
|
EOS.GetConnectInterface().CreateUser(new Epic.OnlineServices.Connect.CreateUserOptions() { ContinuanceToken = loginCallbackInfo.ContinuanceToken }, null, (Epic.OnlineServices.Connect.CreateUserCallbackInfo cb) =>
|
|
{
|
|
if (cb.ResultCode != Result.Success)
|
|
{
|
|
Debug.Log(cb.ResultCode);
|
|
return;
|
|
}
|
|
|
|
localUserProductId = cb.LocalUserId;
|
|
ConnectInterfaceLogin();
|
|
});
|
|
}
|
|
}
|
|
|
|
private void OnAuthExpiration(Epic.OnlineServices.Connect.AuthExpirationCallbackInfo authExpirationCallbackInfo)
|
|
{
|
|
Debug.Log("AuthExpiration callback");
|
|
EOS.GetConnectInterface().RemoveNotifyAuthExpiration(authExpirationHandle);
|
|
ConnectInterfaceLogin();
|
|
}
|
|
|
|
// Calling tick on a regular interval is required for callbacks to work.
|
|
private void LateUpdate()
|
|
{
|
|
if (EOS != null)
|
|
{
|
|
platformTickTimer += Time.deltaTime;
|
|
|
|
if (platformTickTimer >= platformTickIntervalInSeconds)
|
|
{
|
|
platformTickTimer = 0;
|
|
EOS.Tick();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
if (EOS != null)
|
|
{
|
|
EOS.Release();
|
|
EOS = null;
|
|
PlatformInterface.Shutdown();
|
|
}
|
|
|
|
// Unhook the library in the editor, this makes it possible to load the library again after stopping to play
|
|
#if UNITY_EDITOR
|
|
if (libraryPointer != IntPtr.Zero) {
|
|
Bindings.Unhook();
|
|
|
|
// Free until the module ref count is 0
|
|
while (FreeLibrary(libraryPointer) != 0) { }
|
|
|
|
libraryPointer = IntPtr.Zero;
|
|
}
|
|
#endif
|
|
}
|
|
}
|