-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInitializationLoader.cs
More file actions
146 lines (113 loc) · 5.86 KB
/
Copy pathInitializationLoader.cs
File metadata and controls
146 lines (113 loc) · 5.86 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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.SceneManagement;
using Xprees.Events.ScriptableObjects.Base;
using Xprees.SceneManagement.Extensions;
using Xprees.SceneManagement.Initialization.InitializationHandlers;
using Xprees.SceneManagement.ScriptableObjects;
namespace Xprees.SceneManagement.Initialization
{
public class InitializationLoader : MonoBehaviour
{
// Warning: References must be Addressables to avoid duplication in build,
// otherwise assets copies will be included one in player build and one in addressables build
[Header("Scene References")]
[Tooltip("Asset reference to the persistent managers sceneData (SceneSO)")]
[SerializeField] private AssetReferenceT<SceneSO> managersSceneDataReference;
[Header("Listening to")]
[SerializeField] private AssetReferenceT<VoidEventChannelSO> startHandlersInitializationEventRef;
[Header("Additional Initialization Handlers")]
[SerializeField] private List<AbstractInitializationHandlerSO> initializationHandlers;
private IEnumerable<AbstractInitializationHandlerSO> ActiveHandlers => initializationHandlers.Where(handler => handler.IsActive);
private VoidEventChannelSO _startHandlersInitializationEvent;
private async void Awake()
{
var token = this.GetCancellationTokenOnDestroy();
// Managers scene must be loaded first and in Awake
// to have all PersistentManagers ready for calls from the other scripts on Start
await LoadManagersScene(token)
.SuppressCancellationThrow();
CheckActiveHandlers();
}
private void OnDisable()
{
_startHandlersInitializationEvent.onEventRaised -= StartHandlersInitialization;
}
private async void StartHandlersInitialization()
{
var token = this.GetCancellationTokenOnDestroy();
await InitializeHandlers(token);
await TriggerInitHandlers(token);
await UnloadHandlers(token);
UnloadInitializationScene().Forget(); // Will also unload this script we don't need to wait for that :-)
}
private async UniTask InitializeHandlers(CancellationToken cancellationToken)
{
var initTasks = initializationHandlers // Initialize all handlers - some need to initialize to decide if they are active
.Select(handler => handler.InitializeHandlerAsync(cancellationToken))
.ToList();
await UniTask.WhenAll(initTasks).SuppressCancellationThrow();
}
private async UniTask TriggerInitHandlers(CancellationToken cancellationToken)
{
var triggerTasks = ActiveHandlers
.Select(handler => handler.TriggerInitializationAsync(cancellationToken))
.ToList();
await UniTask.WhenAll(triggerTasks).SuppressCancellationThrow();
}
private async UniTask UnloadHandlers(CancellationToken cancellationToken)
{
var unloadTasks = initializationHandlers
.Select(handler => handler.UnloadHandlerAsync(cancellationToken))
.ToList();
await UniTask.WhenAll(unloadTasks).SuppressCancellationThrow();
}
private async UniTask LoadManagersScene(CancellationToken cancellationToken)
{
_startHandlersInitializationEvent = await startHandlersInitializationEventRef.LoadAssetAsync<VoidEventChannelSO>()
.ToUniTask(cancellationToken: cancellationToken);
_startHandlersInitializationEvent.onEventRaised += StartHandlersInitialization;
var managersScene = await managersSceneDataReference.LoadAssetAsync<SceneSO>()
.ToUniTask(cancellationToken: cancellationToken);
// Set as active to make sure if there are any created objects they are in the right scene and initialization scene
await LoadSceneIfNotProcessed(managersScene, true, LoadSceneMode.Additive, cancellationToken);
await UniTask.WaitUntil(IsManagersSceneReady, cancellationToken: cancellationToken);
return;
bool IsManagersSceneReady() => managersScene.sceneInstance.HasValue && managersScene.IsLoaded;
}
private UniTask UnloadInitializationScene() => SceneManager.UnloadSceneAsync(0).ToUniTask(); // only scene in build settings
private void CheckActiveHandlers()
{
var disabledHandlers = initializationHandlers.Where(h => !h.IsActive).ToList();
foreach (var disabledHandler in disabledHandlers)
{
var disabledHandlerName = disabledHandler ? disabledHandler.name : "Already destroyed handler";
Debug.LogWarning($"Initialization handler {disabledHandlerName} is disabled and will be skipped.");
}
if (!ActiveHandlers.Any())
{
Debug.LogWarning($"{nameof(InitializationLoader)} has {initializationHandlers.Count} initialization handlers. " +
$"Only {ActiveHandlers.Count()} are active.");
}
}
private async UniTask LoadSceneIfNotProcessed(
SceneSO scene,
bool setActive = false,
LoadSceneMode loadMode = LoadSceneMode.Additive,
CancellationToken token = default
)
{
if (scene.IsBeingProcessed || scene.IsLoaded) return;
scene.SetAsProcessed(true);
var sceneInstance = await scene.sceneReference
.LoadSceneAsync(loadMode, true)
.ToUniTask(cancellationToken: token);
if (setActive) sceneInstance.SetAsActiveScene();
scene.SetAsProcessed(false);
}
}
}