popcorn/Scripts/Utilities/TimeValidator.cs

138 lines
4.0 KiB
C#

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<TimeValidator> {
[SerializeField]
private float boundaryMinutes = 5;
private TimeValidateState state = TimeValidateState.Requesting;
private List<Action<TimeValidationResult>> callbackList = new List<Action<TimeValidationResult>>();
private TimeValidationResult result;
private Coroutine coroutine;
#if !UNITY_EDITOR
void Awake(){
coroutine = BeginTimeValidation();
}
#endif
public TimeValidateCoroutine TimeValidation(Action<TimeValidationResult> 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<DateTime> 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();
}
}
}