game: fiddle around with execution order

generally found

- Awake -> init
- OnEnable -> init + register callbacks
- Start -> do anything that might call a callback
This commit is contained in:
Mark Joshwel 2024-11-18 00:16:53 +08:00
parent e8091632c4
commit 872f3f263e
9 changed files with 260 additions and 156 deletions

View file

@ -78,22 +78,11 @@ public class AccountUI : MonoBehaviour
/// </summary> /// </summary>
private Button _usernameUpdateButton; private Button _usernameUpdateButton;
private void Awake()
{
GameManager.Instance.Backend.RegisterOnSignInCallback(OnSignInCallback);
}
public void Start()
{
if (state == State.UnassociatedState) throw new Exception("unreachable state");
// GameManager.Instance.Backend.RegisterOnSignInCallback(OnSignInCallback);
}
/// <summary> /// <summary>
/// function to subscribe button events to their respective functions /// function called when the object is enabled,
/// subscribes button events to their respective functions
/// </summary> /// </summary>
public void OnEnable() private void OnEnable()
{ {
var ui = GetComponent<UIDocument>().rootVisualElement; var ui = GetComponent<UIDocument>().rootVisualElement;
@ -121,17 +110,18 @@ public void OnEnable()
_secondaryActionButton.clicked += OnSecondaryActionButtonClick; _secondaryActionButton.clicked += OnSecondaryActionButtonClick;
TransitionStateTo(State.NotSignedIn); TransitionStateTo(State.NotSignedIn);
}
private void OnSignInCallback(FirebaseUser user) if (state == State.UnassociatedState) throw new Exception("unreachable state");
GameManager.Instance.Backend.RegisterOnSignInCallback(_ =>
{ {
Debug.Log("sign in account ui callback"); Debug.Log("post-authentication callback, updating AccountView fields");
var username = GameManager.Instance.Backend.GetUsername(); var username = GameManager.Instance.Backend.GetUsername();
_header.text = $"Signed in as {username}";
_passwordField.value = "";
_usernameField.value = username; _usernameField.value = username;
_emailField.value = GameManager.Instance.Backend.GetUser().Email; _emailField.value = GameManager.Instance.Backend.GetUser().Email;
_passwordField.value = ""; });
_header.text = $"Signed in as {username}"; GameManager.Instance.RegisterOnLocalPlayerDataChangeCallback(PopulateFields);
} }
private void TransitionStateTo(State newState, bool keepAccompanyingText = false) private void TransitionStateTo(State newState, bool keepAccompanyingText = false)
@ -619,6 +609,18 @@ private void OnSecondaryActionButtonClick()
} }
} }
/// <summary>
/// populate the fields with the given username and email,
/// used as a callback to when local player data is changed
/// </summary>
public void PopulateFields(LocalPlayerData data)
{
Debug.Log(
$"populating AccountView fields with lkUsername={data.LastKnownUsername} and lkEmail={data.LastKnownEmail}");
_usernameField.value = data.LastKnownUsername;
_emailField.value = data.LastKnownEmail;
}
/// <summary> /// <summary>
/// state of the account view /// state of the account view
/// </summary> /// </summary>

View file

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: -10
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName: assetBundleName:

View file

@ -60,12 +60,18 @@ public enum UserAccountDetailTargetEnum
/// <summary> /// <summary>
/// callback functions to be invoked when the user signs in /// callback functions to be invoked when the user signs in
/// </summary> /// </summary>
private readonly List<Action<FirebaseUser>> _onSignInCallback = new(); private readonly List<Action<FirebaseUser>> _onSignInCallbacks = new();
/// <summary> /// <summary>
/// callback functions to be invoked when the user signs out /// callback functions to be invoked when the user signs out
/// </summary> /// </summary>
private readonly List<Action> _onSignOutCallback = new(); private readonly List<Action> _onSignOutCallbacks = new();
/// <summary>
/// callback functions to be invoked when the user signs in
/// </summary>
/// <returns></returns>
private readonly List<Action<FirebaseConnectionStatus>> _onConnectionStatusChangedCallbacks = new ();
/// <summary> /// <summary>
/// the firebase authentication object /// the firebase authentication object
@ -100,9 +106,9 @@ public enum UserAccountDetailTargetEnum
/// <summary> /// <summary>
/// variable initialisation function /// variable initialisation function
/// </summary> /// </summary>
public void Initialise() public void Initialise(Action<FirebaseConnectionStatus> callback)
{ {
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
{ {
// cher is this robust enough // cher is this robust enough
switch (task.Result) switch (task.Result)
@ -112,6 +118,7 @@ public void Initialise()
_auth.StateChanged += AuthStateChanged; _auth.StateChanged += AuthStateChanged;
_db = FirebaseDatabase.DefaultInstance.RootReference; _db = FirebaseDatabase.DefaultInstance.RootReference;
Status = FirebaseConnectionStatus.Connected; Status = FirebaseConnectionStatus.Connected;
callback(Status);
break; break;
case DependencyStatus.UnavailableDisabled: case DependencyStatus.UnavailableDisabled:
@ -119,21 +126,25 @@ public void Initialise()
case DependencyStatus.UnavilableMissing: case DependencyStatus.UnavilableMissing:
case DependencyStatus.UnavailablePermission: case DependencyStatus.UnavailablePermission:
Status = FirebaseConnectionStatus.ExternalError; Status = FirebaseConnectionStatus.ExternalError;
callback(Status);
break; break;
case DependencyStatus.UnavailableUpdating: case DependencyStatus.UnavailableUpdating:
Status = FirebaseConnectionStatus.Updating; Status = FirebaseConnectionStatus.Updating;
RetryInitialiseAfterDelay(); callback(Status);
RetryInitialiseAfterDelay(callback);
break; break;
case DependencyStatus.UnavailableUpdaterequired: case DependencyStatus.UnavailableUpdaterequired:
Status = FirebaseConnectionStatus.UpdateRequired; Status = FirebaseConnectionStatus.UpdateRequired;
callback(Status);
break; break;
case DependencyStatus.UnavailableOther: case DependencyStatus.UnavailableOther:
default: default:
Status = FirebaseConnectionStatus.InternalError; Status = FirebaseConnectionStatus.InternalError;
Debug.LogError("firebase ??? blew up or something," + task.Result); Debug.LogError("firebase ??? blew up or something," + task.Result);
callback(Status);
break; break;
} }
@ -146,8 +157,8 @@ public void Initialise()
/// </summary> /// </summary>
private void FireOnSignInCallbacks() private void FireOnSignInCallbacks()
{ {
Debug.Log($"firing on sign in callbacks ({_onSignInCallback.Count})"); Debug.Log($"firing OnSignInCallbacks ({_onSignInCallbacks.Count})");
foreach (var callback in _onSignInCallback) foreach (var callback in _onSignInCallbacks)
{ {
try try
{ {
@ -155,18 +166,18 @@ private void FireOnSignInCallbacks()
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError(e); Debug.LogError($"error invoking OnSignInCallback: {e.Message}");
} }
} }
} }
/// <summary> /// <summary>
/// function to fire all on sign out callbacks /// function to fire all on sign-out callbacks
/// </summary> /// </summary>
private void FireOnSignOutCallbacks() private void FireOnSignOutCallbacks()
{ {
Debug.Log($"firing on sign out callbacks ({_onSignOutCallback.Count})"); Debug.Log($"firing OnSignOutCallbacks ({_onSignOutCallbacks.Count})");
foreach (var callback in _onSignOutCallback) foreach (var callback in _onSignOutCallbacks)
{ {
try try
{ {
@ -174,7 +185,7 @@ private void FireOnSignOutCallbacks()
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError(e); Debug.LogError($"error invoking OnSignOutCallback: {e.Message}");
} }
} }
} }
@ -182,10 +193,10 @@ private void FireOnSignOutCallbacks()
/// <summary> /// <summary>
/// async function to retry initialisation after a delay /// async function to retry initialisation after a delay
/// </summary> /// </summary>
private async void RetryInitialiseAfterDelay() private async void RetryInitialiseAfterDelay(Action<FirebaseConnectionStatus> callback)
{ {
await Task.Delay(TimeSpan.FromSeconds(10)); await Task.Delay(TimeSpan.FromSeconds(10));
Initialise(); Initialise(callback);
} }
/// <summary> /// <summary>
@ -235,25 +246,24 @@ private void AuthStateChanged(object sender, EventArgs eventArgs)
/// <param name="callback">callback function that takes in a <c>FirebaseUser</c> object</param> /// <param name="callback">callback function that takes in a <c>FirebaseUser</c> object</param>
public void RegisterOnSignInCallback(Action<FirebaseUser> callback) public void RegisterOnSignInCallback(Action<FirebaseUser> callback)
{ {
Debug.Log("registering on sign in callback"); _onSignInCallbacks.Add(callback);
_onSignInCallback.Add(callback);
} }
// /// <summary> /// <summary>
// /// function to register a callback for when the user signs out /// function to register a callback for when the user signs out
// /// </summary> /// </summary>
// /// <param name="callback">callback function</param> /// <param name="callback">callback function</param>
// public void RegisterOnSignOutCallback(Action callback) public void RegisterOnSignOutCallback(Action callback)
// { {
// _onSignOutCallback.Add(callback); _onSignOutCallbacks.Add(callback);
// } }
/// <summary> /// <summary>
/// abstraction function to authenticate the user /// abstraction function to authenticate the user
/// </summary> /// </summary>
/// <param name="email">email string</param> /// <param name="email">email string</param>
/// <param name="password">user raw password string</param> /// <param name="password">user raw password string</param>
/// <param name="callback">callback function that takes in an AuthenticationResult argument</param> /// <param name="callback">callback function that takes in an <c>AuthenticationResult</c> enum</param>
/// <param name="registerUser">whether to treat authentication as registration</param> /// <param name="registerUser">whether to treat authentication as registration</param>
/// <param name="registeringUsername">username string if registering</param> /// <param name="registeringUsername">username string if registering</param>
public void AuthenticateUser( public void AuthenticateUser(
@ -472,8 +482,7 @@ public void ForgotPassword(string email, Action<bool> callback)
/// abstraction function to get the user's recent scores from the database /// abstraction function to get the user's recent scores from the database
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// callback function that takes in a DatabaseTransactionResult and List of LocalPlayerData.Score /// callback function that takes in a <c>DatabaseTransactionResult</c> enum and a <c>List&lt;LocalPlayerData.Score&gt;</c>
/// argument
/// </param> /// </param>
public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerData.Score>> callback) public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerData.Score>> callback)
{ {
@ -485,7 +494,7 @@ public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerDa
/// abstraction function to submit a score to the database /// abstraction function to submit a score to the database
/// </summary> /// </summary>
/// <param name="score">score</param> /// <param name="score">score</param>
/// <param name="callback">callback function that takes in one DatabaseTransactionResult argument</param> /// <param name="callback">callback function that takes in a <c>DatabaseTransactionResult</c> enum </param>
public void SubmitScore( public void SubmitScore(
LocalPlayerData.Score score, LocalPlayerData.Score score,
Action<DatabaseTransactionResult> callback) Action<DatabaseTransactionResult> callback)
@ -497,7 +506,7 @@ public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerDa
/// abstraction function to get and calculate the user's rating from the database /// abstraction function to get and calculate the user's rating from the database
/// calculation is done locally, call UpdateUserRating to update the user's rating in the database /// calculation is done locally, call UpdateUserRating to update the user's rating in the database
/// </summary> /// </summary>
/// <param name="callback">callback function that takes in a DatabaseTransactionResult and float (user rating) argument</param> /// <param name="callback">callback function that takes in a <c>DatabaseTransactionResult</c> enum and a user rating <c>float</c></param>
public void CalculateUserRating( public void CalculateUserRating(
Action<DatabaseTransactionResult, float> callback) Action<DatabaseTransactionResult, float> callback)
{ {
@ -508,7 +517,7 @@ public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerDa
/// abstraction function to update the user's rating in the database /// abstraction function to update the user's rating in the database
/// </summary> /// </summary>
/// <param name="newRating">new user rating value as a float</param> /// <param name="newRating">new user rating value as a float</param>
/// <param name="callback">callback function that takes in one DatabaseTransactionResult argument</param> /// <param name="callback">callback function that takes in a <c>DatabaseTransactionResult</c> enum </param>
public void UpdateUserRating( public void UpdateUserRating(
float newRating, float newRating,
Action<DatabaseTransactionResult> callback) Action<DatabaseTransactionResult> callback)
@ -520,7 +529,7 @@ public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerDa
/// abstraction function to get the leaderboard from the database /// abstraction function to get the leaderboard from the database
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// callback function that takes in a DatabaseTransactionResult and LeaderboardEntry[] argument /// callback function that takes in a <c>DatabaseTransactionResult</c> enum and a <c>List&lt;LeaderboardEntry&gt;</c>
/// </param> /// </param>
public void GetLeaderboard( public void GetLeaderboard(
Action<DatabaseTransactionResult, LeaderboardEntry[]> callback) Action<DatabaseTransactionResult, LeaderboardEntry[]> callback)
@ -533,7 +542,7 @@ public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerDa
/// </summary> /// </summary>
/// <param name="target">the target account detail to update</param> /// <param name="target">the target account detail to update</param>
/// <param name="newValue">the new value for the target account detail</param> /// <param name="newValue">the new value for the target account detail</param>
/// <param name="callback">callback function that takes in one DatabaseTransactionResult argument</param> /// <param name="callback">callback function that takes in a <c>DatabaseTransactionResult</c> enum</param>
/// <exception cref="ArgumentOutOfRangeException">thrown when the target is not a valid UserAccountDetailTargetEnum</exception> /// <exception cref="ArgumentOutOfRangeException">thrown when the target is not a valid UserAccountDetailTargetEnum</exception>
public void UpdateUserAccountDetail( public void UpdateUserAccountDetail(
UserAccountDetailTargetEnum target, UserAccountDetailTargetEnum target,

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements; using UnityEngine.UIElements;
@ -20,7 +21,17 @@ public class GameManager : MonoBehaviour
/// <summary> /// <summary>
/// the local player data object for storing player data /// the local player data object for storing player data
/// </summary> /// </summary>
private LocalPlayerData _localPlayerData; private LocalPlayerData _data;
/// <summary>
/// read-only property for accessing the local player data outside of this class
/// </summary>
public LocalPlayerData Data => _data;
/// <summary>
/// list of callbacks to call when the local player data changes
/// </summary>
private readonly List<Action<LocalPlayerData>> _onLocalPlayerDataChangeCallbacks = new List<Action<LocalPlayerData>>();
/// <summary> /// <summary>
/// backend object for handling communication with the firebase backend /// backend object for handling communication with the firebase backend
@ -47,40 +58,55 @@ private void Awake()
} }
} }
private void Start()
{
// load the local player data and refresh the ui
_localPlayerData = new LocalPlayerData();
_localPlayerData.LoadFromTheWorld();
PopulateFields();
try
{
RenderFromPlayerData();
}
catch (Exception)
{
// TODO: remove this once the bug is fixed
Debug.LogWarning("handling known exception, remove this once the bug is fixed");
}
// register a callback to refresh the ui when the player signs in
Backend.RegisterOnSignInCallback(_ =>
{
Debug.Log("sign in callback, refreshing GameManager-controlled SideView UI");
_localPlayerData.LoadFromTheWorld();
PopulateFields();
RenderFromPlayerData();
});
}
/// <summary> /// <summary>
/// called when the game object is enabled, initialises variables /// called when the game object is enabled, initialises variables
/// </summary> /// </summary>
private void OnEnable() private void OnEnable()
{ {
Backend = new Backend();
Backend.Initialise();
ui = UIManager.Instance; ui = UIManager.Instance;
// load the local player data and refresh the ui
_data = new LocalPlayerData();
Backend = new Backend();
Backend.Initialise(status =>
{
Debug.Log("initialised backend, setting connection status text");
ui.UI.Q<Label>("ConnectionStatusText").text = status switch
{
Backend.FirebaseConnectionStatus.Connected => "Status: Connected",
Backend.FirebaseConnectionStatus.Updating => "Status: Updating... (Retrying in a bit!)",
Backend.FirebaseConnectionStatus.NotConnected => "Status: Disconnected",
Backend.FirebaseConnectionStatus.UpdateRequired =>
"Status: Disconnected (Device Component Update Required)",
Backend.FirebaseConnectionStatus.ExternalError => "Status: Disconnected (External/Device Error)",
Backend.FirebaseConnectionStatus.InternalError => "Status: Disconnected (Internal Error)",
_ => "Status: Disconnected (unknown fcs state, this is unreachable and a bug)"
};
if (status == Backend.FirebaseConnectionStatus.Connected) return;
// if we're not connected, hide any online 'features'
ui.UI.Q<Button>("LeaderboardButton").style.display = DisplayStyle.None;
ui.UI.Q<Button>("AccountButton").style.display = DisplayStyle.None;
});
// register a callback to refresh the ui when the player signs in
Backend.RegisterOnSignInCallback(_ =>
{
Debug.Log("sign in callback, refreshing GameManager-controlled SideView UI");
_data.LoadFromTheWorld(FireLocalPlayerDataChangeCallbacks);
});
}
/// <summary>
/// load in stuff
/// </summary>
private void Start()
{
Debug.Log("GameManager starts here");
_data.LoadFromTheWorld(FireLocalPlayerDataChangeCallbacks);
} }
/// <summary> /// <summary>
@ -90,76 +116,35 @@ private void OnApplicationQuit()
{ {
Debug.Log("running deferred cleanup/save functions"); Debug.Log("running deferred cleanup/save functions");
Backend.Cleanup(); Backend.Cleanup();
_localPlayerData.SaveToTheWorld(); _data.SaveToTheWorld();
} }
/// <summary> /// <summary>
/// populate the fields with the given username and email, used by GameManager after local player data is loaded /// function to register a callback to be called when the local player data changes
/// </summary> /// </summary>
public void PopulateFields() /// <param name="callback">callback function that takes a <c>LocalPlayerData</c> object</param>
public void RegisterOnLocalPlayerDataChangeCallback(Action<LocalPlayerData> callback)
{ {
Debug.Log( _onLocalPlayerDataChangeCallbacks.Add(callback);
$"populating AccountView fields with lkUsername={_localPlayerData.LastKnownUsername} and lkEmail={_localPlayerData.LastKnownEmail}"); Debug.Log($"registering LocalPlayerDataChangeCallback ({_onLocalPlayerDataChangeCallbacks.Count})");
ui.UI.Q<TextField>("UsernameField").value = _localPlayerData.LastKnownUsername;
ui.UI.Q<TextField>("EmailField").value = _localPlayerData.LastKnownEmail;
} }
/// <summary> /// <summary>
/// function to update the ui with the latest data /// function to fire all local player data change callbacks
/// </summary> /// </summary>
private void RenderFromPlayerData() private void FireLocalPlayerDataChangeCallbacks(LocalPlayerData data)
{ {
// calculate averages from both recent local scores and online scores Debug.Log($"firing LocalPlayerDataChangeCallbacks ({_onLocalPlayerDataChangeCallbacks.Count})");
var totalLightnessAcc = 0f; foreach (var callback in _onLocalPlayerDataChangeCallbacks)
var totalChromaAcc = 0f;
var totalHueAcc = 0f;
var totalRounds = 0;
// average out all the scores we have to get a stable-ish average
foreach (var localScore in _localPlayerData.RecentLocalScores)
{ {
totalLightnessAcc += localScore.AvgLightnessAccuracy; try
totalChromaAcc += localScore.AvgChromaAccuracy; {
totalHueAcc += localScore.AvgHueAccuracy; callback.Invoke(data);
totalRounds += localScore.NoOfRounds;
} }
catch (Exception e)
foreach (var onlineScore in _localPlayerData.RecentOnlineScores)
{ {
totalLightnessAcc += onlineScore.AvgLightnessAccuracy; Debug.LogError($"error invoking LocalPlayerDataChangeCallback: {e.Message}");
totalChromaAcc += onlineScore.AvgChromaAccuracy; }
totalHueAcc += onlineScore.AvgHueAccuracy; }
totalRounds += onlineScore.NoOfRounds;
}
foreach (var onlineScore in _localPlayerData.BestOnlineScores)
{
totalLightnessAcc += onlineScore.AvgLightnessAccuracy;
totalChromaAcc += onlineScore.AvgChromaAccuracy;
totalHueAcc += onlineScore.AvgHueAccuracy;
totalRounds += onlineScore.NoOfRounds;
}
Debug.Log($"tL={totalLightnessAcc} tC={totalChromaAcc} tH={totalHueAcc} tR={totalRounds}");
if (totalRounds == 0) totalRounds = 1;
var lightnessAcc = totalLightnessAcc / totalRounds;
var chromaAcc = totalChromaAcc / totalRounds;
var hueAcc = totalHueAcc / totalRounds;
var playerText = Backend.IsSignedIn ? _localPlayerData.LastKnownUsername : $"{_localPlayerData.LastKnownUsername} (Not Signed In)";
// finally, set the labels
ui.UI.Q<Label>("PlayerText").text = playerText;
ui.UI.Q<Label>("LightnessAccuracyText").text = $"{lightnessAcc:F}";
ui.UI.Q<Label>("ChromaAccuracyText").text = $"{chromaAcc:F}";
ui.UI.Q<Label>("HueAccuracyText").text = $"{hueAcc:F}";
// and set the player rating, but after we get it from the backend
// (god I LOVE async (I am LYING out of my teeth))
Backend.CalculateUserRating((dtr, rating) =>
{
if (dtr != Backend.DatabaseTransactionResult.Ok) return;
ui.UI.Q<Label>("RatingText").text = $"{rating:F}";
});
} }
} }

View file

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: -10 executionOrder: -50
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName: assetBundleName:

View file

@ -35,7 +35,7 @@ public class LocalPlayerData
/// <summary> /// <summary>
/// loads player data from player prefs and database /// loads player data from player prefs and database
/// </summary> /// </summary>
public void LoadFromTheWorld() public void LoadFromTheWorld(Action<LocalPlayerData> callback)
{ {
// load user data, possibly from the backend // load user data, possibly from the backend
var possibleUser = GameManager.Instance.Backend.GetUser(); var possibleUser = GameManager.Instance.Backend.GetUser();
@ -91,7 +91,8 @@ public void LoadFromTheWorld()
} }
Debug.Log( Debug.Log(
$"loaded lpdata from the world ({LastKnownUsername} <{LastKnownEmail}> with RLS.Count={RecentLocalScores.Count}), ROS.Count={RecentOnlineScores.Count}"); $"loaded lpdata from the world ({LastKnownUsername} <{LastKnownEmail}> with RLS.Count={RecentLocalScores.Count}, ROS.Count={RecentOnlineScores.Count}");
callback(this);
}); });
} }

View file

@ -1,3 +1,4 @@
using System;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements; using UnityEngine.UIElements;
@ -21,6 +22,36 @@ public class SideViewUI : MonoBehaviour
/// </summary> /// </summary>
private Button _playButton; private Button _playButton;
/// <summary>
/// connection status label for showing the connection status
/// </summary>
private Label _connectionStatusLabel;
/// <summary>
/// text label for showing the player's known name
/// </summary>
private Label _playerText;
/// <summary>
/// text label for showing the player's rating
/// </summary>
private Label _ratingText;
/// <summary>
/// text label for showing the player's stable-ish lightness accuracy
/// </summary>
private Label _lightnessAccuracyText;
/// <summary>
/// text label for showing the player's stable-ish chroma accuracy
/// </summary>
private Label _chromaAccuracyText;
/// <summary>
/// text label for showing the player's stable-ish hue accuracy
/// </summary>
private Label _hueAccuracyText;
/// <summary> /// <summary>
/// function to subscribe button events to their respective functions /// function to subscribe button events to their respective functions
/// </summary> /// </summary>
@ -36,6 +67,21 @@ private void OnEnable()
_accountButton = ui.Q<Button>("AccountButton"); _accountButton = ui.Q<Button>("AccountButton");
_accountButton.clicked += OnAccountButtonClicked; _accountButton.clicked += OnAccountButtonClicked;
_connectionStatusLabel = ui.Q<Label>("ConnectionStatusLabel");
_playerText = ui.Q<Label>("PlayerText");
_ratingText = ui.Q<Label>("RatingText");
_lightnessAccuracyText = ui.Q<Label>("LightnessAccuracyText");
_chromaAccuracyText = ui.Q<Label>("ChromaAccuracyText");
_hueAccuracyText = ui.Q<Label>("HueAccuracyText");
GameManager.Instance.Backend.RegisterOnSignOutCallback(() =>
{
RenderFromPlayerData(GameManager.Instance.Data);
});
GameManager.Instance.RegisterOnLocalPlayerDataChangeCallback(RenderFromPlayerData);
} }
/// <summary> /// <summary>
@ -61,4 +107,65 @@ private static void OnAccountButtonClicked()
{ {
GameManager.Instance.ui.SetDisplayState(UIManager.DisplayState.AccountView); GameManager.Instance.ui.SetDisplayState(UIManager.DisplayState.AccountView);
} }
/// <summary>
/// function to update the ui with the latest data
/// </summary>
private void RenderFromPlayerData(LocalPlayerData data)
{
// calculate averages from both recent local scores and online scores
var totalLightnessAcc = 0f;
var totalChromaAcc = 0f;
var totalHueAcc = 0f;
var totalRounds = 0;
// average out all the scores we have to get a stable-ish average
foreach (var localScore in data.RecentLocalScores)
{
totalLightnessAcc += localScore.AvgLightnessAccuracy;
totalChromaAcc += localScore.AvgChromaAccuracy;
totalHueAcc += localScore.AvgHueAccuracy;
totalRounds += localScore.NoOfRounds;
}
foreach (var onlineScore in data.RecentOnlineScores)
{
totalLightnessAcc += onlineScore.AvgLightnessAccuracy;
totalChromaAcc += onlineScore.AvgChromaAccuracy;
totalHueAcc += onlineScore.AvgHueAccuracy;
totalRounds += onlineScore.NoOfRounds;
}
foreach (var onlineScore in data.BestOnlineScores)
{
totalLightnessAcc += onlineScore.AvgLightnessAccuracy;
totalChromaAcc += onlineScore.AvgChromaAccuracy;
totalHueAcc += onlineScore.AvgHueAccuracy;
totalRounds += onlineScore.NoOfRounds;
}
Debug.Log($"tL={totalLightnessAcc} tC={totalChromaAcc} tH={totalHueAcc} tR={totalRounds}");
if (totalRounds == 0) totalRounds = 1;
var lightnessAcc = totalLightnessAcc / totalRounds;
var chromaAcc = totalChromaAcc / totalRounds;
var hueAcc = totalHueAcc / totalRounds;
var playerText = GameManager.Instance.Backend.IsSignedIn
? data.LastKnownUsername
: $"{data.LastKnownUsername} (Not Signed In)";
// finally, set the labels
_playerText.text = playerText;
_lightnessAccuracyText.text = $"{lightnessAcc:F}";
_chromaAccuracyText.text = $"{chromaAcc:F}";
_hueAccuracyText.text = $"{hueAcc:F}";
// and set the player rating, but after we get it from the backend
// (god I LOVE async (I am LYING out of my teeth))
GameManager.Instance.Backend.CalculateUserRating((dtr, rating) =>
{
if (dtr != Backend.DatabaseTransactionResult.Ok) return;
_ratingText.text = $"{rating:F}";
});
}
} }

View file

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: -20
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName: assetBundleName:

View file

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: -15 executionOrder: -60
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName: assetBundleName: