// // /*=============================================================================== // // Copyright (C) 2024 PhantomsXR Ltd. All Rights Reserved. // // // // This file is part of the Phantom.XRMOD.GameServices.Runtime. // // // // The XR-MOD cannot be copied, distributed, or made available to // // third-parties for commercial purposes without written permission of PhantomsXR Ltd. // // // // Contact nswell@phantomsxr.com for licensing requests. // // ===============================================================================*/ using System; using System.Text.RegularExpressions; using System.Threading.Tasks; using Unity.Services.Authentication; using Unity.Services.Core; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.Events; using UnityEngine.Serialization; namespace Phantom.XRMOD.GameServices.Runtime { /// /// The core manager for XRMOD Game Services. /// Handles initialization and coordination of various game service modules like Voice, Friends, etc. /// public class XRMODGameServicesManager : MonoBehaviour { /// /// Singleton instance of the XRMODGameServicesManager. /// public static XRMODGameServicesManager GetInstance { get; private set; } /// /// Whether the Friend system should be initialized. /// public bool EnableFriend = true; /// /// Whether the Voice system should be initialized. /// [Header("Voice Properties")] public bool EnableVoice = true; /// /// Configuration arguments for the Voice system. /// [SerializeField] private ConfigurationArgs voiceConfigurationArgs; /// /// 3D audio properties for the Voice system. /// [SerializeField] private Voice3DProperties voice3DProperties; /// /// Invoked when all enabled Game Services are ready. /// public UnityEvent GameServicesReady; /// /// Invoked when Game Services initialization fails. /// public UnityEvent GameServicesFailed; private bool authReady; private bool voiceReady; private bool friendReady; private bool readyInvoked; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] static void Initialize() { var tmp_XRMODServicesManager = FindAnyObjectByType(); if (tmp_XRMODServicesManager == null) { GameObject tmp_XRMODGameServices = new GameObject("XRMOD GameServices", new[] { typeof(XRMODGameServicesManager) }); } } private void Awake() { DontDestroyOnLoad(this.gameObject); GetInstance = this; voice3DProperties ??= new Voice3DProperties(); authReady = false; voiceReady = false; friendReady = false; readyInvoked = false; } /// /// Initializes Unity Services with an optional profile name. /// This is the entry point for starting all XRMOD game services. /// /// Optional profile name for authentication (e.g., for multi-login testing). /// /// /// XRMODGameServicesManager.GetInstance.InitializeUnityServices("Player1"); /// /// /// /// Ensure this is called before using any other game services. /// Profile names must be alphanumeric. /// public async void InitializeUnityServices(string _profileName = null) { if (UnityServices.State != ServicesInitializationState.Uninitialized) return; UnityServices.Initialized += OnUnityServicesInitialized; UnityServices.InitializeFailed += OnUnityServicesInitializeFailed; if (_profileName != null) { //ProfileNames can't contain non-alphanumeric characters var tmp_Rgx = new Regex("[^a-zA-Z0-9 -]"); _profileName = tmp_Rgx.Replace(_profileName, ""); var tmp_AuthProfile = new InitializationOptions().SetProfile(_profileName); //If you are using multiple unity services, make sure to initialize it only once before using your services. await UnityServices.InitializeAsync(tmp_AuthProfile); } else await UnityServices.InitializeAsync(); } /// /// Asynchronously initializes Unity Services with an optional profile name. /// Provides a Task that can be awaited. /// /// Optional profile name for authentication. /// A Task representing the initialization process. /// /// /// await XRMODGameServicesManager.GetInstance.InitializeUnityServicesAsync("TestUser"); /// /// public async Task InitializeUnityServicesAsync(string _profileName = null) { if (UnityServices.State != ServicesInitializationState.Uninitialized) return; UnityServices.Initialized += OnUnityServicesInitialized; UnityServices.InitializeFailed += OnUnityServicesInitializeFailed; if (_profileName != null) { //ProfileNames can't contain non-alphanumeric characters var tmp_Rgx = new Regex("[^a-zA-Z0-9 -]"); _profileName = tmp_Rgx.Replace(_profileName, ""); var tmp_AuthProfile = new InitializationOptions().SetProfile(_profileName); //If you are using multiple unity services, make sure to initialize it only once before using your services. await UnityServices.InitializeAsync(tmp_AuthProfile); } else await UnityServices.InitializeAsync(); } private void OnUnityServicesInitialized() { // Listen for Authentication sign-in first AuthenticationSystemManager.SignedInCallback += OnAuthSignedIn; // Ensure auth service callbacks are wired AuthenticationSystemManager.InitServices(); } private void OnAuthSignedIn() { authReady = true; // Subscribe to voice login success before initializing, to avoid missing the event if (EnableVoice) { #if USE_VIVOX || USE_AGORA_RTC || USE_PHOTON_VOICE Phantom.XRMOD.ActionNotification.Runtime.ActionNotificationCenter.DefaultCenter.AddObserver( _data => { if (_data is VoiceNotificationDataArgs v && v.VoiceEventKey == VoiceNotificationKey.OnLogged) { voiceReady = true; CheckReady(); } }, VoiceModuleNotifyActionKey.CONST_VOICE_EVENT_KEY); #endif VoiceSystemManager.GetInstance.Initialize(JsonUtility.ToJson(voiceConfigurationArgs), voice3DProperties); } // Subscribe to friend sign-in notification and initialize if (EnableFriend) { var tmp_RelMgr = RelationshipsManager.GetInstance; tmp_RelMgr.FriendSignedIn += OnFriendSignedIn; tmp_RelMgr.InitFriendProvider(AuthenticationSystemManager.GetUserInfo()); } // If neither Voice nor Friend is enabled, auth alone determines readiness CheckReady(); } private void OnFriendSignedIn() { friendReady = true; CheckReady(); } private void CheckReady() { if (readyInvoked) return; // Determine required conditions bool tmp_NeedVoice = EnableVoice; bool tmp_NeedFriend = EnableFriend; bool tmp_ConditionsMet = authReady && (!tmp_NeedVoice || voiceReady) && (!tmp_NeedFriend || friendReady); if (tmp_ConditionsMet) { readyInvoked = true; GameServicesReady?.Invoke(); } } private void OnUnityServicesInitializeFailed(Exception _exception) { GameServicesFailed?.Invoke(); } } }