diff --git a/Assets/Scripts/Gameplay/GameState/ClientMainMenuState.cs b/Assets/Scripts/Gameplay/GameState/ClientMainMenuState.cs index c1b2a691f..53da328ae 100644 --- a/Assets/Scripts/Gameplay/GameState/ClientMainMenuState.cs +++ b/Assets/Scripts/Gameplay/GameState/ClientMainMenuState.cs @@ -22,20 +22,31 @@ namespace Unity.BossRoom.Gameplay.GameState /// public class ClientMainMenuState : GameStateBehaviour { - public override GameState ActiveState { get { return GameState.MainMenu; } } - - [SerializeField] NameGenerationData m_NameGenerationData; - [SerializeField] LobbyUIMediator m_LobbyUIMediator; - [SerializeField] IPUIMediator m_IPUIMediator; - [SerializeField] Button m_LobbyButton; - [SerializeField] GameObject m_SignInSpinner; - [SerializeField] UIProfileSelector m_UIProfileSelector; - [SerializeField] UITooltipDetector m_UGSSetupTooltipDetector; - - [Inject] AuthenticationServiceFacade m_AuthServiceFacade; - [Inject] LocalLobbyUser m_LocalUser; - [Inject] LocalLobby m_LocalLobby; - [Inject] ProfileManager m_ProfileManager; + public override GameState ActiveState => GameState.MainMenu; + + [SerializeField] + NameGenerationData m_NameGenerationData; + [SerializeField] + LobbyUIMediator m_LobbyUIMediator; + [SerializeField] + IPUIMediator m_IPUIMediator; + [SerializeField] + Button m_LobbyButton; + [SerializeField] + GameObject m_SignInSpinner; + [SerializeField] + UIProfileSelector m_UIProfileSelector; + [SerializeField] + UITooltipDetector m_UGSSetupTooltipDetector; + + [Inject] + AuthenticationServiceFacade m_AuthServiceFacade; + [Inject] + LocalLobbyUser m_LocalUser; + [Inject] + LocalLobby m_LocalLobby; + [Inject] + ProfileManager m_ProfileManager; protected override void Awake() { @@ -61,17 +72,12 @@ protected override void Configure(IContainerBuilder builder) builder.RegisterComponent(m_IPUIMediator); } - private async void TrySignIn() { try { - var unityAuthenticationInitOptions = new InitializationOptions(); - var profile = m_ProfileManager.Profile; - if (profile.Length > 0) - { - unityAuthenticationInitOptions.SetProfile(profile); - } + var unityAuthenticationInitOptions = + m_AuthServiceFacade.GenerateAuthenticationOptions(m_ProfileManager.Profile); await m_AuthServiceFacade.InitializeAndSignInAsync(unityAuthenticationInitOptions); OnAuthSignIn(); @@ -92,6 +98,7 @@ private void OnAuthSignIn() Debug.Log($"Signed in. Unity Player ID {AuthenticationService.Instance.PlayerId}"); m_LocalUser.ID = AuthenticationService.Instance.PlayerId; + // The local LobbyUser object will be hooked into UI before the LocalLobby is populated during lobby join, so the LocalLobby must know about it already when that happens. m_LocalLobby.AddUser(m_LocalUser); } @@ -103,6 +110,7 @@ private void OnSignInFailed() m_LobbyButton.interactable = false; m_UGSSetupTooltipDetector.enabled = true; } + if (m_SignInSpinner) { m_SignInSpinner.SetActive(false); diff --git a/Assets/Scripts/Gameplay/UI/UIProfileSelector.cs b/Assets/Scripts/Gameplay/UI/UIProfileSelector.cs index c6300cb38..7699605df 100644 --- a/Assets/Scripts/Gameplay/UI/UIProfileSelector.cs +++ b/Assets/Scripts/Gameplay/UI/UIProfileSelector.cs @@ -26,6 +26,9 @@ public class UIProfileSelector : MonoBehaviour [Inject] IObjectResolver m_Resolver; [Inject] ProfileManager m_ProfileManager; + // Authentication service only accepts profile names of 30 characters or under + const int k_AuthenticationMaxProfileLength = 30; + void Awake() { m_ProfileListItemPrototype.gameObject.SetActive(false); @@ -44,7 +47,8 @@ public void SanitizeProfileNameInputText() string SanitizeProfileName(string dirtyString) { - return Regex.Replace(dirtyString, "[^a-zA-Z0-9]", ""); + var output = Regex.Replace(dirtyString, "[^a-zA-Z0-9]", ""); + return output[..Math.Min(output.Length, k_AuthenticationMaxProfileLength)]; } public void OnNewProfileButtonPressed() diff --git a/Assets/Scripts/UnityServices/Auth/AuthenticationServiceFacade.cs b/Assets/Scripts/UnityServices/Auth/AuthenticationServiceFacade.cs index c8f8396aa..ef26eceeb 100644 --- a/Assets/Scripts/UnityServices/Auth/AuthenticationServiceFacade.cs +++ b/Assets/Scripts/UnityServices/Auth/AuthenticationServiceFacade.cs @@ -10,7 +10,28 @@ namespace Unity.BossRoom.UnityServices.Auth { public class AuthenticationServiceFacade { - [Inject] IPublisher m_UnityServiceErrorMessagePublisher; + [Inject] + IPublisher m_UnityServiceErrorMessagePublisher; + + public InitializationOptions GenerateAuthenticationOptions(string profile) + { + try + { + var unityAuthenticationInitOptions = new InitializationOptions(); + if (profile.Length > 0) + { + unityAuthenticationInitOptions.SetProfile(profile); + } + + return unityAuthenticationInitOptions; + } + catch (Exception e) + { + var reason = $"{e.Message} ({e.InnerException?.Message})"; + m_UnityServiceErrorMessagePublisher.Publish(new UnityServiceErrorMessage("Authentication Error", reason, UnityServiceErrorMessage.Service.Authentication, e)); + throw; + } + } public async Task InitializeAndSignInAsync(InitializationOptions initializationOptions) { @@ -37,6 +58,7 @@ public async Task SwitchProfileAndReSignInAsync(string profile) { AuthenticationService.Instance.SignOut(); } + AuthenticationService.Instance.SwitchProfile(profile); try @@ -67,6 +89,7 @@ public async Task EnsurePlayerIsAuthorized() { var reason = $"{e.Message} ({e.InnerException?.Message})"; m_UnityServiceErrorMessagePublisher.Publish(new UnityServiceErrorMessage("Authentication Error", reason, UnityServiceErrorMessage.Service.Authentication, e)); + //not rethrowing for authentication exceptions - any failure to authenticate is considered "handled failure" return false; } @@ -78,6 +101,5 @@ public async Task EnsurePlayerIsAuthorized() throw; } } - } } diff --git a/Assets/Scripts/Utils/ProfileManager.cs b/Assets/Scripts/Utils/ProfileManager.cs index aa7c75713..466e60a8c 100644 --- a/Assets/Scripts/Utils/ProfileManager.cs +++ b/Assets/Scripts/Utils/ProfileManager.cs @@ -80,7 +80,9 @@ static string GetProfile() var hashedBytes = new MD5CryptoServiceProvider() .ComputeHash(Encoding.UTF8.GetBytes(Application.dataPath)); Array.Resize(ref hashedBytes, 16); - return new Guid(hashedBytes).ToString("N"); + // Authentication service only allows profile names of maximum 30 characters. We're generating a GUID based + // on the project's path. Truncating the first 30 characters of said GUID string suffices for uniqueness. + return new Guid(hashedBytes).ToString("N")[..30]; #else return ""; #endif diff --git a/CHANGELOG.md b/CHANGELOG.md index 13c130a4b..78ae0a7de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Additional documentation and release notes are available at [Multiplayer Documen * Replaced our custom pool implementation using queues with ObjectPool (#824) * Upgraded Boss Room to NGO 1.3.1 (#828) NetworkPrefabs inside NetworkManager's NetworkPrefabs list have been converted to NetworkPrefabsList ScriptableObject. * Upgraded Boss Room to NGO 1.4.0 (#829) +* Profile names generated are now only 30 characters or under to fit Authentication Service requirements (#831) ### Cleanup * Clarified a TODO comment inside ClientCharacter, detailing how anticipation should only be executed on owning client players (#786)