You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
5.7 KiB
C#
228 lines
5.7 KiB
C#
using UnityEngine;
|
|
using PlayFab;
|
|
using PlayFab.ClientModels;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
public class PlayerPrefsSyncManager : MonoBehaviour
|
|
{
|
|
public static PlayerPrefsSyncManager instance;
|
|
|
|
private const int MaxKeysPerRequest = 10;
|
|
|
|
private static float syncDelay = 3f;
|
|
private static float syncTimer = 0f;
|
|
private static bool syncPending = false;
|
|
|
|
private Coroutine periodicSyncCoroutine;
|
|
|
|
private void Awake()
|
|
{
|
|
if (instance == null)
|
|
{
|
|
instance = this;
|
|
DontDestroyOnLoad(this.gameObject);
|
|
Application.wantsToQuit += OnWantsToQuit;
|
|
}
|
|
else
|
|
{
|
|
Destroy(gameObject);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
private IEnumerator PeriodicSyncCoroutine()
|
|
{
|
|
float interval = 120f; // 3 minutes
|
|
while (true)
|
|
{
|
|
yield return new WaitForSecondsRealtime(interval);
|
|
if (PlayFabClientAPI.IsClientLoggedIn())
|
|
{
|
|
Debug.Log("⏰ Periodic sync triggered.");
|
|
yield return SyncCoroutine();
|
|
}
|
|
}
|
|
}
|
|
|
|
private IEnumerator SyncThenQuit()
|
|
{
|
|
bool isDone = false;
|
|
|
|
SyncPlayerPrefsToPlayFab(() =>
|
|
{
|
|
isDone = true;
|
|
});
|
|
|
|
float timeout = 5f;
|
|
float timer = 0f;
|
|
|
|
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__")
|
|
{
|
|
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");
|
|
}
|
|
}
|
|
|
|
// 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>();
|
|
}
|
|
}
|
|
|
|
if (currentBatch.Count > 0)
|
|
{
|
|
batches.Add(currentBatch);
|
|
}
|
|
|
|
UploadPlayerPrefsBatches(batches, 0, onComplete);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
var request = new UpdateUserDataRequest
|
|
{
|
|
Data = batches[index],
|
|
Permission = UserDataPermission.Public
|
|
};
|
|
|
|
PlayFabClientAPI.UpdateUserData(request,
|
|
result =>
|
|
{
|
|
Debug.Log($"✅ Synced batch {index + 1}/{batches.Count}");
|
|
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
|
|
});
|
|
}
|
|
}
|