diff --git a/ColourMeOKGame/Assets/Scenes/GameScene.unity b/ColourMeOKGame/Assets/Scenes/GameScene.unity index 486551b..1caf66b 100644 --- a/ColourMeOKGame/Assets/Scenes/GameScene.unity +++ b/ColourMeOKGame/Assets/Scenes/GameScene.unity @@ -166,7 +166,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 133964670} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 @@ -459,6 +459,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 0cfc0b50e9003a0468ebdc186439c53b, type: 3} m_Name: m_EditorClassIdentifier: + state: 0 + uiGameObject: {fileID: 133964670} --- !u!4 &1204483826 Transform: m_ObjectHideFlags: 0 @@ -480,6 +482,6 @@ SceneRoots: m_Roots: - {fileID: 963194228} - {fileID: 705507995} - - {fileID: 133964672} - {fileID: 447905427} + - {fileID: 133964672} - {fileID: 1204483826} diff --git a/ColourMeOKGame/Assets/Scripts/AccountUI.cs b/ColourMeOKGame/Assets/Scripts/AccountUI.cs index 977f0ff..5b94677 100644 --- a/ColourMeOKGame/Assets/Scripts/AccountUI.cs +++ b/ColourMeOKGame/Assets/Scripts/AccountUI.cs @@ -78,11 +78,20 @@ public class AccountUI : MonoBehaviour /// private Button _usernameUpdateButton; - public void Start() + /// + /// start function to initialise the account view + /// + /// + private void Start() { + TransitionStateTo(State.NotSignedIn); if (state == State.UnassociatedState) throw new Exception("unreachable state"); - - // GameManager.Instance.Backend.RegisterOnSignInCallback(OnSignInCallback); + GameManager.Instance.Backend.RegisterOnSignInCallback(OnSignInCallback); + GameManager.Instance.RegisterOnLocalPlayerDataUpdate(data => + { + Debug.Log("local player data update callback, populating AccountView fields"); + PopulateFields(data.LastKnownUsername, data.LastKnownEmail); + }); } /// @@ -114,15 +123,11 @@ public void OnEnable() _secondaryActionButton = ui.Q private DatabaseReference _db; + /// + /// callback functions to be invoked when the user signs in + /// + private readonly List> _onSignInCallbacks = new(); + + /// + /// callback functions to be invoked when the user signs out + /// + private readonly List> _onSignOutCallbacks = new(); + /// /// the current user object, if authenticated /// @@ -95,9 +95,9 @@ public enum UserAccountDetailTargetEnum /// /// variable initialisation function /// - public void Initialise() + public void Initialise(Action callback) { - FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => + FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => { // cher is this robust enough switch (task.Result) @@ -107,6 +107,7 @@ public void Initialise() _auth.StateChanged += AuthStateChanged; _db = FirebaseDatabase.DefaultInstance.RootReference; Status = FirebaseConnectionStatus.Connected; + callback(Status); break; case DependencyStatus.UnavailableDisabled: @@ -114,21 +115,25 @@ public void Initialise() case DependencyStatus.UnavilableMissing: case DependencyStatus.UnavailablePermission: Status = FirebaseConnectionStatus.ExternalError; + callback(Status); break; case DependencyStatus.UnavailableUpdating: Status = FirebaseConnectionStatus.Updating; - RetryInitialiseAfterDelay(); + callback(Status); + RetryInitialiseAfterDelay(callback); break; case DependencyStatus.UnavailableUpdaterequired: Status = FirebaseConnectionStatus.UpdateRequired; + callback(Status); break; case DependencyStatus.UnavailableOther: default: Status = FirebaseConnectionStatus.InternalError; Debug.LogError("firebase ??? blew up or something," + task.Result); + callback(Status); break; } @@ -139,10 +144,10 @@ public void Initialise() /// /// async function to retry initialisation after a delay /// - private async void RetryInitialiseAfterDelay() + private async void RetryInitialiseAfterDelay(Action callback) { await Task.Delay(TimeSpan.FromSeconds(10)); - Initialise(); + Initialise(callback); } /// @@ -172,7 +177,7 @@ private void AuthStateChanged(object sender, EventArgs eventArgs) if (!signedIn && _user != null) { Debug.Log($"signed out successfully as {_user.UserId}"); - foreach (var callback in _onSignOutCallback) + foreach (var callback in _onSignOutCallbacks) callback(_user); } @@ -183,26 +188,29 @@ private void AuthStateChanged(object sender, EventArgs eventArgs) Debug.Log($"signed in successfully as {_user.UserId}"); RetrieveUsernameWithCallback((_, _) => { - foreach (var callback in _onSignInCallback) callback(_user); + Debug.Log($"retrieved username post-authentication, calling {_onSignInCallbacks.Count} callbacks"); + foreach (var callback in _onSignInCallbacks) callback(_user); }); } /// /// function to register a callback for when the user signs in /// - /// callback function that takes in a FirebaseUser argument + /// callback function that takes in a FirebaseUser object public void RegisterOnSignInCallback(Action callback) { - _onSignInCallback.Add(callback); + _onSignInCallbacks.Add(callback); + Debug.Log($"registering on sign in callback, there are now {_onSignInCallbacks.Count} callbacks"); } /// /// function to register a callback for when the user signs out /// - /// callback function that takes in a FirebaseUser argument + /// callback function that takes in a FirebaseUser object public void RegisterOnSignOutCallback(Action callback) { - _onSignOutCallback.Add(callback); + Debug.Log($"registering on sign out callback, there are now {_onSignOutCallbacks.Count} callbacks"); + _onSignOutCallbacks.Add(callback); } /// @@ -364,21 +372,18 @@ private void RetrieveUsernameWithCallback(Action { - DatabaseTransactionResult result; if (task.IsCompletedSuccessfully) { - result = DatabaseTransactionResult.Ok; _username = task.Result.Value.ToString(); Debug.Log($"our username is {_username}"); + callback(DatabaseTransactionResult.Ok, _username); } else { - result = DatabaseTransactionResult.Error; _username = "Unknown"; Debug.LogError("failed to get username"); + callback(DatabaseTransactionResult.Error, _username); } - - callback(result, _username); }); } @@ -429,8 +434,8 @@ public void ForgotPassword(string email, Action callback) /// abstraction function to get the user's recent scores from the database /// /// - /// callback function that takes in a DatabaseTransactionResult and List of LocalPlayerData.Score - /// argument + /// callback function that takes in a DatabaseTransactionResult enum + /// and a List<LocalPlayerData.Score>argument /// public void GetRecentScores(Action> callback) { diff --git a/ColourMeOKGame/Assets/Scripts/Colorimetry.cs b/ColourMeOKGame/Assets/Scripts/Colorimetry.cs index 72c98b6..bf5ce52 100644 --- a/ColourMeOKGame/Assets/Scripts/Colorimetry.cs +++ b/ColourMeOKGame/Assets/Scripts/Colorimetry.cs @@ -62,6 +62,7 @@ public static RGB gamut_clip_preserve_chroma(RGB rgb) /// a and b must be normalized so a^2 + b^2 == 1 /// // https://bottosson.github.io/posts/gamutclipping/ (MIT) + // ReSharper disable once MemberCanBePrivate.Global public static float find_gamut_intersection( float a, float b, @@ -164,6 +165,7 @@ public static RGB gamut_clip_preserve_chroma(RGB rgb) /// a and b must be normalized so a^2 + b^2 == 1 /// // https://bottosson.github.io/posts/gamutclipping/ (MIT) + // ReSharper disable once MemberCanBePrivate.Global public static LC find_cusp(float a, float b) { // First, find the maximum saturation (saturation S = C/L) @@ -178,7 +180,7 @@ public static LC find_cusp(float a, float b) } // https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab (public domain) - + // ReSharper disable once MemberCanBePrivate.Global public static Lab linear_srgb_to_oklab(RGB c) { var l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b; @@ -220,6 +222,7 @@ public static RGB oklab_to_linear_srgb(Lab c) /// a and b must be normalized so a^2 + b^2 == 1 /// // https://bottosson.github.io/posts/gamutclipping/ (MIT) + // ReSharper disable once MemberCanBePrivate.Global public static float compute_max_saturation(float a, float b) { // Max saturation will be when one of r, g or b goes below zero. diff --git a/ColourMeOKGame/Assets/Scripts/GameManager.cs b/ColourMeOKGame/Assets/Scripts/GameManager.cs index 56709b1..a504299 100644 --- a/ColourMeOKGame/Assets/Scripts/GameManager.cs +++ b/ColourMeOKGame/Assets/Scripts/GameManager.cs @@ -1,5 +1,6 @@ +using System; +using System.Collections.Generic; using UnityEngine; -using UnityEngine.Serialization; using UnityEngine.UIElements; /// @@ -12,24 +13,45 @@ public class GameManager : MonoBehaviour /// public enum DisplayState { + UnassociatedState, // initial state, then we transition to Nothing to initialise the ui Nothing, - PlayView, + GameView, LeaderboardView, - AccountView, + AccountView } /// /// singleton pattern: define instance field for accessing the singleton elsewhere /// public static GameManager Instance; - + /// /// the current display state of the game /// - [SerializeField] private DisplayState state = DisplayState.Nothing; + [SerializeField] private DisplayState state = DisplayState.UnassociatedState; /// - /// the visual element object for game ui (hud/prompts/tooltips) + /// the game object for the ui + /// + [SerializeField] private GameObject uiGameObject; + + // /// + // /// callback functions to be invoked when the display state changes + // /// + // private readonly List> _onDisplayStateChange = new(); + + /// + /// callback functions to be invoked when the local player data is updated + /// + private readonly List> _onLocalPlayerDataUpdateCallbacks = new(); + + /// + /// the local player data object for storing player data + /// + private LocalPlayerData _data; + + /// + /// the visual element for the ui /// private VisualElement _ui; @@ -37,11 +59,6 @@ public enum DisplayState /// backend object for handling communication with the firebase backend /// public Backend Backend; - - /// - /// the local player data object for storing player data - /// - private LocalPlayerData _localPlayerData; /// /// enforces singleton behaviour; sets doesn't destroy on load and checks for multiple instances @@ -61,30 +78,65 @@ private void Awake() Debug.Log("awake as non-singleton instance, destroying self"); Destroy(gameObject); } - } - private void Start() - { - SetDisplayState(DisplayState.PlayView); - - // load the local player data and refresh the ui - _localPlayerData = new LocalPlayerData(); - _localPlayerData.LoadFromTheWorld(); - - // register a callback to refresh the ui when the player signs in - Backend.RegisterOnSignInCallback(_ => - { - _localPlayerData.LoadFromTheWorld(); - }); + if (uiGameObject == null) + throw new NullReferenceException("a reference UI GameObject is not set in the inspector"); + + _ui = uiGameObject.GetComponent()?.rootVisualElement; + if (_ui == null) + throw new NullReferenceException("could not grab the UIDocument in the reference UI GameObject"); } /// - /// called when the game object is enabled + /// called before the first frame update /// - private void OnEnable() + private void Start() { + // transition to the initial state + SetDisplayState(DisplayState.Nothing); + + // initialise the backend Backend = new Backend(); - Backend.Initialise(); + Backend.Initialise(status => + { + Debug.Log("initialised backend, setting connection status text"); + + _ui.Q