Skip to content

Commit 5dc6bd6

Browse files
feat: change profile in-game [MTT-2809] (#636)
* Added a simple popup window to select a profile * Created profiles are saved and can be loaded in ulterior runs Co-authored-by: Sam Bellomo <[email protected]>
1 parent 13640be commit 5dc6bd6

File tree

14 files changed

+2631
-23
lines changed

14 files changed

+2631
-23
lines changed

Assets/BossRoom/Prefabs/UI/ProfilePopup.prefab

Lines changed: 2289 additions & 0 deletions
Large diffs are not rendered by default.

Assets/BossRoom/Prefabs/UI/ProfilePopup.prefab.meta

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/BossRoom/Scenes/MainMenu.unity

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:94ba7d5188c030aa53bd078d4044cc7c0deb9a542aac6f189e0f97b5a564a647
3-
size 90859
2+
oid sha256:adc6afd461a56bb9bba1d1a3e4ce253f004139e17262094795940a80cbc91c9b
3+
size 106380

Assets/BossRoom/Scripts/Game/ApplicationController.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ private void Awake()
4747
scope.BindAsSingle<LocalLobbyUser>();
4848
scope.BindAsSingle<LocalLobby>();
4949

50+
scope.BindAsSingle<ProfileManager>();
51+
5052
//these message channels are essential and persist for the lifetime of the lobby and relay services
5153
scope.BindMessageChannelInstance<UnityServiceErrorMessage>();
5254
scope.BindMessageChannelInstance<ConnectStatus>();

Assets/BossRoom/Scripts/Game/Client/State/ClientMainMenuState.cs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ public class ClientMainMenuState : GameStateBehaviour
2727
[SerializeField] IPUIMediator m_IPUIMediator;
2828
[SerializeField] Button m_LobbyButton;
2929
[SerializeField] GameObject m_SignInSpinner;
30+
[SerializeField] UIProfileSelector m_UIProfileSelector;
31+
32+
AuthenticationServiceFacade m_AuthServiceFacade;
33+
LocalLobbyUser m_LocalUser;
34+
LocalLobby m_LocalLobby;
35+
ProfileManager m_ProfileManager;
3036

3137
protected override void Awake()
3238
{
@@ -43,8 +49,14 @@ protected override void InitializeScope()
4349
}
4450

4551
[Inject]
46-
async void InjectDependenciesAndInitialize(AuthenticationServiceFacade authServiceFacade, LocalLobbyUser localUser, LocalLobby localLobby)
52+
async void InjectDependenciesAndInitialize(AuthenticationServiceFacade authServiceFacade, LocalLobbyUser localUser, LocalLobby localLobby, ProfileManager profileManager)
4753
{
54+
m_AuthServiceFacade = authServiceFacade;
55+
m_LocalUser = localUser;
56+
m_LocalLobby = localLobby;
57+
m_ProfileManager = profileManager;
58+
59+
4860
if (string.IsNullOrEmpty(Application.cloudProjectId))
4961
{
5062
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\"");
@@ -55,14 +67,15 @@ async void InjectDependenciesAndInitialize(AuthenticationServiceFacade authServi
5567
try
5668
{
5769
var unityAuthenticationInitOptions = new InitializationOptions();
58-
var profile = ProfileManager.Profile;
70+
var profile = m_ProfileManager.Profile;
5971
if (profile.Length > 0)
6072
{
6173
unityAuthenticationInitOptions.SetProfile(profile);
6274
}
6375

64-
await authServiceFacade.InitializeAndSignInAsync(unityAuthenticationInitOptions);
76+
await m_AuthServiceFacade.InitializeAndSignInAsync(unityAuthenticationInitOptions);
6577
OnAuthSignIn();
78+
m_ProfileManager.onProfileChanged += OnProfileChanged;
6679
}
6780
catch (Exception)
6881
{
@@ -76,9 +89,9 @@ void OnAuthSignIn()
7689

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

79-
localUser.ID = AuthenticationService.Instance.PlayerId;
92+
m_LocalUser.ID = AuthenticationService.Instance.PlayerId;
8093
// 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.
81-
localLobby.AddUser(localUser);
94+
m_LocalLobby.AddUser(m_LocalUser);
8295
}
8396

8497
void OnSignInFailed()
@@ -94,6 +107,23 @@ void OnSignInFailed()
94107
}
95108
}
96109

110+
async void OnProfileChanged()
111+
{
112+
m_LobbyButton.interactable = false;
113+
m_SignInSpinner.SetActive(true);
114+
await m_AuthServiceFacade.SwitchProfileAndReSignInAsync(m_ProfileManager.Profile);
115+
116+
m_LobbyButton.interactable = true;
117+
m_SignInSpinner.SetActive(false);
118+
119+
Debug.Log($"Signed in. Unity Player ID {AuthenticationService.Instance.PlayerId}");
120+
121+
// Updating LocalUser and LocalLobby
122+
m_LocalLobby.RemoveUser(m_LocalUser);
123+
m_LocalUser.ID = AuthenticationService.Instance.PlayerId;
124+
m_LocalLobby.AddUser(m_LocalUser);
125+
}
126+
97127
public void OnStartClicked()
98128
{
99129
m_LobbyUIMediator.ToggleJoinLobbyUI();
@@ -105,5 +135,10 @@ public void OnDirectIPClicked()
105135
m_LobbyUIMediator.Hide();
106136
m_IPUIMediator.Show();
107137
}
138+
139+
public void OnChangeProfileClicked()
140+
{
141+
m_UIProfileSelector.Show();
142+
}
108143
}
109144
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using TMPro;
2+
using Unity.Multiplayer.Samples.BossRoom.Shared;
3+
using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure;
4+
using UnityEngine;
5+
6+
namespace Unity.Multiplayer.Samples.BossRoom.Visual
7+
{
8+
public class ProfileListItemUI : MonoBehaviour
9+
{
10+
[SerializeField]
11+
TextMeshProUGUI m_ProfileNameText;
12+
13+
ProfileManager m_ProfileManager;
14+
15+
[Inject]
16+
void InjectDependency(ProfileManager profileManager)
17+
{
18+
m_ProfileManager = profileManager;
19+
}
20+
21+
public void SetProfileName(string profileName)
22+
{
23+
m_ProfileNameText.text = profileName;
24+
}
25+
26+
public void OnSelectClick()
27+
{
28+
m_ProfileManager.Profile = m_ProfileNameText.text;
29+
}
30+
31+
public void OnDeleteClick()
32+
{
33+
m_ProfileManager.DeleteProfile(m_ProfileNameText.text);
34+
}
35+
}
36+
}

Assets/BossRoom/Scripts/Game/Client/UI/ProfileListItemUI.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text.RegularExpressions;
4+
using Unity.Multiplayer.Samples.BossRoom.Shared;
5+
using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure;
6+
using UnityEngine;
7+
using UnityEngine.UI;
8+
9+
namespace Unity.Multiplayer.Samples.BossRoom.Visual
10+
{
11+
public class UIProfileSelector : MonoBehaviour
12+
{
13+
[SerializeField]
14+
ProfileListItemUI m_ProfileListItemPrototype;
15+
[SerializeField]
16+
InputField m_NewProfileField;
17+
[SerializeField]
18+
Button m_CreateProfileButton;
19+
[SerializeField]
20+
CanvasGroup m_CanvasGroup;
21+
[SerializeField]
22+
Graphic m_EmptyProfileListLabel;
23+
24+
List<ProfileListItemUI> m_ProfileListItems = new List<ProfileListItemUI>();
25+
26+
IInstanceResolver m_Container;
27+
ProfileManager m_ProfileManager;
28+
29+
[Inject]
30+
void InjectDependency(IInstanceResolver container, ProfileManager profileManager)
31+
{
32+
m_Container = container;
33+
m_ProfileManager = profileManager;
34+
}
35+
36+
void Awake()
37+
{
38+
m_ProfileListItemPrototype.gameObject.SetActive(false);
39+
Hide();
40+
m_CreateProfileButton.interactable = false;
41+
}
42+
43+
/// <summary>
44+
/// Added to the InputField component's OnValueChanged callback for the join code text.
45+
/// </summary>
46+
public void SanitizeProfileNameInputText()
47+
{
48+
m_NewProfileField.text = SanitizeProfileName(m_NewProfileField.text);
49+
m_CreateProfileButton.interactable = m_NewProfileField.text.Length > 0 && !m_ProfileManager.AvailableProfiles.Contains(m_NewProfileField.text);
50+
}
51+
52+
string SanitizeProfileName(string dirtyString)
53+
{
54+
return Regex.Replace(dirtyString, "[^a-zA-Z0-9]", "");
55+
}
56+
57+
public void OnNewProfileButtonPressed()
58+
{
59+
var profile = m_NewProfileField.text;
60+
if (!m_ProfileManager.AvailableProfiles.Contains(profile))
61+
{
62+
m_ProfileManager.CreateProfile(profile);
63+
m_ProfileManager.Profile = profile;
64+
}
65+
else
66+
{
67+
PopupManager.ShowPopupPanel("Could not create new Profile", "A profile already exists with this same name. Select one of the already existing profiles or create a new one.");
68+
}
69+
}
70+
71+
public void InitializeUI()
72+
{
73+
EnsureNumberOfActiveUISlots(m_ProfileManager.AvailableProfiles.Count);
74+
for (var i = 0; i < m_ProfileManager.AvailableProfiles.Count; i++)
75+
{
76+
var profileName = m_ProfileManager.AvailableProfiles[i];
77+
m_ProfileListItems[i].SetProfileName(profileName);
78+
}
79+
80+
m_EmptyProfileListLabel.enabled = m_ProfileManager.AvailableProfiles.Count == 0;
81+
}
82+
83+
void EnsureNumberOfActiveUISlots(int requiredNumber)
84+
{
85+
int delta = requiredNumber - m_ProfileListItems.Count;
86+
87+
for (int i = 0; i < delta; i++)
88+
{
89+
CreateProfileListItem();
90+
}
91+
92+
for (int i = 0; i < m_ProfileListItems.Count; i++)
93+
{
94+
m_ProfileListItems[i].gameObject.SetActive(i < requiredNumber);
95+
}
96+
}
97+
98+
void CreateProfileListItem()
99+
{
100+
var listItem = Instantiate(m_ProfileListItemPrototype.gameObject, m_ProfileListItemPrototype.transform.parent)
101+
.GetComponent<ProfileListItemUI>();
102+
m_ProfileListItems.Add(listItem);
103+
listItem.gameObject.SetActive(true);
104+
m_Container.InjectIn(listItem);
105+
}
106+
107+
public void Show()
108+
{
109+
m_CanvasGroup.alpha = 1f;
110+
m_CanvasGroup.blocksRaycasts = true;
111+
m_NewProfileField.text = "";
112+
InitializeUI();
113+
}
114+
115+
public void Hide()
116+
{
117+
m_CanvasGroup.alpha = 0f;
118+
m_CanvasGroup.blocksRaycasts = false;
119+
}
120+
}
121+
}

Assets/BossRoom/Scripts/Game/Client/UI/UIProfileSelector.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/BossRoom/Scripts/Game/Client/UI/UnityServicesUIHandler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ void HandleLobbyError(UnityServiceErrorMessage error)
6363
break;
6464
case LobbyExceptionReason.LobbyConflict:
6565
// LobbyConflict can have multiple causes. Let's add other solutions here if there's other situations that arise for this.
66-
UnityEngine.Debug.LogError($"Got service error {error.Message} with LobbyConflict. Possible conflict cause: Trying to play with two builds on the " +
67-
$"same machine. Please use command line arg '{ProfileManager.AuthProfileCommandLineArg} someName' to set a different auth profile.\n");
68-
PopupManager.ShowPopupPanel("Failed to join Lobby", "Failed to join Lobby due to a conflict. See logs for more details.");
66+
Debug.LogError($"Got service error {error.Message} with LobbyConflict. Possible conflict cause: Trying to play with two builds on the " +
67+
$"same machine. Please change profile in-game or use command line arg '{ProfileManager.AuthProfileCommandLineArg} someName' to set a different auth profile.\n");
68+
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.");
6969
break;
7070
case LobbyExceptionReason.NoOpenLobbies:
7171
PopupManager.ShowPopupPanel("Failed to join Lobby", "No accessible lobbies are currently available for quick-join.");

Assets/BossRoom/Scripts/Game/ConnectionManagement/GameNetPortal.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,14 @@ public class GameNetPortal : MonoBehaviour
100100

101101
private LocalLobby m_LocalLobby;
102102
private LobbyServiceFacade m_LobbyServiceFacade;
103+
private ProfileManager m_ProfileManager;
103104

104105
[Inject]
105-
private void InjectDependencies(LocalLobby localLobby, LobbyServiceFacade lobbyServiceFacade)
106+
private void InjectDependencies(LocalLobby localLobby, LobbyServiceFacade lobbyServiceFacade, ProfileManager profileManager)
106107
{
107108
m_LocalLobby = localLobby;
108109
m_LobbyServiceFacade = lobbyServiceFacade;
110+
m_ProfileManager = profileManager;
109111
}
110112

111113
private void Awake()
@@ -260,10 +262,10 @@ public string GetPlayerId()
260262
{
261263
if (UnityServices.State != ServicesInitializationState.Initialized)
262264
{
263-
return ClientPrefs.GetGuid() + ProfileManager.Profile;
265+
return ClientPrefs.GetGuid() + m_ProfileManager.Profile;
264266
}
265267

266-
return AuthenticationService.Instance.IsSignedIn ? AuthenticationService.Instance.PlayerId : ClientPrefs.GetGuid() + ProfileManager.Profile;
268+
return AuthenticationService.Instance.IsSignedIn ? AuthenticationService.Instance.PlayerId : ClientPrefs.GetGuid() + m_ProfileManager.Profile;
267269
}
268270
}
269271
}

0 commit comments

Comments
 (0)