using System; using UnityEngine; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using game.PlayerDataStorage; internal class KVMgr : MonoBehaviour { private static KVMgr instance; private KVPlayerSaveStore store; private CancellationTokenSource tokenSource; private void Awake() { instance = this; tokenSource = new CancellationTokenSource(); UpdateUserDataValue(tokenSource.Token); } internal static void Bind(KVPlayerSaveStore original) { if (instance != null) { instance.store = original; } } internal static void Unbind(KVPlayerSaveStore original) { if (instance != null && ReferenceEquals(instance.store, original)) { instance.store = null; } } internal static void Stop(KVPlayerSaveStore original) { if (instance != null && ReferenceEquals(instance.store, original)) { instance.Stop(); } } private void Stop() { tokenSource?.Cancel(); tokenSource?.Dispose(); tokenSource = null; } private async void UpdateUserDataValue(CancellationToken token) { try { while (!token.IsCancellationRequested) { SyncData(); await Task.Delay(40000); } } catch (Exception ex) { Debug.LogError("KV save schedule failed: " + ex.GetType().Name); } } private void SyncData() { var original = store; if (original != null && original.IsCurrent) { ObserveSync(original.SyncDataAsync()); } } private static async void ObserveSync(Task task) { try { var result = await task; if (result.TransportError != PlayerKVError.None || result.Blocked != 0 || result.Unknown != 0) { Debug.LogError("KV ordinary save failed: " + result.TransportError); } } catch (Exception ex) { Debug.LogError("KV ordinary save failed: " + ex.GetType().Name); } } private void OnApplicationPause(bool paused) { if (paused) { Stop(); SyncData(); } else { tokenSource = new CancellationTokenSource(); UpdateUserDataValue(tokenSource.Token); } } private void OnApplicationQuit() { Stop(); } private void OnDestroy() { Stop(); SyncData(); store = null; if (ReferenceEquals(instance, this)) { instance = null; } } internal static Task SyncKVAsync(PlayerKVGameSessionHandle handle, CancellationToken waitToken = default) { if (PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Sync)) { return Task.FromResult(new PlayerKVFlushResult(0, 0, 1, 0, PlayerKVError.Cancelled, handle == null ? PlayerKVSyncStatus.NotLoaded : handle.Status)); } if (handle == null) { return Task.FromResult(new PlayerKVFlushResult(0, 0, 1, 0, PlayerKVError.Validation, PlayerKVSyncStatus.NotLoaded)); } return handle.SyncDataAsync(waitToken); } internal static PlayerKVKeyReadResult ReadKV(PlayerKVGameSessionHandle handle, string key) { if (PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.ReadLocal)) { return new PlayerKVKeyReadResult(PlayerKVKeyReadError.LoadGuardActive, handle); } if (handle == null || !KVSnapshot.ValidKey(key)) { return new PlayerKVKeyReadResult(PlayerKVKeyReadError.InvalidInput, handle); } var read = handle.ReadLocalSnapshot(); if (!read.Success) { return new PlayerKVKeyReadResult(read.Error == PlayerKVLocalReadError.StaleOrClosing ? PlayerKVKeyReadError.StaleOrClosing : PlayerKVKeyReadError.NotReady, handle); } var found = read.Data.TryGetValue(key, out var value); return new PlayerKVKeyReadResult(PlayerKVKeyReadError.None, handle, found, value); } internal static PlayerKVEditResult WriteKV(PlayerKVGameSessionHandle handle, string key, string value, bool remoteStore = true) { if (PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Update)) { return new PlayerKVEditResult(new PlayerKVStateResult(PlayerKVChangeError.Blocked)); } if (handle == null) { return new PlayerKVEditResult(new PlayerKVStateResult(PlayerKVChangeError.Validation)); } return handle.Update(key, value, remoteStore, ZZTimeHelper.UtcNow().Ticks); } internal static async Task SyncDataImmidiate(PlayerKVGameSessionHandle handle, string key, string value) { var success = false; if (!PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Immediate) && handle != null && KVSnapshot.ValidKey(key) && value != null) { success = await handle.SyncDataImmidiate(key, value); } if (!success) { UnityEngine.Debug.LogError("[KVMgr::SyncDataImmidiate] Save failed."); } return success; } internal static async Task SyncDataImmidiate(PlayerKVGameSessionHandle handle, IReadOnlyDictionary values) { var success = false; if (!PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Immediate) && handle != null && values != null && values.Count > 0) { success = await handle.SyncDataImmidiate(values); } if (!success) { UnityEngine.Debug.LogError("[KVMgr::SyncDataImmidiate] Save failed."); } return success; } internal static Task FlushKVAsync(PlayerKVGameSessionHandle handle, CancellationToken waitToken = default) { if (PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Sync)) { return Task.FromResult(new PlayerKVFlushResult(0, 0, 1, 0, PlayerKVError.Cancelled, handle == null ? PlayerKVSyncStatus.NotLoaded : handle.Status)); } if (handle == null) { return Task.FromResult(new PlayerKVFlushResult(0, 0, 1, 0, PlayerKVError.Validation, PlayerKVSyncStatus.NotLoaded)); } return handle.FlushAsync(waitToken); } internal static Task WaitForKVTicketAsync(PlayerKVGameSessionHandle handle, PlayerKVSyncTicket ticket, CancellationToken waitToken = default) { if (PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Sync)) { return Task.FromResult(new PlayerKVTicketResult(PlayerKVTicketStatus.Blocked, PlayerKVError.Cancelled, handle == null ? PlayerKVSyncStatus.NotLoaded : handle.Status)); } if (handle == null) { return Task.FromResult(new PlayerKVTicketResult(PlayerKVTicketStatus.Blocked, PlayerKVError.Validation, PlayerKVSyncStatus.NotLoaded)); } return handle.WaitForTicketAsync(ticket, waitToken); } }