using UnityEngine; using System; using System.Collections; using System.Collections.Generic; public enum TimeValidationResult { Success, OffLine, TimeOut, HardwareTimeShiftFaster, HardwareTimeShiftLater, } public enum TimeValidateState { Requesting, Succeeded, Failed, } public class TimeValidateCoroutine { private Action removeAction; public TimeValidateCoroutine(Action removeAction){ this.removeAction = removeAction; } public void Remove(){ removeAction(); removeAction = ActionExtensions.EmptyAction; } } static class TimeValidateCoroutineExtensions { public static void StopCoroutine(this TimeValidateCoroutine coroutine){ if(coroutine != null) coroutine.Remove(); } } public class TimeValidator : SingletonMonoBehaviour { [SerializeField] private float boundaryMinutes = 5; private TimeValidateState state = TimeValidateState.Requesting; private List> callbackList = new List>(); private TimeValidationResult result; private Coroutine coroutine; #if !UNITY_EDITOR void Awake(){ coroutine = BeginTimeValidation(); } #endif public TimeValidateCoroutine TimeValidation(Action callback){ switch(state){ case TimeValidateState.Requesting: callbackList.Add(callback); return new TimeValidateCoroutine(() => callbackList.Remove(callback)); default: callback(result); return null; } } void OnApplicationPause(bool pauseStatus){ if(pauseStatus){ this.SafeStopCoroutine(coroutine); state = TimeValidateState.Requesting; }else{ coroutine = BeginTimeValidation(); } } private Coroutine BeginTimeValidation(){ #if DEVELOPMENT_BUILD state = TimeValidateState.Succeeded; result = TimeValidationResult.Success; return null; #else if(Application.internetReachability == NetworkReachability.NotReachable){ state = TimeValidateState.Failed; result = TimeValidationResult.OffLine; return null; } return StartCoroutine(TimeValidation(networkTime => { DateTime now = DateTime.UtcNow; bool isLater = networkTime < now; TimeSpan difference; if(isLater){ difference = now - networkTime; }else{ difference = networkTime - now; } var isSuccess = difference.TotalMinutes <= boundaryMinutes; if(isSuccess){ state = TimeValidateState.Succeeded; result = TimeValidationResult.Success; }else{ state = TimeValidateState.Failed; if(isLater){ result = TimeValidationResult.HardwareTimeShiftLater; }else{ result = TimeValidationResult.HardwareTimeShiftFaster; } } foreach(var callback in callbackList){ callback(result); } callbackList.Clear(); }, () => { state = TimeValidateState.Failed; result = TimeValidationResult.TimeOut; foreach(var callback in callbackList){ callback(result); } callbackList.Clear(); })); #endif } private IEnumerator TimeValidation(Action succeededCallback, Action failedCallback){ var retryCounter = 3; var isTimeOut = true; var timeout = 3000; while(isTimeOut && retryCounter > 0){ yield return new NtpRequest().GetNetworkTime(this, result => { isTimeOut = false; succeededCallback(result); }, ActionExtensions.EmptyAction, timeout); --retryCounter; timeout *= 5; } if(isTimeOut){ failedCallback(); } } }