Data saving bug fixed

dev-ali
Ali Sharoz 22 hours ago
parent 2eb9bd4e37
commit efd902c023

@ -1,35 +1,28 @@
using UnityEngine;
using PlayFab;
using PlayFab.ClientModels;
using System.Collections;
using System.Collections.Generic;
public class PlayerPrefsSyncManager : MonoBehaviour
{
private static PlayerPrefsSyncManager instance;
public static PlayerPrefsSyncManager instance;
private void OnApplicationQuit()
{
SyncPlayerPrefsToPlayFabOnQuit();
}
private const int MaxKeysPerRequest = 10;
private void OnApplicationPause()
{
SyncPlayerPrefsToPlayFabOnQuit();
}
private static float syncDelay = 3f;
private static float syncTimer = 0f;
private static bool syncPending = false;
private void OnApplicationFocus(bool focus)
{
//if (!focus)
SyncPlayerPrefsToPlayFabOnQuit();
}
private Coroutine periodicSyncCoroutine;
void Awake()
private void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(this.gameObject);
Application.quitting += SyncPlayerPrefsToPlayFabOnQuit;
Application.wantsToQuit += OnWantsToQuit;
}
else
{
@ -37,79 +30,179 @@ public class PlayerPrefsSyncManager : MonoBehaviour
}
}
private const int MaxKeysPerRequest = 10;
private void Start()
{
periodicSyncCoroutine = StartCoroutine(PeriodicSyncCoroutine());
}
private void Update()
{
if (syncPending)
{
syncTimer -= Time.unscaledDeltaTime;
if (syncTimer <= 0f)
{
syncPending = false;
Debug.Log("🌀 Debounced sync triggered.");
StartCoroutine(SyncCoroutine());
}
}
}
public static void RequestSync()
{
if (!instance) return;
syncTimer = syncDelay;
syncPending = true;
}
public void SyncPlayerPrefsToPlayFabOnQuit()
private bool OnWantsToQuit()
{
if (PlayFabClientAPI.IsClientLoggedIn())
{
StartCoroutine(SyncThenQuit());
return false;
}
return true;
}
private void OnApplicationPause(bool pause)
{
if (pause)
{
Debug.Log("📱 App paused — syncing.");
StartCoroutine(SyncCoroutine());
}
}
var keys = PlayerPrefsKeys.GetAllKeys();
if (keys.Count == 0)
private IEnumerator PeriodicSyncCoroutine()
{
float interval = 120f; // 3 minutes
while (true)
{
yield return new WaitForSecondsRealtime(interval);
if (PlayFabClientAPI.IsClientLoggedIn())
{
Debug.Log("No PlayerPrefs keys registered, skipping sync.");
return;
Debug.Log("⏰ Periodic sync triggered.");
yield return SyncCoroutine();
}
}
}
private IEnumerator SyncThenQuit()
{
bool isDone = false;
SyncPlayerPrefsToPlayFab(() =>
{
isDone = true;
});
Dictionary<string, string> allPrefs = new Dictionary<string, string>();
float timeout = 5f;
float timer = 0f;
foreach (var key in keys)
while (!isDone && timer < timeout)
{
timer += Time.unscaledDeltaTime;
yield return null;
}
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
private IEnumerator SyncCoroutine()
{
bool isDone = false;
SyncPlayerPrefsToPlayFab(() =>
{
isDone = true;
});
float timeout = 5f;
float timer = 0f;
while (!isDone && timer < timeout)
{
timer += Time.unscaledDeltaTime;
yield return null;
}
}
public void SyncPlayerPrefsToPlayFab(System.Action onComplete = null)
{
if (!PlayFabClientAPI.IsClientLoggedIn())
{
onComplete?.Invoke();
return;
}
var keys = PlayerPrefsKeys.GetAllKeys();
if (keys.Count == 0)
{
Debug.Log("No PlayerPrefs keys registered, skipping sync.");
onComplete?.Invoke();
return;
}
Dictionary<string, string> allPrefs = new Dictionary<string, string>();
foreach (var key in keys)
{
string strVal = PlayerPrefs.GetString(key, "__MISSING__");
if (strVal != "__MISSING__")
{
string strVal = PlayerPrefs.GetString(key, "__MISSING__");
if (strVal != "__MISSING__")
{
allPrefs[key] = "string:" + strVal;
continue;
}
int intVal = PlayerPrefs.GetInt(key, int.MinValue + 1);
if (intVal != int.MinValue + 1)
{
allPrefs[key] = "int:" + intVal;
continue;
}
float floatVal = PlayerPrefs.GetFloat(key, float.MinValue + 1);
if (floatVal != float.MinValue + 1)
{
allPrefs[key] = "float:" + floatVal.ToString("R");
}
allPrefs[key] = "string:" + strVal;
continue;
}
foreach (var pair in allPrefs)
int intVal = PlayerPrefs.GetInt(key, int.MinValue + 1);
if (intVal != int.MinValue + 1)
{
Debug.Log($"[Sync] {pair.Key} = {pair.Value}");
allPrefs[key] = "int:" + intVal;
continue;
}
// Split into batches of 10
var batches = new List<Dictionary<string, string>>();
var currentBatch = new Dictionary<string, string>();
foreach (var pair in allPrefs)
float floatVal = PlayerPrefs.GetFloat(key, float.MinValue + 1);
if (floatVal != float.MinValue + 1)
{
currentBatch[pair.Key] = pair.Value;
if (currentBatch.Count == MaxKeysPerRequest)
{
batches.Add(currentBatch);
currentBatch = new Dictionary<string, string>();
}
allPrefs[key] = "float:" + floatVal.ToString("R");
}
}
if (currentBatch.Count > 0)
// Batch in chunks of 10
var batches = new List<Dictionary<string, string>>();
var currentBatch = new Dictionary<string, string>();
foreach (var pair in allPrefs)
{
currentBatch[pair.Key] = pair.Value;
if (currentBatch.Count == MaxKeysPerRequest)
{
batches.Add(currentBatch);
currentBatch = new Dictionary<string, string>();
}
}
UploadPlayerPrefsBatches(batches, 0);
if (currentBatch.Count > 0)
{
batches.Add(currentBatch);
}
UploadPlayerPrefsBatches(batches, 0, onComplete);
}
private void UploadPlayerPrefsBatches(List<Dictionary<string, string>> batches, int index)
private void UploadPlayerPrefsBatches(List<Dictionary<string, string>> batches, int index, System.Action onComplete)
{
if (index >= batches.Count)
{
Debug.Log("✅ All PlayerPrefs batches synced to PlayFab.");
onComplete?.Invoke();
return;
}
@ -118,20 +211,17 @@ public class PlayerPrefsSyncManager : MonoBehaviour
Data = batches[index],
Permission = UserDataPermission.Public
};
if (PlayFabClientAPI.IsClientLoggedIn())
{
PlayFabClientAPI.UpdateUserData(request,
PlayFabClientAPI.UpdateUserData(request,
result =>
{
Debug.Log($"✅ Synced batch {index + 1}/{batches.Count}");
UploadPlayerPrefsBatches(batches, index + 1);
UploadPlayerPrefsBatches(batches, index + 1, onComplete);
},
error =>
{
Debug.LogError($"❌ Failed to sync batch {index + 1}/{batches.Count}: {error.GenerateErrorReport()}");
onComplete?.Invoke(); // Fail fast or add retry logic
});
}
}
}

@ -1,4 +1,5 @@
using UnityEngine;
public static class SafePlayerPrefs
{
public static void SetInt(string key, int value)
@ -6,6 +7,7 @@ public static class SafePlayerPrefs
PlayerPrefs.SetInt(key, value);
PlayerPrefsKeys.RegisterKey(key);
PlayerPrefs.Save();
PlayerPrefsSyncManager.RequestSync();
}
public static void SetFloat(string key, float value)
@ -13,6 +15,7 @@ public static class SafePlayerPrefs
PlayerPrefs.SetFloat(key, value);
PlayerPrefsKeys.RegisterKey(key);
PlayerPrefs.Save();
PlayerPrefsSyncManager.RequestSync();
}
public static void SetString(string key, string value)
@ -20,5 +23,6 @@ public static class SafePlayerPrefs
PlayerPrefs.SetString(key, value);
PlayerPrefsKeys.RegisterKey(key);
PlayerPrefs.Save();
PlayerPrefsSyncManager.RequestSync();
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 36e16ab8a506e7948a8c74853e2ce4a3
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -1,10 +1,29 @@
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using PlayFab;
using PlayFab.ClientModels;
using PlayFab.EconomyModels;
using UnityEngine;
using System;
public static class LeaderboardUtils
{
public static string GetStatisticName(this LeaderboardType type)
{
return type switch
{
LeaderboardType.PiPuzzle => "PiPuzzle_LevelsCompleted",
LeaderboardType.HeroesOfLabyrinth => "HeroesOfLabyrinth_LevelsCompleted",
LeaderboardType.TowerEscape => "TowerEscape_HighScore",
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
}
public enum LeaderboardType
{
PiPuzzle,
HeroesOfLabyrinth,
TowerEscape
}
public class LeaderboardUIScreen : MonoBehaviour
{
@ -15,122 +34,45 @@ public class LeaderboardUIScreen : MonoBehaviour
[SerializeField] private Transform contentTowerEscape;
[SerializeField] private List<LBPedestalItem> _lbPedestalItems;
public void Init()
{
PlayFabManager.Instance.playFabLeaderboards.GetLeaderboard(OnLeaderboardFetchSuccess, OnLeaderboardFetchFailure);
InitHOL();
InitTowerEscape();
}
public void InitHOL()
{
PlayFabManager.Instance.playFabLeaderboards.GetLeaderboardHOL(OnLeaderboardFetchSuccessHOL, OnLeaderboardFetchFailure);
}
public void InitTowerEscape()
{
PlayFabManager.Instance.playFabLeaderboards.GetLeaderboardTowerEscape(OnLeaderboardFetchSuccessTowerEscape, OnLeaderboardFetchFailure);
LoadLeaderboard(LeaderboardType.PiPuzzle, content);
LoadLeaderboard(LeaderboardType.HeroesOfLabyrinth, contentHOL);
LoadLeaderboard(LeaderboardType.TowerEscape, contentTowerEscape);
}
public void OnClose()
{
// GameObject[] temp = content.transform.GetComponentsInChildren<GameObject>();
// for (int i = 0; i < temp.Length; i++)
// {
// Destroy(temp[i]);
// }
}
private void OnLeaderboardFetchSuccess(List<PlayerLeaderboardEntry> leaderboard)
{
foreach (Transform child in content.transform)
{
Destroy(child.gameObject);
}
PopulateLeaderboard(leaderboard);
}
private void OnLeaderboardFetchSuccessHOL(List<PlayerLeaderboardEntry> leaderboard)
private void LoadLeaderboard(LeaderboardType type, Transform content)
{
foreach (Transform child in contentHOL.transform)
{
Destroy(child.gameObject);
}
PopulateLeaderboardHOL(leaderboard);
string statName = type.GetStatisticName();
PlayFabManager.Instance.playFabLeaderboards.GetLeaderboard(statName, 10,
leaderboard => PopulateLeaderboard(leaderboard, content),
OnLeaderboardFetchFailure
);
}
private void OnLeaderboardFetchSuccessTowerEscape(List<PlayerLeaderboardEntry> leaderboard)
private void PopulateLeaderboard(List<PlayerLeaderboardEntry> leaderboard, Transform parent)
{
foreach (Transform child in contentTowerEscape.transform)
{
foreach (Transform child in parent)
Destroy(child.gameObject);
}
PopulateLeaderboardTowerEscape(leaderboard);
}
private void PopulateLeaderboard(List<PlayerLeaderboardEntry> leaderboard)
{
foreach (PlayerLeaderboardEntry lbEntry in leaderboard)
{
PopulateLbItem(lbEntry);
if (lbEntry.Position <= 2)
{
PopulatePedestalItem(lbEntry);
}
}
content.GetComponent<RectTransform>().DOAnchorPosX(0f,1f).SetEase(Ease.OutElastic);
}
private void PopulateLeaderboardHOL(List<PlayerLeaderboardEntry> leaderboard)
{
foreach (PlayerLeaderboardEntry lbEntry in leaderboard)
foreach (var lbEntry in leaderboard)
{
PopulateLbItemHOL(lbEntry);
PopulateLbItem(lbEntry, parent);
if (lbEntry.Position <= 2)
{
PopulatePedestalItem(lbEntry);
}
}
contentHOL.GetComponent<RectTransform>().DOAnchorPosX(0f, 1f).SetEase(Ease.OutElastic);
}
private void PopulateLeaderboardTowerEscape(List<PlayerLeaderboardEntry> leaderboard)
{
foreach (PlayerLeaderboardEntry lbEntry in leaderboard)
{
PopulateLbItemTowerEscape(lbEntry);
if (lbEntry.Position <= 2)
{
PopulatePedestalItem(lbEntry);
}
}
contentTowerEscape.GetComponent<RectTransform>().DOAnchorPosX(0f, 1f).SetEase(Ease.OutElastic);
}
private void PopulateLbItem(PlayerLeaderboardEntry lbEntry)
private void PopulateLbItem(PlayerLeaderboardEntry lbEntry, Transform parent)
{
bool isSelf = false;
if (lbEntry.Profile != null && PlayFabManager.Instance.playFabUserDataManager.myProfile != null)
{
isSelf = lbEntry.Profile.PlayerId == PlayFabManager.Instance.playFabUserDataManager.myProfile.PlayerId;
}
bool isSelf = lbEntry.Profile != null &&
PlayFabManager.Instance.playFabUserDataManager.myProfile != null &&
lbEntry.Profile.PlayerId == PlayFabManager.Instance.playFabUserDataManager.myProfile.PlayerId;
LBEntryItem lbItem = Instantiate(isSelf ? lbItemSelfPrefab : lbItemPrefab, content).GetComponent<LBEntryItem>();
lbItem.nameText.text = lbEntry.DisplayName ?? lbEntry.PlayFabId;
lbItem.rankText.text = (lbEntry.Position + 1).ToString();
lbItem.scoreText.text = lbEntry.StatValue.ToString();
var prefab = isSelf ? lbItemSelfPrefab : lbItemPrefab;
var lbItem = Instantiate(prefab, parent).GetComponent<LBEntryItem>();
PlayFabManager.Instance.playFabUserDataManager.GetPlayerAvatarImage(lbEntry.PlayFabId, sprite =>
{
lbItem.profilePic.sprite = sprite;
}, error =>
{
Debug.Log("Couldn't get pic");
});
}
private void PopulateLbItemHOL(PlayerLeaderboardEntry lbEntry)
{
bool isSelf = false;
if (lbEntry.Profile != null && PlayFabManager.Instance.playFabUserDataManager.myProfile != null)
{
isSelf = lbEntry.Profile.PlayerId == PlayFabManager.Instance.playFabUserDataManager.myProfile.PlayerId;
}
LBEntryItem lbItem = Instantiate(isSelf ? lbItemSelfPrefab : lbItemPrefab, contentHOL).GetComponent<LBEntryItem>();
lbItem.nameText.text = lbEntry.DisplayName ?? lbEntry.PlayFabId;
lbItem.rankText.text = (lbEntry.Position + 1).ToString();
lbItem.scoreText.text = lbEntry.StatValue.ToString();
@ -143,49 +85,29 @@ public class LeaderboardUIScreen : MonoBehaviour
Debug.Log("Couldn't get pic");
});
}
private void PopulateLbItemTowerEscape(PlayerLeaderboardEntry lbEntry)
private void PopulatePedestalItem(PlayerLeaderboardEntry lbEntry)
{
bool isSelf = false;
if (lbEntry.Profile != null && PlayFabManager.Instance.playFabUserDataManager.myProfile != null)
{
isSelf = lbEntry.Profile.PlayerId == PlayFabManager.Instance.playFabUserDataManager.myProfile.PlayerId;
}
if (lbEntry.Position >= _lbPedestalItems.Count) return;
LBEntryItem lbItem = Instantiate(isSelf ? lbItemSelfPrefab : lbItemPrefab, contentTowerEscape).GetComponent<LBEntryItem>();
lbItem.nameText.text = lbEntry.DisplayName ?? lbEntry.PlayFabId;
lbItem.rankText.text = (lbEntry.Position + 1).ToString();
lbItem.scoreText.text = lbEntry.StatValue.ToString();
var pedestalItem = _lbPedestalItems[lbEntry.Position];
pedestalItem.nameText.text = lbEntry.DisplayName ?? lbEntry.PlayFabId;
pedestalItem.scoreText.text = lbEntry.StatValue.ToString();
PlayFabManager.Instance.playFabUserDataManager.GetPlayerAvatarImage(lbEntry.PlayFabId, sprite =>
{
lbItem.profilePic.sprite = sprite;
pedestalItem.profilePic.sprite = sprite;
}, error =>
{
Debug.Log("Couldn't get pic");
});
}
private void PopulatePedestalItem(PlayerLeaderboardEntry lbEntry)
{
LBPedestalItem pedestalItem = _lbPedestalItems[lbEntry.Position];
pedestalItem.nameText.text = "PlayerName";
pedestalItem.scoreText.text = "0";
pedestalItem.nameText.text = lbEntry.DisplayName??lbEntry.PlayFabId;
pedestalItem.scoreText.text = lbEntry.StatValue.ToString();
PlayFabManager.Instance.playFabUserDataManager.GetPlayerAvatarImage(lbEntry.PlayFabId, (sprite) =>
{
pedestalItem.profilePic.sprite = sprite;
},
(s) =>
{
Debug.Log("Could'nt get pic");
});
}
private void OnLeaderboardFetchFailure(PlayFabError obj)
{
Debug.Log("Couldn't Load Leaderboards");
throw new System.NotImplementedException();
Debug.Log("Couldn't Load Leaderboards: " + obj.GenerateErrorReport());
}
}
public void OnClose() { }
}

@ -7,112 +7,69 @@ using UnityEngine;
public class PlayFabLeaderboards : MonoBehaviour
{
public static string DisplayName;
public void UpdateLevelsCompleted(int levelsCompleted)
{
var request = new UpdatePlayerStatisticsRequest
{
Statistics = new List<StatisticUpdate>
{
new StatisticUpdate
{
StatisticName = GameConstants.LevelCompletedStatsKey,
Value = levelsCompleted
}
}
};
if (PlayFabClientAPI.IsClientLoggedIn())
PlayFabClientAPI.UpdatePlayerStatistics(request, OnStatisticsUpdateSuccess, OnStatisticsUpdateFailure);
UpdateStatistic(GameConstants.LevelCompletedStatsKey, levelsCompleted);
}
public void UpdateLevelCompletionTime(string tier, int levelNumber, int completionTimeInSeconds)
{
string statisticName = $"{GameConstants.GameNamePrefix}_{tier}_Level_{levelNumber}_Time";
UpdateStatistic(statisticName, completionTimeInSeconds);
}
private void UpdateStatistic(string statName, int value)
{
var request = new UpdatePlayerStatisticsRequest
{
Statistics = new List<StatisticUpdate>
{
new StatisticUpdate
{
StatisticName = statisticName,
Value = completionTimeInSeconds
}
new StatisticUpdate { StatisticName = statName, Value = value }
}
};
PlayFabClientAPI.UpdatePlayerStatistics(request, OnStatisticsUpdateSuccess, OnStatisticsUpdateFailure);
}
private void OnStatisticsUpdateSuccess(UpdatePlayerStatisticsResult result)
{
Debug.Log("Successfully updated player statistics!");
}
private void OnStatisticsUpdateFailure(PlayFabError error)
{
Debug.LogError("Failed to update player statistics: " + error.GenerateErrorReport());
if (PlayFabClientAPI.IsClientLoggedIn())
{
PlayFabClientAPI.UpdatePlayerStatistics(request, OnStatisticsUpdateSuccess, OnStatisticsUpdateFailure);
}
}
public void GetLeaderboardByKey(string key, Action<List<PlayerLeaderboardEntry>> onSuccess, Action<PlayFabError> onFailure)
public void GetLeaderboard(string statisticName, int maxResults, Action<List<PlayerLeaderboardEntry>> onSuccess, Action<PlayFabError> onFailure)
{
var request = new GetLeaderboardRequest
{
StatisticName = key,
StatisticName = statisticName,
StartPosition = 0,
MaxResultsCount = 1
MaxResultsCount = maxResults
};
PlayFabClientAPI.GetLeaderboard(request, result => onSuccess(result.Leaderboard), onFailure);
}
public void GetLeaderboard(Action<List<PlayerLeaderboardEntry>> onSuccess, Action<PlayFabError> onFailure)
public void GetLeaderboardByKey(string key, Action<List<PlayerLeaderboardEntry>> onSuccess, Action<PlayFabError> onFailure)
{
var request = new GetLeaderboardRequest
{
StatisticName = GameConstants.LevelCompletedStatsKey,
StartPosition = 0,
MaxResultsCount = 10
};
GetLeaderboard(key, 1, onSuccess, onFailure);
}
PlayFabClientAPI.GetLeaderboard(request, result => onSuccess(result.Leaderboard), onFailure);
}
public void GetLeaderboardHOL(Action<List<PlayerLeaderboardEntry>> onSuccess, Action<PlayFabError> onFailure)
public void SetDisplayName(string displayName)
{
var request = new GetLeaderboardRequest
{
//StatisticName = GameConstants.LevelCompletedStatsKey,
StatisticName = "HeroesOfLabyrinth_LevelsCompleted",
StartPosition = 0,
MaxResultsCount = 10
};
DisplayName = displayName;
PlayFabClientAPI.GetLeaderboard(request, result => onSuccess(result.Leaderboard), onFailure);
if (!PlayFabClientAPI.IsClientLoggedIn()) return;
var request = new UpdateUserTitleDisplayNameRequest { DisplayName = displayName };
PlayFabClientAPI.UpdateUserTitleDisplayName(request, OnDisplayNameUpdateSuccess, OnDisplayNameUpdateFailure);
}
public void GetLeaderboardTowerEscape(Action<List<PlayerLeaderboardEntry>> onSuccess, Action<PlayFabError> onFailure)
{
var request = new GetLeaderboardRequest
{
//StatisticName = GameConstants.LevelCompletedStatsKey,
StatisticName = "TowerEscape_HighScore",
StartPosition = 0,
MaxResultsCount = 10
};
PlayFabClientAPI.GetLeaderboard(request, result => onSuccess(result.Leaderboard), onFailure);
private void OnStatisticsUpdateSuccess(UpdatePlayerStatisticsResult result)
{
Debug.Log("Successfully updated player statistics!");
}
public void SetDisplayName(string displayName)
private void OnStatisticsUpdateFailure(PlayFabError error)
{
var request = new UpdateUserTitleDisplayNameRequest
{
DisplayName = displayName
};
if (PlayFabClientAPI.IsClientLoggedIn())
{
PlayFabClientAPI.UpdateUserTitleDisplayName(request, OnDisplayNameUpdateSuccess, OnDisplayNameUpdateFailure);
}
DisplayName = displayName;
Debug.LogError("Failed to update player statistics: " + error.GenerateErrorReport());
}
private void OnDisplayNameUpdateSuccess(UpdateUserTitleDisplayNameResult result)
@ -124,5 +81,4 @@ public class PlayFabLeaderboards : MonoBehaviour
{
Debug.LogError("Failed to set display name: " + error.GenerateErrorReport());
}
}
}

@ -140,7 +140,7 @@ PlayerSettings:
loadStoreDebugModeEnabled: 0
visionOSBundleVersion: 1.0
tvOSBundleVersion: 1.0
bundleVersion: 2.1
bundleVersion: 2.4
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0
@ -170,7 +170,7 @@ PlayerSettings:
iPhone: 0
tvOS: 0
overrideDefaultApplicationIdentifier: 1
AndroidBundleVersionCode: 10
AndroidBundleVersionCode: 14
AndroidMinSdkVersion: 22
AndroidTargetSdkVersion: 34
AndroidPreferredInstallLocation: 1

Loading…
Cancel
Save