From 12c2f8776a5df878638cb5499a17b107b354cac3 Mon Sep 17 00:00:00 2001
From: Ali Sharoz <sharoz_dev@rizzestudios.com>
Date: Thu, 23 Jan 2025 18:46:17 +0500
Subject: [PATCH] Game Stuck Bug not fixed. Commiting for analysing

---
 .../Scripts/Gameplay/PlayerScoreComponent.cs  |  61 +++-
 Assets/Scripts/Gameplay/ScoreManager.cs       | 304 +++++++++++++++---
 .../Gameplay/SerializableDictionary.cs        |  35 ++
 .../Gameplay/SerializableDictionary.cs.meta   |  11 +
 4 files changed, 345 insertions(+), 66 deletions(-)
 create mode 100644 Assets/Scripts/Gameplay/SerializableDictionary.cs
 create mode 100644 Assets/Scripts/Gameplay/SerializableDictionary.cs.meta

diff --git a/Assets/Scripts/Gameplay/PlayerScoreComponent.cs b/Assets/Scripts/Gameplay/PlayerScoreComponent.cs
index 7adb20b2..212ee4a2 100644
--- a/Assets/Scripts/Gameplay/PlayerScoreComponent.cs
+++ b/Assets/Scripts/Gameplay/PlayerScoreComponent.cs
@@ -6,21 +6,14 @@ public class PlayerScoreComponent : NetworkBehaviour
 {
     public int CurrentScore { get; private set; }
     public ServerCharacter serverCharacter;
-    public int m_index;
-    public PlayerItem playerItem;
+
     public override void OnNetworkSpawn()
     {
-        if (IsOwner && IsClient)
-        {
-            Debug.Log($"[PlayerScoreComponent] Requesting score initialization for Player {OwnerClientId}");
-            ScoreManager.Instance?.InitializePlayerScoreServerRpc(OwnerClientId,serverCharacter.uIStateDisplayHandler.m_UIState.playerName);
-        }
-        // For the server player (host), ensure the PlayerScoreComponent is registered properly
-        if (IsServer && OwnerClientId == NetworkManager.Singleton.LocalClientId)
+        if (IsServer)
         {
+            Debug.Log($"[PlayerScoreComponent] Initializing score for Player {OwnerClientId}");
             ScoreManager.Instance?.InitializePlayerScoreServerRpc(OwnerClientId, serverCharacter.uIStateDisplayHandler.m_UIState.playerName);
         }
-        serverCharacter=GetComponent<ServerCharacter>();
     }
 
     public void UpdateScore(int newScore)
@@ -35,8 +28,48 @@ public class PlayerScoreComponent : NetworkBehaviour
         // Update the player's score display in the UI.
         Debug.Log($"[PlayerScoreComponent] Updated score UI for Player {OwnerClientId}: {CurrentScore}");
     }
-    //private void OnDestroy()
-    //{
-    //    Destroy(playerItem.gameObject);
-    //}
 }
+
+
+//using UnityEngine;
+//using Unity.Netcode;
+//using Unity.BossRoom.Gameplay.GameplayObjects.Character;
+
+//public class PlayerScoreComponent : NetworkBehaviour
+//{
+//    public int CurrentScore { get; private set; }
+//    public ServerCharacter serverCharacter;
+//    public int m_index;
+//    public PlayerItem playerItem;
+//    public override void OnNetworkSpawn()
+//    {
+//        if (IsOwner && IsClient)
+//        {
+//            Debug.Log($"[PlayerScoreComponent] Requesting score initialization for Player {OwnerClientId}");
+//            ScoreManager.Instance?.InitializePlayerScore(OwnerClientId,serverCharacter.uIStateDisplayHandler.m_UIState.playerName);
+//        }
+//        // For the server player (host), ensure the PlayerScoreComponent is registered properly
+//        if (IsServer && OwnerClientId == NetworkManager.Singleton.LocalClientId)
+//        {
+//            ScoreManager.Instance?.InitializePlayerScore(OwnerClientId, serverCharacter.uIStateDisplayHandler.m_UIState.playerName);
+//        }
+//        serverCharacter=GetComponent<ServerCharacter>();
+//    }
+
+//    public void UpdateScore(int newScore)
+//    {
+//        CurrentScore = newScore;
+//        Debug.Log($"[PlayerScoreComponent] Player {OwnerClientId} score updated to {newScore}.");
+//        UpdateScoreUI();
+//    }
+
+//    private void UpdateScoreUI()
+//    {
+//        // Update the player's score display in the UI.
+//        Debug.Log($"[PlayerScoreComponent] Updated score UI for Player {OwnerClientId}: {CurrentScore}");
+//    }
+//    //private void OnDestroy()
+//    //{
+//    //    Destroy(playerItem.gameObject);
+//    //}
+//}
diff --git a/Assets/Scripts/Gameplay/ScoreManager.cs b/Assets/Scripts/Gameplay/ScoreManager.cs
index 488da8ee..427d3c7e 100644
--- a/Assets/Scripts/Gameplay/ScoreManager.cs
+++ b/Assets/Scripts/Gameplay/ScoreManager.cs
@@ -3,13 +3,56 @@ using System.Collections.Generic;
 using Unity.Multiplayer.Samples.BossRoom;
 using UnityEngine;
 using Unity.Netcode;
+using Unity.Collections;
 
 public class ScoreManager : NetworkBehaviour
 {
     public static ScoreManager Instance { get; private set; }
 
-    public Dictionary<ulong, int> playerScores = new Dictionary<ulong, int>();
-    public Dictionary<ulong, string> playerNames = new Dictionary<ulong, string>();
+    // Use a NetworkList to store PlayerData
+    private NetworkList<PlayerData> playerDataList = new NetworkList<PlayerData>();
+
+    // NetworkVariable to sync scores (key: clientId, value: score)
+    private NetworkVariable<SerializableDictionary<ulong, int>> playerScores =
+        new NetworkVariable<SerializableDictionary<ulong, int>>(new SerializableDictionary<ulong, int>());
+
+    [System.Serializable]
+    public struct PlayerData : System.IEquatable<PlayerData>
+    {
+        public ulong ClientId;
+        public FixedString64Bytes Name;
+
+        public bool Equals(PlayerData other)
+        {
+            return ClientId == other.ClientId && Name.Equals(other.Name);
+        }
+
+        public override bool Equals(object obj)
+        {
+            return obj is PlayerData other && Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                int hash = 17;
+                hash = hash * 31 + ClientId.GetHashCode();
+                hash = hash * 31 + Name.GetHashCode();
+                return hash;
+            }
+        }
+
+        public static bool operator ==(PlayerData left, PlayerData right)
+        {
+            return left.Equals(right);
+        }
+
+        public static bool operator !=(PlayerData left, PlayerData right)
+        {
+            return !(left == right);
+        }
+    }
 
     private void Awake()
     {
@@ -25,76 +68,69 @@ public class ScoreManager : NetworkBehaviour
     {
         if (IsServer)
         {
-            StartCrowPenaltyCoroutineServerRpc();
+            StartCrowPenaltyCoroutine();
         }
-    }
 
-    [ServerRpc(RequireOwnership = false)]
-    public void InitializePlayerScoreServerRpc(ulong ownerClientId, string name)
-    {
-        if (!playerScores.ContainsKey(ownerClientId))
+        if (IsClient)
         {
-            playerScores[ownerClientId] = 200;
-            playerNames[ownerClientId] = name;
-            Debug.Log($"[ScoreManager] Player {ownerClientId} initialized with a starting score of 200 and name '{name}'.");
-            Scoreboard.instance.ScoreBoardItemInitializer(ownerClientId, playerScores[ownerClientId], name);
-            UpdatePlayerScoreClientRpc(ownerClientId, 200);
-        }
-        else
-        {
-            Debug.LogWarning($"[ScoreManager] Player {ownerClientId} already initialized.");
+            playerDataList.OnListChanged += OnPlayerDataListChanged;
+            playerScores.OnValueChanged += OnPlayerScoresChanged;
         }
     }
 
-    public void UpdatePlayerScore(ulong ownerClientId, int newScore)
+    private void OnDestroy()
     {
-        if (playerScores.ContainsKey(ownerClientId))
-        {
-            playerScores[ownerClientId] = newScore;
-            Debug.Log($"[ScoreManager] Updating Player {ownerClientId} score to {newScore}.");
-            UpdatePlayerScoreClientRpc(ownerClientId, newScore);
-        }
-        else
+        if (IsClient)
         {
-            Debug.LogWarning($"[ScoreManager] No entry found for Player {ownerClientId}. Cannot update score.");
+            playerDataList.OnListChanged -= OnPlayerDataListChanged;
+            playerScores.OnValueChanged -= OnPlayerScoresChanged;
         }
     }
 
-    [ClientRpc]
-    public void UpdatePlayerScoreClientRpc(ulong ownerClientId, int newScore)
+    [ServerRpc(RequireOwnership = false)]
+    public void InitializePlayerScoreServerRpc(ulong ownerClientId, string name)
     {
-        if (playerNames.ContainsKey(ownerClientId))
+        if (string.IsNullOrEmpty(name))
+        {
+            Debug.LogWarning($"[ScoreManager] Player name for Client {ownerClientId} is null or empty. Assigning default name.");
+            name = $"Player {ownerClientId}"; // Default name
+        }
+
+        var fixedName = new FixedString64Bytes(name); // Convert to FixedString
+        if (!playerScores.Value.ContainsKey(ownerClientId))
         {
-            string playerName = playerNames[ownerClientId];
-            Debug.Log($"[ScoreManager] Received score update for Player {ownerClientId} (Name: {playerName}): {newScore}");
-            Scoreboard.instance.ScoreBoardUpdater(playerName, newScore);
+            // Add initial score and name to the server-side collections
+            playerScores.Value[ownerClientId] = 200;
+            playerDataList.Add(new PlayerData { ClientId = ownerClientId, Name = fixedName });
+
+            Debug.Log($"[ScoreManager] Player {ownerClientId} initialized with a starting score of 200 and name '{fixedName}'.");
         }
         else
         {
-            Debug.LogWarning($"[ScoreManager] Player name not found for Player {ownerClientId}. Cannot update scoreboard.");
+            Debug.LogWarning($"[ScoreManager] Player {ownerClientId} already initialized.");
         }
     }
 
     public void AddPlayerScore(ulong ownerClientId, int scoreToAdd)
     {
-        if (playerScores.ContainsKey(ownerClientId))
+        if (IsServer && playerScores.Value.ContainsKey(ownerClientId))
         {
-            playerScores[ownerClientId] += scoreToAdd;
-            UpdatePlayerScore(ownerClientId, playerScores[ownerClientId]);
+            playerScores.Value[ownerClientId] += scoreToAdd;
+            Debug.Log($"[ScoreManager] Added {scoreToAdd} points to Player {ownerClientId}. New score: {playerScores.Value[ownerClientId]}.");
         }
     }
 
     public void SubtractPlayerScore(ulong ownerClientId, int scoreToSubtract)
     {
-        if (playerScores.ContainsKey(ownerClientId))
+        if (IsServer && playerScores.Value.ContainsKey(ownerClientId))
         {
-            int newScore = Mathf.Max(0, playerScores[ownerClientId] - scoreToSubtract);
-            UpdatePlayerScore(ownerClientId, newScore);
+            int currentScore = playerScores.Value[ownerClientId];
+            playerScores.Value[ownerClientId] = Mathf.Max(0, currentScore - scoreToSubtract);
+            Debug.Log($"[ScoreManager] Subtracted {scoreToSubtract} points from Player {ownerClientId}. New score: {playerScores.Value[ownerClientId]}.");
         }
     }
 
-    [ServerRpc]
-    public void StartCrowPenaltyCoroutineServerRpc()
+    private void StartCrowPenaltyCoroutine()
     {
         StartCoroutine(ApplyCrowPenaltyCoroutine());
     }
@@ -105,27 +141,191 @@ public class ScoreManager : NetworkBehaviour
 
         while (true)
         {
-            // Create a list to store client IDs whose scores need to be updated
-            List<ulong> clientsToModify = new List<ulong>();
-
-            foreach (var entry in playerScores)
+            foreach (var entry in playerScores.Value)
             {
                 ulong ownerClientId = entry.Key;
-
                 if (CrowManager.Instance != null && CrowManager.Instance.GetCurrentCrow().OwnerClientId == ownerClientId)
                 {
-                    clientsToModify.Add(ownerClientId);
+                    SubtractPlayerScore(ownerClientId, 2);
+                    Debug.Log($"[ScoreManager] Applied crow penalty to Player {ownerClientId}. New score: {playerScores.Value[ownerClientId]}.");
                 }
             }
 
-            // Apply the penalties after the iteration
-            foreach (ulong clientId in clientsToModify)
+            yield return new WaitForSeconds(5f);
+        }
+    }
+
+    private void OnPlayerDataListChanged(NetworkListEvent<PlayerData> changeEvent)
+    {
+        if (changeEvent.Type == NetworkListEvent<PlayerData>.EventType.Add)
+        {
+            var newData = changeEvent.Value;
+            string playerName = newData.Name.ToString(); // Convert FixedString to string
+
+            Debug.Log($"[ScoreManager] Player {newData.ClientId} added with name '{playerName}' on client.");
+        }
+    }
+
+    private void OnPlayerScoresChanged(SerializableDictionary<ulong, int> oldValue, SerializableDictionary<ulong, int> newValue)
+    {
+        foreach (var entry in newValue)
+        {
+            ulong clientId = entry.Key;
+            int score = entry.Value;
+
+            // Manually search for the player entry in playerDataList
+            FixedString64Bytes playerName = default;
+            foreach (var playerData in playerDataList)
             {
-                SubtractPlayerScore(clientId, 2);
-                Debug.Log($"[ScoreManager] Applied crow penalty to Player {clientId}. New score: {playerScores[clientId]}");
+                if (playerData.ClientId == clientId)
+                {
+                    playerName = playerData.Name;
+                    break;
+                }
             }
 
-            yield return new WaitForSeconds(5f);
+            if (!playerName.IsEmpty)
+            {
+                Debug.Log($"[ScoreManager] Score updated for Player {clientId} (Name: {playerName}): {score}");
+                Scoreboard.instance.ScoreBoardUpdater(playerName.ToString(), score);
+            }
+            else
+            {
+                Debug.LogWarning($"[ScoreManager] Player name not found for Client {clientId}. Cannot update scoreboard.");
+            }
         }
     }
 }
+
+
+//using System.Collections;
+//using System.Collections.Generic;
+//using Unity.Multiplayer.Samples.BossRoom;
+//using UnityEngine;
+//using Unity.Netcode;
+
+//public class ScoreManager : NetworkBehaviour
+//{
+//    public static ScoreManager Instance { get; private set; }
+
+//    public Dictionary<ulong, int> playerScores = new Dictionary<ulong, int>();
+//    public Dictionary<ulong, string> playerNames = new Dictionary<ulong, string>();
+
+//    private void Awake()
+//    {
+//        if (Instance != null && Instance != this)
+//        {
+//            Destroy(gameObject);
+//            return;
+//        }
+//        Instance = this;
+//    }
+
+//    public override void OnNetworkSpawn()
+//    {
+//        if (IsServer)
+//        {
+//            StartCrowPenaltyCoroutine();
+//        }
+//    }
+
+//    //[ServerRpc(RequireOwnership = false)]
+//    public void InitializePlayerScore(ulong ownerClientId, string name)
+//    {
+//        if (!playerScores.ContainsKey(ownerClientId))
+//        {
+//            playerScores[ownerClientId] = 200;
+//            playerNames[ownerClientId] = name;
+//            Debug.Log($"[ScoreManager] Player {ownerClientId} initialized with a starting score of 200 and name '{name}'.");
+//            Scoreboard.instance.ScoreBoardItemInitializer(ownerClientId, playerScores[ownerClientId], name);
+//            UpdatePlayerScoreClientRpc(ownerClientId, 200);
+//        }
+//        else
+//        {
+//            Debug.LogWarning($"[ScoreManager] Player {ownerClientId} already initialized.");
+//        }
+//    }
+
+//    public void UpdatePlayerScore(ulong ownerClientId, int newScore)
+//    {
+//        if (playerScores.ContainsKey(ownerClientId))
+//        {
+//            playerScores[ownerClientId] = newScore;
+//            Debug.Log($"[ScoreManager] Updating Player {ownerClientId} score to {newScore}.");
+//            UpdatePlayerScoreClientRpc(ownerClientId, newScore);
+//        }
+//        else
+//        {
+//            Debug.LogWarning($"[ScoreManager] No entry found for Player {ownerClientId}. Cannot update score.");
+//        }
+//    }
+
+//    [ClientRpc]
+//    public void UpdatePlayerScoreClientRpc(ulong ownerClientId, int newScore)
+//    {
+//        if (playerNames.ContainsKey(ownerClientId))
+//        {
+//            string playerName = playerNames[ownerClientId];
+//            Debug.Log($"[ScoreManager] Received score update for Player {ownerClientId} (Name: {playerName}): {newScore}");
+//            Scoreboard.instance.ScoreBoardUpdater(playerName, newScore);
+//        }
+//        else
+//        {
+//            Debug.LogWarning($"[ScoreManager] Player name not found for Player {ownerClientId}. Cannot update scoreboard.");
+//        }
+//    }
+
+//    public void AddPlayerScore(ulong ownerClientId, int scoreToAdd)
+//    {
+//        if (playerScores.ContainsKey(ownerClientId))
+//        {
+//            playerScores[ownerClientId] += scoreToAdd;
+//            UpdatePlayerScore(ownerClientId, playerScores[ownerClientId]);
+//        }
+//    }
+
+//    public void SubtractPlayerScore(ulong ownerClientId, int scoreToSubtract)
+//    {
+//        if (playerScores.ContainsKey(ownerClientId))
+//        {
+//            int newScore = Mathf.Max(0, playerScores[ownerClientId] - scoreToSubtract);
+//            UpdatePlayerScore(ownerClientId, newScore);
+//        }
+//    }
+
+
+//    public void StartCrowPenaltyCoroutine()
+//    {
+//        StartCoroutine(ApplyCrowPenaltyCoroutine());
+//    }
+
+//    private IEnumerator ApplyCrowPenaltyCoroutine()
+//    {
+//        yield return new WaitUntil(() => PlatformManager.Instance != null && PlatformManager.Instance.AreAllPlatformsOccupied());
+
+//        while (true)
+//        {
+//            // Create a list to store client IDs whose scores need to be updated
+//            List<ulong> clientsToModify = new List<ulong>();
+
+//            foreach (var entry in playerScores)
+//            {
+//                ulong ownerClientId = entry.Key;
+
+//                if (CrowManager.Instance != null && CrowManager.Instance.GetCurrentCrow().OwnerClientId == ownerClientId)
+//                {
+//                    clientsToModify.Add(ownerClientId);
+//                }
+//            }
+
+//            // Apply the penalties after the iteration
+//            foreach (ulong clientId in clientsToModify)
+//            {
+//                SubtractPlayerScore(clientId, 2);
+//                Debug.Log($"[ScoreManager] Applied crow penalty to Player {clientId}. New score: {playerScores[clientId]}");
+//            }
+
+//            yield return new WaitForSeconds(5f);
+//        }
+//    }
+//}
diff --git a/Assets/Scripts/Gameplay/SerializableDictionary.cs b/Assets/Scripts/Gameplay/SerializableDictionary.cs
new file mode 100644
index 00000000..8b260792
--- /dev/null
+++ b/Assets/Scripts/Gameplay/SerializableDictionary.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+[Serializable]
+public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
+{
+    [SerializeField] private List<TKey> keys = new List<TKey>();
+    [SerializeField] private List<TValue> values = new List<TValue>();
+
+    public void OnBeforeSerialize()
+    {
+        keys.Clear();
+        values.Clear();
+
+        foreach (var pair in this)
+        {
+            keys.Add(pair.Key);
+            values.Add(pair.Value);
+        }
+    }
+
+    public void OnAfterDeserialize()
+    {
+        Clear();
+
+        if (keys.Count != values.Count)
+            throw new Exception("Key and value count mismatch!");
+
+        for (int i = 0; i < keys.Count; i++)
+        {
+            Add(keys[i], values[i]);
+        }
+    }
+}
diff --git a/Assets/Scripts/Gameplay/SerializableDictionary.cs.meta b/Assets/Scripts/Gameplay/SerializableDictionary.cs.meta
new file mode 100644
index 00000000..c68ddfbb
--- /dev/null
+++ b/Assets/Scripts/Gameplay/SerializableDictionary.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 318306cbce4e91a468e5d39c8bd4218a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: