-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGameBootStrapper.cs
More file actions
99 lines (85 loc) · 3.91 KB
/
Copy pathGameBootStrapper.cs
File metadata and controls
99 lines (85 loc) · 3.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace GameBootStrapper.Unity.Runtime
{
public static class BootStrapRunner
{
public static async Task<BootStrapResult> RunTasks(Action<int> progress, BootStrapContext ctx,
params Func<BootStrapContext, BootStrapResult>[] tasks)
{
ctx.totalTaskCount = tasks.Length;
var parallelTasks = new List<(Task<BootStrapResult> Task, bool Suppress)>();
foreach (var fn in tasks)
{
var meta = fn.Method.GetCustomAttribute<BootStepAttribute>();
if(meta == null)
continue;
switch (meta.TaskType)
{
case BootStrapTaskType.Sequential:
var result = await ExecuteTaskAsync(fn, ctx, progress, meta);
if (!result.Success && !meta.SuppressError)
return result;
break;
case BootStrapTaskType.Parallel:
parallelTasks.Add((ExecuteTaskAsync(fn, ctx, progress, meta), meta.SuppressError));
break;
case BootStrapTaskType.Forget:
_ = ExecuteTaskAsync(fn, ctx, progress, meta);
break;
}
}
if (!parallelTasks.Any())
return new BootStrapResult() { Success = true };
var parallelTaskResults = await Task.WhenAll(parallelTasks.Select(p => p.Task));
var zipped = parallelTaskResults.Zip(parallelTasks, (result, info) => new { result, info.Suppress });
var failedTaskResult = zipped.FirstOrDefault(p => !p.result.Success && !p.Suppress)?.result;
return failedTaskResult ?? new BootStrapResult { Success = true };
}
private static async Task<BootStrapResult> ExecuteTaskAsync(Func<BootStrapContext, BootStrapResult> task,
BootStrapContext ctx, Action<int> progress, BootStepAttribute meta)
{
try
{
var result = await Task.Run(() => task(ctx), ctx.ct)
.TimeOut(meta.Timeout, ctx.ct);
ctx.IncrementCompletedTaskCount();
progress?.Invoke(CalculateProgressPercentage(ctx));
Debug.Log($"Step {task.Method.Name} completed with result: {result.Success}");
if (!result.Success && !meta.SuppressError)
ctx.cancellationTokenSource.Cancel();
return result;
}
catch (Exception ex)
{
if(!ctx.ct.IsCancellationRequested)
ctx.cancellationTokenSource.Cancel();
Debug.LogWarning($"Step {task.Method.Name} failed with error: {ex.Message}");
return new BootStrapResult { Success = false, Message = ex.Message };
}
}
private static int CalculateProgressPercentage(BootStrapContext ctx)
{
return (int)((float)ctx.completedTaskCount / ctx.totalTaskCount * 100);
}
private static async Task<TResult> TimeOut<TResult>(this Task<TResult> task, TimeSpan timeout, CancellationToken cancellationToken)
{
var timeoutTask = Task.Delay(timeout, cancellationToken);
var completedTask = await Task.WhenAny(task, timeoutTask);
if (completedTask == timeoutTask)
{
if (cancellationToken.IsCancellationRequested)
{
throw new OperationCanceledException(cancellationToken);
}
throw new TimeoutException("The operation has timed out.");
}
return await task;
}
}
}