Skip to content

feat: change profile in-game [MTT-2809] #636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,289 changes: 2,289 additions & 0 deletions Assets/BossRoom/Prefabs/UI/ProfilePopup.prefab

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions Assets/BossRoom/Prefabs/UI/ProfilePopup.prefab.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Assets/BossRoom/Scenes/MainMenu.unity
Git LFS file not shown
45 changes: 40 additions & 5 deletions Assets/BossRoom/Scripts/Client/Game/State/ClientMainMenuState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public class ClientMainMenuState : GameStateBehaviour
[SerializeField] IPUIMediator m_IPUIMediator;
[SerializeField] Button m_LobbyButton;
[SerializeField] GameObject m_SignInSpinner;
[SerializeField] UIProfileSelector m_UIProfileSelector;

AuthenticationServiceFacade m_AuthServiceFacade;
LocalLobbyUser m_LocalUser;
LocalLobby m_LocalLobby;
ProfileManager m_ProfileManager;

protected override void Awake()
{
Expand All @@ -43,8 +49,14 @@ protected override void InitializeScope()
}

[Inject]
async void InjectDependenciesAndInitialize(AuthenticationServiceFacade authServiceFacade, LocalLobbyUser localUser, LocalLobby localLobby)
async void InjectDependenciesAndInitialize(AuthenticationServiceFacade authServiceFacade, LocalLobbyUser localUser, LocalLobby localLobby, ProfileManager profileManager)
{
m_AuthServiceFacade = authServiceFacade;
m_LocalUser = localUser;
m_LocalLobby = localLobby;
m_ProfileManager = profileManager;


if (string.IsNullOrEmpty(Application.cloudProjectId))
{
PopupManager.ShowPopupPanel("Unity Gaming Services ProjectID not set up" ,"Click the Readme file in the Assets Folder within the Project window in-editor to follow \"How to set up Unity Gaming Services\"");
Expand All @@ -55,14 +67,15 @@ async void InjectDependenciesAndInitialize(AuthenticationServiceFacade authServi
try
{
var unityAuthenticationInitOptions = new InitializationOptions();
var profile = ProfileManager.Profile;
var profile = m_ProfileManager.Profile;
if (profile.Length > 0)
{
unityAuthenticationInitOptions.SetProfile(profile);
}

await authServiceFacade.InitializeAndSignInAsync(unityAuthenticationInitOptions);
await m_AuthServiceFacade.InitializeAndSignInAsync(unityAuthenticationInitOptions);
OnAuthSignIn();
m_ProfileManager.onProfileChanged += OnProfileChanged;
}
catch (Exception)
{
Expand All @@ -76,9 +89,9 @@ void OnAuthSignIn()

Debug.Log($"Signed in. Unity Player ID {AuthenticationService.Instance.PlayerId}");

localUser.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.
localLobby.AddUser(localUser);
m_LocalLobby.AddUser(m_LocalUser);
}

void OnSignInFailed()
Expand All @@ -94,6 +107,23 @@ void OnSignInFailed()
}
}

async void OnProfileChanged()
{
m_LobbyButton.interactable = false;
m_SignInSpinner.SetActive(true);
await m_AuthServiceFacade.SwitchProfileAndReSignInAsync(m_ProfileManager.Profile);

m_LobbyButton.interactable = true;
m_SignInSpinner.SetActive(false);

Debug.Log($"Signed in. Unity Player ID {AuthenticationService.Instance.PlayerId}");

// Updating LocalUser and LocalLobby
m_LocalLobby.RemoveUser(m_LocalUser);
m_LocalUser.ID = AuthenticationService.Instance.PlayerId;
m_LocalLobby.AddUser(m_LocalUser);
}

public void OnStartClicked()
{
m_LobbyUIMediator.ToggleJoinLobbyUI();
Expand All @@ -105,5 +135,10 @@ public void OnDirectIPClicked()
m_LobbyUIMediator.Hide();
m_IPUIMediator.Show();
}

public void OnChangeProfileClicked()
{
m_UIProfileSelector.Show();
}
}
}
36 changes: 36 additions & 0 deletions Assets/BossRoom/Scripts/Client/UI/ProfileListItemUI.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using TMPro;
using Unity.Multiplayer.Samples.BossRoom.Shared;
using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure;
using UnityEngine;

namespace Unity.Multiplayer.Samples.BossRoom.Visual
{
public class ProfileListItemUI : MonoBehaviour
{
[SerializeField]
TextMeshProUGUI m_ProfileNameText;

ProfileManager m_ProfileManager;

[Inject]
void InjectDependency(ProfileManager profileManager)
{
m_ProfileManager = profileManager;
}

public void SetProfileName(string profileName)
{
m_ProfileNameText.text = profileName;
}

public void OnSelectClick()
{
m_ProfileManager.Profile = m_ProfileNameText.text;
}

public void OnDeleteClick()
{
m_ProfileManager.DeleteProfile(m_ProfileNameText.text);
}
}
}
3 changes: 3 additions & 0 deletions Assets/BossRoom/Scripts/Client/UI/ProfileListItemUI.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

121 changes: 121 additions & 0 deletions Assets/BossRoom/Scripts/Client/UI/UIProfileSelector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Unity.Multiplayer.Samples.BossRoom.Shared;
using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure;
using UnityEngine;
using UnityEngine.UI;

namespace Unity.Multiplayer.Samples.BossRoom.Visual
{
public class UIProfileSelector : MonoBehaviour
{
[SerializeField]
ProfileListItemUI m_ProfileListItemPrototype;
[SerializeField]
InputField m_NewProfileField;
[SerializeField]
Button m_CreateProfileButton;
[SerializeField]
CanvasGroup m_CanvasGroup;
[SerializeField]
Graphic m_EmptyProfileListLabel;

List<ProfileListItemUI> m_ProfileListItems = new List<ProfileListItemUI>();

IInstanceResolver m_Container;
ProfileManager m_ProfileManager;

[Inject]
void InjectDependency(IInstanceResolver container, ProfileManager profileManager)
{
m_Container = container;
m_ProfileManager = profileManager;
}

void Awake()
{
m_ProfileListItemPrototype.gameObject.SetActive(false);
Hide();
m_CreateProfileButton.interactable = false;
}

/// <summary>
/// Added to the InputField component's OnValueChanged callback for the join code text.
/// </summary>
public void SanitizeProfileNameInputText()
{
m_NewProfileField.text = SanitizeProfileName(m_NewProfileField.text);
m_CreateProfileButton.interactable = m_NewProfileField.text.Length > 0 && !m_ProfileManager.AvailableProfiles.Contains(m_NewProfileField.text);
}

string SanitizeProfileName(string dirtyString)
{
return Regex.Replace(dirtyString, "[^a-zA-Z0-9]", "");
}

public void OnNewProfileButtonPressed()
{
var profile = m_NewProfileField.text;
if (!m_ProfileManager.AvailableProfiles.Contains(profile))
{
m_ProfileManager.CreateProfile(profile);
m_ProfileManager.Profile = profile;
}
else
{
PopupManager.ShowPopupPanel("Could not create new Profile", "A profile already exists with this same. Select one of the already existing profiles or create a new one.");
}
}

public void InitializeUI()
{
EnsureNumberOfActiveUISlots(m_ProfileManager.AvailableProfiles.Count);
for (var i = 0; i < m_ProfileManager.AvailableProfiles.Count; i++)
{
var profileName = m_ProfileManager.AvailableProfiles[i];
m_ProfileListItems[i].SetProfileName(profileName);
}

m_EmptyProfileListLabel.enabled = m_ProfileManager.AvailableProfiles.Count == 0;
}

void EnsureNumberOfActiveUISlots(int requiredNumber)
{
int delta = requiredNumber - m_ProfileListItems.Count;

for (int i = 0; i < delta; i++)
{
CreateProfileListItem();
}

for (int i = 0; i < m_ProfileListItems.Count; i++)
{
m_ProfileListItems[i].gameObject.SetActive(i < requiredNumber);
}
}

void CreateProfileListItem()
{
var listItem = Instantiate(m_ProfileListItemPrototype.gameObject, m_ProfileListItemPrototype.transform.parent)
.GetComponent<ProfileListItemUI>();
m_ProfileListItems.Add(listItem);
listItem.gameObject.SetActive(true);
m_Container.InjectIn(listItem);
}

public void Show()
{
m_CanvasGroup.alpha = 1f;
m_CanvasGroup.blocksRaycasts = true;
m_NewProfileField.text = "";
InitializeUI();
}

public void Hide()
{
m_CanvasGroup.alpha = 0f;
m_CanvasGroup.blocksRaycasts = false;
}
}
}
11 changes: 11 additions & 0 deletions Assets/BossRoom/Scripts/Client/UI/UIProfileSelector.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Assets/BossRoom/Scripts/Client/UI/UnityServicesUIHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ void HandleLobbyError(UnityServiceErrorMessage error)
case LobbyExceptionReason.LobbyConflict:
// LobbyConflict can have multiple causes. Let's add other solutions here if there's other situations that arise for this.
Debug.LogError($"Got service error {error.Message} with LobbyConflict. Possible conflict cause: Trying to play with two builds on the " +
$"same machine. Please use command line arg '{ProfileManager.AuthProfileCommandLineArg} someName' to set a different auth profile.\n");
PopupManager.ShowPopupPanel("Failed to join Lobby", "Failed to join Lobby due to a conflict. See logs for more details.");
$"same machine. Please change profile in-game or use command line arg '{ProfileManager.AuthProfileCommandLineArg} someName' to set a different auth profile.\n");
PopupManager.ShowPopupPanel("Failed to join Lobby", "Failed to join Lobby due to a conflict. If trying to connect two local builds to the same lobby, they need to have different profiles. See logs for more details.");
break;
case LobbyExceptionReason.NoOpenLobbies:
PopupManager.ShowPopupPanel("Failed to join Lobby", "No accessible lobbies are currently available for quick-join.");
Expand Down
2 changes: 2 additions & 0 deletions Assets/BossRoom/Scripts/Shared/ApplicationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ private void Awake()
scope.BindAsSingle<LocalLobbyUser>();
scope.BindAsSingle<LocalLobby>();

scope.BindAsSingle<ProfileManager>();

//these message channels are essential and persist for the lifetime of the lobby and relay services
scope.BindMessageChannelInstance<UnityServiceErrorMessage>();
scope.BindMessageChannelInstance<ConnectStatus>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,14 @@ public class GameNetPortal : MonoBehaviour

private LocalLobby m_LocalLobby;
private LobbyServiceFacade m_LobbyServiceFacade;
private ProfileManager m_ProfileManager;

[Inject]
private void InjectDependencies(LocalLobby localLobby, LobbyServiceFacade lobbyServiceFacade)
private void InjectDependencies(LocalLobby localLobby, LobbyServiceFacade lobbyServiceFacade, ProfileManager profileManager)
{
m_LocalLobby = localLobby;
m_LobbyServiceFacade = lobbyServiceFacade;
m_ProfileManager = profileManager;
}

private void Awake()
Expand Down Expand Up @@ -252,10 +254,10 @@ public string GetPlayerId()
{
if (UnityServices.State != ServicesInitializationState.Initialized)
{
return ClientPrefs.GetGuid() + ProfileManager.Profile;
return ClientPrefs.GetGuid() + m_ProfileManager.Profile;
}

return AuthenticationService.Instance.IsSignedIn ? AuthenticationService.Instance.PlayerId : ClientPrefs.GetGuid() + ProfileManager.Profile;
return AuthenticationService.Instance.IsSignedIn ? AuthenticationService.Instance.PlayerId : ClientPrefs.GetGuid() + m_ProfileManager.Profile;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,26 @@ public async Task InitializeAndSignInAsync(InitializationOptions initializationO
}
}

public async Task SwitchProfileAndReSignInAsync(string profile)
{
if (AuthenticationService.Instance.IsSignedIn)
{
AuthenticationService.Instance.SignOut();
}
AuthenticationService.Instance.SwitchProfile(profile);

try
{
await AuthenticationService.Instance.SignInAnonymouslyAsync();
}
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<bool> EnsurePlayerIsAuthorized()
{
if (AuthenticationService.Instance.IsAuthorized)
Expand Down
Loading