| | 1 | | using Cysharp.Threading.Tasks; |
| | 2 | | using DCL; |
| | 3 | | using DCLServices.MapRendererV2.CoordsUtils; |
| | 4 | | using DCLServices.MapRendererV2.Culling; |
| | 5 | | using DCLServices.MapRendererV2.MapCameraController; |
| | 6 | | using MainScripts.DCL.Controllers.HotScenes; |
| | 7 | | using System; |
| | 8 | | using System.Collections.Generic; |
| | 9 | | using System.Runtime.CompilerServices; |
| | 10 | | using System.Threading; |
| | 11 | | using UnityEngine; |
| | 12 | | using Random = UnityEngine.Random; |
| | 13 | |
|
| | 14 | | namespace DCLServices.MapRendererV2.MapLayers.UsersMarkers.ColdArea |
| | 15 | | { |
| | 16 | | /// <summary> |
| | 17 | | /// This controller is responsible for updating User Markers outside of the comms area. |
| | 18 | | /// Whereas the comms area is the region around the player where avatars (and therefore the users) are fetched accor |
| | 19 | | /// </summary> |
| | 20 | | internal partial class UsersMarkersColdAreaController : MapLayerControllerBase, IMapLayerController, IMapCullingList |
| | 21 | | { |
| | 22 | | internal delegate IColdUserMarker ColdUserMarkerBuilder( |
| | 23 | | ColdUserMarkerObject prefab, |
| | 24 | | Transform parent); |
| | 25 | |
|
| | 26 | | internal const int CREATE_PER_BATCH = 20; |
| | 27 | | internal const int COMMS_RADIUS_THRESHOLD = 2; |
| | 28 | |
|
| | 29 | | private IHotScenesFetcher hotScenesFetcher; |
| | 30 | |
|
| | 31 | | private readonly ColdUserMarkerObject prefab; |
| | 32 | | private readonly int maxMarkers; |
| | 33 | | private readonly ColdUserMarkerBuilder builder; |
| | 34 | | private readonly BaseVariable<string> realmName; |
| | 35 | | private readonly Vector2IntVariable userPosition; |
| | 36 | |
|
| | 37 | | private ExclusionAreaProvider exclusionArea; |
| | 38 | | private ColdUserMarkersStorage storage; |
| | 39 | |
|
| | 40 | | private CancellationTokenSource cancellationTokenSource; |
| | 41 | | IColdUserMarker[] instances; |
| | 42 | |
|
| | 43 | | public UsersMarkersColdAreaController(Transform parent, ColdUserMarkerObject prefab, ColdUserMarkerBuilder build |
| | 44 | | IHotScenesFetcher hotScenesFetcher, BaseVariable<string> realmName, Vector2IntVariable userPosition, |
| | 45 | | KernelConfig kernelConfig, ICoordsUtils coordsUtils, IMapCullingController cullingController, int maxMarkers |
| 96 | 46 | | : base(parent, coordsUtils, cullingController) |
| | 47 | | { |
| 96 | 48 | | this.prefab = prefab; |
| 96 | 49 | | this.maxMarkers = maxMarkers; |
| 96 | 50 | | this.builder = builder; |
| 96 | 51 | | this.realmName = realmName; |
| 96 | 52 | | this.userPosition = userPosition; |
| 96 | 53 | | this.hotScenesFetcher = hotScenesFetcher; |
| | 54 | |
|
| 96 | 55 | | exclusionArea = new ExclusionAreaProvider(kernelConfig, COMMS_RADIUS_THRESHOLD); |
| 96 | 56 | | } |
| | 57 | |
|
| | 58 | | public async UniTask Initialize(CancellationToken cancellationToken) |
| | 59 | | { |
| 96 | 60 | | cancellationTokenSource = LinkWithDisposeToken(cancellationToken); |
| 96 | 61 | | cancellationToken = cancellationTokenSource.Token; |
| 96 | 62 | | instances = new IColdUserMarker[maxMarkers]; |
| | 63 | | async UniTask InstantiateMarkers(CancellationToken ct) |
| | 64 | | { |
| 614 | 65 | | for (var i = 0; i < maxMarkers;) |
| | 66 | | { |
| 449 | 67 | | var batchSize = Mathf.Min(maxMarkers - i, CREATE_PER_BATCH); |
| | 68 | |
|
| 18858 | 69 | | for (var j = 0; j < batchSize; j++) |
| | 70 | | { |
| 8980 | 71 | | var marker = builder(prefab, instantiationParent); |
| 8980 | 72 | | marker.SetActive(false); |
| 8980 | 73 | | instances[i] = marker; |
| 8980 | 74 | | i++; |
| | 75 | | } |
| | 76 | |
|
| | 77 | | // NextFrame does not work in Tests (see the implementation, there is a special case if Application |
| 1347 | 78 | | await UniTask.DelayFrame(1, cancellationToken: ct); |
| | 79 | | } |
| | 80 | |
|
| 69 | 81 | | storage = new ColdUserMarkersStorage(instances, SetData); |
| 69 | 82 | | } |
| | 83 | |
|
| 288 | 84 | | await UniTask.WhenAll(InstantiateMarkers(cancellationToken), exclusionArea.Initialize(cancellationToken)); |
| | 85 | |
|
| 6 | 86 | | realmName.OnChange += OnRealmNameChange; |
| 6 | 87 | | userPosition.OnChange += OnUserPositionChange; |
| 6 | 88 | | } |
| | 89 | |
|
| | 90 | | private void SetData(IColdUserMarker marker, (string realmServer, Vector2Int coords) data) |
| | 91 | | { |
| 0 | 92 | | var randomizedCoords = new Vector2Int( |
| | 93 | | RandomizeCoord(data.coords.x), |
| | 94 | | RandomizeCoord(data.coords.y)); |
| | 95 | |
|
| 0 | 96 | | var position = coordsUtils.CoordsToPosition(randomizedCoords, marker); |
| 0 | 97 | | marker.SetData(data.realmServer, realmName.Get(), data.coords, position); |
| 0 | 98 | | ResolveVisibility(marker); |
| 0 | 99 | | } |
| | 100 | |
|
| | 101 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 102 | | private static int RandomizeCoord(int coord) => |
| 0 | 103 | | Mathf.RoundToInt(coord + Random.Range(-0.5f, 0.5f)); |
| | 104 | |
|
| | 105 | | private void ResolveVisibility(IColdUserMarker marker) |
| | 106 | | { |
| 0 | 107 | | marker.SetActive(!exclusionArea.Contains(marker.Coords)); |
| 0 | 108 | | } |
| | 109 | |
|
| | 110 | | protected override void DisposeImpl() |
| | 111 | | { |
| 6 | 112 | | realmName.OnChange -= OnRealmNameChange; |
| 6 | 113 | | userPosition.OnChange -= OnUserPositionChange; |
| 6 | 114 | | storage.Dispose(); |
| 6 | 115 | | } |
| | 116 | |
|
| | 117 | | private async UniTaskVoid ColdAreasUpdateLoop(CancellationToken cancellationToken) |
| | 118 | | { |
| 0 | 119 | | await foreach (var data in hotScenesFetcher.ScenesInfo) |
| | 120 | | { |
| 0 | 121 | | if (cancellationToken.IsCancellationRequested) |
| | 122 | | return; |
| | 123 | |
|
| 0 | 124 | | RenewSceneInfos(data); |
| | 125 | | } |
| 0 | 126 | | } |
| | 127 | |
|
| | 128 | | private void RenewSceneInfos(IReadOnlyList<IHotScenesController.HotSceneInfo> sceneInfos) |
| | 129 | | { |
| | 130 | | // it is forbidden to declare Spans variables in the async flow so we have to have a separate sync method fo |
| 0 | 131 | | storage.Update(sceneInfos, out var newRents, out var recycledRents); |
| | 132 | |
|
| 0 | 133 | | foreach (var marker in recycledRents) |
| 0 | 134 | | mapCullingController.StopTracking(marker); |
| | 135 | |
|
| 0 | 136 | | foreach (var marker in newRents) |
| | 137 | | { |
| 0 | 138 | | mapCullingController.StartTracking(marker, this); |
| 0 | 139 | | mapCullingController.SetTrackedObjectPositionDirty(marker); |
| | 140 | | } |
| 0 | 141 | | } |
| | 142 | |
|
| | 143 | | private void OnRealmNameChange(string current, string previous) |
| | 144 | | { |
| 0 | 145 | | if (!string.IsNullOrEmpty(current)) |
| 0 | 146 | | ResolveRealm(current); |
| 0 | 147 | | } |
| | 148 | |
|
| | 149 | | private void ResolveRealm(string realmName) |
| | 150 | | { |
| 0 | 151 | | foreach (var marker in storage.Markers) |
| 0 | 152 | | marker.OnRealmChanged(realmName); |
| 0 | 153 | | } |
| | 154 | |
|
| | 155 | | private void OnUserPositionChange(Vector2Int current, Vector2Int previous) |
| | 156 | | { |
| 0 | 157 | | exclusionArea.SetExclusionAreaCenter(current); |
| | 158 | |
|
| 0 | 159 | | foreach (IColdUserMarker userMarker in storage.Markers) |
| 0 | 160 | | ResolveVisibility(userMarker); |
| 0 | 161 | | } |
| | 162 | |
|
| | 163 | | public void OnMapObjectBecameVisible(IColdUserMarker marker) |
| | 164 | | { |
| 0 | 165 | | marker.SetCulled(false); |
| 0 | 166 | | } |
| | 167 | |
|
| | 168 | | public void OnMapObjectCulled(IColdUserMarker marker) |
| | 169 | | { |
| 0 | 170 | | marker.SetCulled(true); |
| 0 | 171 | | } |
| | 172 | |
|
| | 173 | | public UniTask Enable(CancellationToken cancellationToken) |
| | 174 | | { |
| 0 | 175 | | hotScenesFetcher.SetUpdateMode(IHotScenesFetcher.UpdateMode.FOREGROUND); |
| 0 | 176 | | ColdAreasUpdateLoop(LinkWithDisposeToken(cancellationToken).Token).Forget(); |
| 0 | 177 | | foreach (IColdUserMarker coldUserMarker in instances) |
| | 178 | | { |
| 0 | 179 | | coldUserMarker.SetActive(true); |
| | 180 | | } |
| 0 | 181 | | return UniTask.CompletedTask; |
| | 182 | | } |
| | 183 | |
|
| | 184 | | public UniTask Disable(CancellationToken cancellationToken) |
| | 185 | | { |
| 88 | 186 | | hotScenesFetcher.SetUpdateMode(IHotScenesFetcher.UpdateMode.BACKGROUND); |
| 17776 | 187 | | foreach (IColdUserMarker coldUserMarker in instances) |
| | 188 | | { |
| 8800 | 189 | | if(coldUserMarker != null) |
| 8740 | 190 | | coldUserMarker.SetActive(false); |
| | 191 | | } |
| | 192 | | // cancellation of `ColdAreasUpdateLoop` is handled by the cancellation token |
| 88 | 193 | | return UniTask.CompletedTask; |
| | 194 | | } |
| | 195 | | } |
| | 196 | | } |