272 lines
8.1 KiB
C#
272 lines
8.1 KiB
C#
using asap.core;
|
|
using PlayFab;
|
|
using PlayFab.ClientModels;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
|
|
public class PlayFabMgr : MonoBehaviour
|
|
{
|
|
const int USER_DATA_UPDATE_INTERVAL = 40;
|
|
const int Buffer_Count = 5;
|
|
const string Last_Update_Time_Key_Prefix = "LastUpdateTime";
|
|
const string Local_Data_Key_prefix = "LocalData";
|
|
private string Last_Update_Time_Key;
|
|
private string Local_Data_Key;
|
|
|
|
private LinkedList<Dictionary<string, string>> dataBuffer;
|
|
private Dictionary<string, string> swapData;
|
|
private Dictionary<string, long> lastUpdateTime;
|
|
private Dictionary<string, string> localData;
|
|
|
|
private CancellationTokenSource tokenSource;
|
|
|
|
static PlayFabMgr _instance = null;
|
|
|
|
public static PlayFabMgr Instance
|
|
{
|
|
get
|
|
{
|
|
if (_instance == null)
|
|
{
|
|
GameDebug.LogError("PlayFabMgr Instance doesn't exist yet!");
|
|
}
|
|
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, string> CompareDatas(Dictionary<string, UserDataRecord> remoteDatas, string userId)
|
|
{
|
|
Last_Update_Time_Key = $"{Last_Update_Time_Key_Prefix}_{userId}";
|
|
Local_Data_Key = $"{Local_Data_Key_prefix}_{userId}";
|
|
var lastUpdateTimeStr = PlayerPrefs.GetString(Last_Update_Time_Key, string.Empty);
|
|
var now = ZZTimeHelper.UtcNow().Ticks;
|
|
localData = new Dictionary<string, string>();
|
|
lastUpdateTime = new Dictionary<string, long>();
|
|
|
|
if (string.IsNullOrEmpty(lastUpdateTimeStr))
|
|
{
|
|
if (remoteDatas != null && remoteDatas.Count > 0)
|
|
{
|
|
lastUpdateTime = remoteDatas.ToDictionary(x => x.Key, x => x.Value.LastUpdated.Ticks);
|
|
localData = remoteDatas.ToDictionary(x => x.Key, x => x.Value.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var tempLastUpdateTime = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, long>>(lastUpdateTimeStr);
|
|
var localDataStr = PlayerPrefs.GetString(Local_Data_Key);
|
|
var tempLocalData = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(localDataStr);
|
|
var allKeys = tempLocalData.Keys.Concat(remoteDatas.Keys).Distinct();
|
|
foreach (var key in allKeys)
|
|
{
|
|
var localValue = tempLocalData.ContainsKey(key) ? tempLocalData[key] : null;
|
|
var remoteValue = remoteDatas.ContainsKey(key) ? remoteDatas[key].Value : null;
|
|
var localTime = tempLastUpdateTime.ContainsKey(key) ? tempLastUpdateTime[key] : 0;
|
|
var remoteTime = remoteDatas.ContainsKey(key) ? remoteDatas[key].LastUpdated.Ticks : 0;
|
|
|
|
if (localValue == null)
|
|
{
|
|
localData[key] = remoteValue;
|
|
lastUpdateTime[key] = remoteTime;
|
|
continue;
|
|
}
|
|
|
|
if (remoteValue == null)
|
|
{
|
|
UpdateUserDataValue(key, localValue);
|
|
lastUpdateTime[key] = now;
|
|
localData[key] = localValue;
|
|
continue;
|
|
}
|
|
|
|
var timeDelta = localTime - remoteTime;
|
|
if (Math.Abs(timeDelta) > TimeSpan.TicksPerSecond * 60)
|
|
{
|
|
if (localTime > remoteTime)
|
|
{
|
|
UpdateUserDataValue(key, localValue);
|
|
lastUpdateTime[key] = now;
|
|
localData[key] = localValue;
|
|
}
|
|
else
|
|
{
|
|
lastUpdateTime[key] = remoteTime;
|
|
localData[key] = remoteValue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lastUpdateTime[key] = localTime;
|
|
localData[key] = localValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
PlayerPrefs.SetString(Last_Update_Time_Key, LitJson.JsonMapper.ToJson(lastUpdateTime));
|
|
PlayerPrefs.SetString(Local_Data_Key, LitJson.JsonMapper.ToJson(localData));
|
|
return localData;
|
|
}
|
|
|
|
void Awake()
|
|
{
|
|
_instance = this;
|
|
InitBuffer();
|
|
tokenSource = new CancellationTokenSource();
|
|
UpdateUserDataValue(tokenSource.Token);
|
|
}
|
|
|
|
void InitBuffer()
|
|
{
|
|
dataBuffer = new LinkedList<Dictionary<string, string>>();
|
|
for (int i = 0; i < Buffer_Count; i++)
|
|
{
|
|
dataBuffer.AddLast(new Dictionary<string, string>());
|
|
}
|
|
swapData = new Dictionary<string, string>();
|
|
}
|
|
|
|
void OnApplicationPause(bool is_pause)
|
|
{
|
|
if (is_pause)
|
|
{
|
|
tokenSource.Cancel();
|
|
tokenSource = null;
|
|
SyncData();
|
|
}
|
|
else
|
|
{
|
|
tokenSource = new CancellationTokenSource();
|
|
UpdateUserDataValue(tokenSource.Token);
|
|
}
|
|
}
|
|
|
|
void OnApplicationQuit()
|
|
{
|
|
tokenSource.Cancel();
|
|
tokenSource = null;
|
|
SyncData();
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
tokenSource?.Cancel();
|
|
tokenSource = null;
|
|
SyncData();
|
|
}
|
|
|
|
private async void UpdateUserDataValue(CancellationToken token)
|
|
{
|
|
try
|
|
{
|
|
while (!token.IsCancellationRequested)
|
|
{
|
|
SyncData();
|
|
await Task.Delay(USER_DATA_UPDATE_INTERVAL * 1000);
|
|
}
|
|
}
|
|
catch (System.Exception) { }
|
|
}
|
|
|
|
public void UpdateUserDataValue(string key, string data, bool remoteStore = true)
|
|
{
|
|
if(remoteStore)
|
|
{
|
|
var cbuff = dataBuffer.First;
|
|
while (cbuff != null)
|
|
{
|
|
var datas = cbuff.Value;
|
|
if (!datas.ContainsKey(key) && datas.Count >= 10)
|
|
{
|
|
cbuff = cbuff.Next;
|
|
continue;
|
|
}
|
|
cbuff.Value[key] = data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
localData[key] = data;
|
|
lastUpdateTime[key] = ZZTimeHelper.UtcNow().Ticks;
|
|
|
|
PlayerPrefs.SetString(Last_Update_Time_Key, LitJson.JsonMapper.ToJson(lastUpdateTime));
|
|
PlayerPrefs.SetString(Local_Data_Key, LitJson.JsonMapper.ToJson(localData));
|
|
}
|
|
/// <summary>
|
|
/// 没有数据返回 null 一定一定一定判空!!!
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
public string GetLocalData(string key)
|
|
{
|
|
if (localData.ContainsKey(key))
|
|
{
|
|
return localData[key];
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void SyncData()
|
|
{
|
|
var buf = dataBuffer.First;
|
|
if (buf.Value.Count == 0)
|
|
return;
|
|
|
|
var datas = buf.Value;
|
|
swapData.Clear();
|
|
buf.Value = swapData;
|
|
swapData = datas;
|
|
|
|
dataBuffer.RemoveFirst();
|
|
dataBuffer.AddLast(buf);
|
|
|
|
var request = new UpdateUserDataRequest()
|
|
{
|
|
Data = swapData,
|
|
Permission = UserDataPermission.Public
|
|
};
|
|
|
|
PlayFabClientAPI.UpdateUserData(request, null, UpdateFaild);
|
|
}
|
|
|
|
private void UpdateFaild(PlayFabError error)
|
|
{
|
|
GameDebug.LogError("[PlayFabMgr]UpdateUserData Error: {0}", error.ToString());
|
|
}
|
|
|
|
public async Task<bool> SyncDataImmidiate(string key, string data)
|
|
{
|
|
foreach(var buff in dataBuffer)
|
|
{
|
|
if(buff.ContainsKey(key))
|
|
{
|
|
buff.Remove(key);
|
|
}
|
|
}
|
|
|
|
var taskSource = new TaskCompletionSource<bool>();
|
|
|
|
var request = new UpdateUserDataRequest()
|
|
{
|
|
Data = new Dictionary<string, string>() { { key, data } },
|
|
Permission = UserDataPermission.Public
|
|
};
|
|
|
|
PlayFabClientAPI.UpdateUserData(request,
|
|
(r) => { taskSource.SetResult(true); },
|
|
(e) => {
|
|
taskSource.SetResult(false);
|
|
UnityEngine.Debug.LogError($"[PlayFabMgr]UpdateUserData Error: {e.ToString()}");
|
|
}
|
|
);
|
|
return await taskSource.Task;
|
|
}
|
|
}
|