备份CatanBuilding瘦身独立工程
This commit is contained in:
271
Assets/Scripts/Core/PlayFabMgr.cs
Normal file
271
Assets/Scripts/Core/PlayFabMgr.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user