using System; using System.Collections; using UnityEngine; namespace Unity.Services.Lobbies.Helpers { internal static class AsyncOpRetry { public static AsyncOpRetry FromCreateAsync(Func op) { return AsyncOpRetry.FromCreateAsync(op); } } internal class AsyncOpRetry { private uint MaxRetries { get; set; } = 4; private float JitterMagnitude { get; set; } = 1.0f; private float DelayScale { get; set; } = 1.0f; private float MaxDelayTime { get; set; } = 8.0f; private Func CreateOperation { get; set; } private Func RetryCondition { get; set; } private Action OnComplete { get; set; } private AsyncOpRetry(Func createAsyncOp) { CreateOperation = createAsyncOp; } private static float AddJitter(float number, float magnitude) { return number + (UnityEngine.Random.value * magnitude); } private static float Pow2(float exponent, float scale) { return (float) (Math.Pow(2.0f, exponent) * scale); } private static float CalculateDelay(int attemptNumber, float maxDelayTime, float delayScale, float jitterMagnitude) { float delayTime = Pow2(attemptNumber, delayScale); delayTime = AddJitter(delayTime, jitterMagnitude); delayTime = Math.Min(delayTime, maxDelayTime); return delayTime; } public AsyncOpRetry WithJitterMagnitude(float magnitude) { JitterMagnitude = Mathf.Clamp(magnitude, 0.001f, 1.0f); return this; } public AsyncOpRetry WithDelayScale(float scale) { DelayScale = Mathf.Clamp(scale, 0.05f, 1.0f); return this; } public AsyncOpRetry WithMaxDelayTime(float time) { MaxDelayTime = Mathf.Clamp(time, 0.1f, 60.0f); return this; } public static AsyncOpRetry FromCreateAsync(Func op) { return new AsyncOpRetry(op); } public AsyncOpRetry WithRetryCondition(Func shouldRetry) { RetryCondition = shouldRetry; return this; } public AsyncOpRetry WhenComplete(Action onComplete) { OnComplete = onComplete; return this; } public AsyncOpRetry UptoMaximumRetries(uint amount) { MaxRetries = amount; return this; } public IEnumerator Run() { T asyncOp = default; for (var attempt = 0; attempt <= MaxRetries; ++attempt) { asyncOp = CreateOperation(attempt + 1); yield return asyncOp; if ((!RetryCondition?.Invoke(asyncOp) ?? false)) { break; } var delayTime = CalculateDelay(attempt, MaxDelayTime, DelayScale, JitterMagnitude); yield return new WaitForSecondsRealtime(delayTime); } OnComplete?.Invoke(asyncOp); } } }