先修复一下,错误的场景 删除不必要的钓场资产 修复into场景 update:更新meta文件,修复报错 update:修复资源 修复第一章节建造 修复建造第三章 update:删除多余内容 update:更新README.md update:还原图标 update:更新README update:更新配置
297 lines
9.2 KiB
C#
297 lines
9.2 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;
|
||
//Debug.Log($"[PlayFabMgr]UpdateUserDataValue key:{key} data:{data}");
|
||
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);
|
||
#if UNITY_EDITOR
|
||
//IConfig config = GContext.container.Resolve<IConfig>();
|
||
//if (config != null && config.Get<string>("DBUG", string.Empty) == "1")
|
||
//{
|
||
try
|
||
{
|
||
//写入本地文件记录 key 和时间
|
||
string persistentPath = Application.persistentDataPath;
|
||
// 拼接文件名,这里使用txt文件记录数据
|
||
string filePath = System.IO.Path.Combine(persistentPath, "syncData.csv");
|
||
string currentTime = ZZTimeHelper.UtcNow().ToString("yyyy-MM-dd HH:mm:ss");
|
||
string dataLine = $"{currentTime},{string.Join(',', swapData.Keys)}";
|
||
// 使用追加模式写入文件,每次写入一行数据
|
||
using (System.IO.StreamWriter writer = System.IO.File.AppendText(filePath))
|
||
{
|
||
writer.WriteLine(dataLine);
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"记录数据时出错: {e.Message}");
|
||
}
|
||
//}
|
||
#endif
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|