1414using DCL . Utility . Types ;
1515using ECS ;
1616using ECS . Prioritization . Components ;
17+ using ECS . SceneLifeCycle ;
1718using ECS . SceneLifeCycle . Realm ;
1819using Newtonsoft . Json . Linq ;
20+ using SceneRunner . Scene ;
1921using System ;
2022using System . Threading ;
2123using UnityEngine ;
@@ -47,6 +49,7 @@ public class RealmNavigator : IRealmNavigator
4749 private readonly IAnalyticsController analyticsController ;
4850 private readonly ILandscape landscape ;
4951 private readonly IWorldAccessGate worldAccessGate ;
52+ private readonly IScenesCache scenesCache ;
5053
5154 public RealmNavigator (
5255 ILoadingScreen loadingScreen ,
@@ -60,7 +63,8 @@ public RealmNavigator(
6063 IAnalyticsController analyticsController ,
6164 SequentialLoadingOperation < TeleportParams > realmChangeOperations ,
6265 SequentialLoadingOperation < TeleportParams > teleportInSameRealmOperation ,
63- IWorldAccessGate worldAccessGate )
66+ IWorldAccessGate worldAccessGate ,
67+ IScenesCache scenesCache )
6468 {
6569 this . loadingScreen = loadingScreen ;
6670 this . realmController = realmController ;
@@ -74,6 +78,7 @@ public RealmNavigator(
7478 this . teleportInSameRealmOperation = teleportInSameRealmOperation ;
7579 this . landscape = landscape ;
7680 this . worldAccessGate = worldAccessGate ;
81+ this . scenesCache = scenesCache ;
7782 }
7883
7984 public bool IsAlreadyOnRealm ( URLDomain realm )
@@ -195,9 +200,14 @@ private async UniTask<EnumResult<TaskError>> ExecuteTeleportOperationsAsync(
195200 SequentialLoadingOperation < TeleportParams > ops ,
196201 string logOpName ,
197202 int attemptsCount ,
203+ bool trackDuration ,
198204 CancellationToken ct
199205 )
200206 {
207+ // Snapshot the source before ops run: realm-change ops clear the scenes cache
208+ string source = SceneHashOrParcel ( scenesCache . CurrentScene . Value , scenesCache . CurrentParcel . Value ) ;
209+ float startTime = UnityEngine . Time . realtimeSinceStartup ;
210+
201211 ReportHub . LogProductionInfo ( $ "Trying to teleport to { teleportParams . CurrentDestinationParcel } . Attempt #{ attemptsCount } ") ;
202212 EnumResult < TaskError > lastOpResult = await ops . ExecuteAsync ( logOpName , attemptsCount , teleportParams , ct ) ;
203213
@@ -220,11 +230,31 @@ CancellationToken ct
220230 ) ;
221231 }
222232 else
233+ {
234+ if ( trackDuration )
235+ {
236+ float loadingTime = UnityEngine . Time . realtimeSinceStartup - startTime ;
237+
238+ // The destination scene is already cached: teleport ops wait for scene readiness before completing
239+ scenesCache . TryGetByParcel ( teleportParams . CurrentDestinationParcel , out ISceneFacade destinationScene ) ;
240+
241+ analyticsController . Track ( AnalyticsEvents . General . TELEPORT_OPERATION_DURATION , new JObject
242+ {
243+ [ "source" ] = source ,
244+ [ "destination" ] = SceneHashOrParcel ( destinationScene , teleportParams . CurrentDestinationParcel ) ,
245+ [ "loading_time" ] = loadingTime ,
246+ } ) ;
247+ }
248+
223249 NavigationExecuted ? . Invoke ( teleportParams . CurrentDestinationParcel ) ;
250+ }
224251
225252 return lastOpResult ;
226253 }
227254
255+ private static string SceneHashOrParcel ( ISceneFacade ? scene , Vector2Int parcel ) =>
256+ scene is { IsEmpty : false } ? scene . Info . Name : parcel . ToString ( ) ;
257+
228258 private Func < AsyncLoadProcessReport , CancellationToken , UniTask < EnumResult < TaskError > > > DoChangeRealmAsync ( URLDomain realm , URLDomain ? fallbackRealm , Vector2Int parcelToTeleport , bool allowsWorldPositionOverride , bool landOnParcel = false , string ? spawnPointName = null )
229259 {
230260 return async ( parentLoadReport , ct ) =>
@@ -237,7 +267,7 @@ private Func<AsyncLoadProcessReport, CancellationToken, UniTask<EnumResult<TaskE
237267
238268 var teleportParams = new TeleportParams ( realm , parcelToTeleport , parentLoadReport , loadingStatus , allowsWorldPositionOverride , landOnParcel , spawnPointName ) ;
239269
240- EnumResult < TaskError > opResult = await ExecuteTeleportOperationsAsync ( teleportParams , realmChangeOperations , LOG_NAME , MAX_REALM_CHANGE_RETRIES , ct ) ;
270+ EnumResult < TaskError > opResult = await ExecuteTeleportOperationsAsync ( teleportParams , realmChangeOperations , LOG_NAME , MAX_REALM_CHANGE_RETRIES , trackDuration : true , ct ) ;
241271
242272 if ( opResult . Success )
243273 return opResult ;
@@ -253,7 +283,8 @@ private Func<AsyncLoadProcessReport, CancellationToken, UniTask<EnumResult<TaskE
253283
254284 teleportParams . ChangeDestination ( fallbackRealm . Value , currentParcel ) ;
255285
256- opResult = await ExecuteTeleportOperationsAsync ( teleportParams , realmChangeOperations , FALLBACK_LOG_NAME , 1 , ct ) ;
286+ // The recovery trip back is not a user-intended teleport: keep it out of the duration metric
287+ opResult = await ExecuteTeleportOperationsAsync ( teleportParams , realmChangeOperations , FALLBACK_LOG_NAME , 1 , trackDuration : false , ct ) ;
257288
258289 if ( ! opResult . Success )
259290 parentLoadReport . SetProgress ( 1 ) ;
@@ -331,7 +362,7 @@ private Func<AsyncLoadProcessReport, CancellationToken, UniTask<EnumResult<TaskE
331362 spawnPointName : spawnPointName
332363 ) ;
333364
334- EnumResult < TaskError > result = await ExecuteTeleportOperationsAsync ( teleportParams , teleportInSameRealmOperation , LOG_NAME , 1 , ct ) ;
365+ EnumResult < TaskError > result = await ExecuteTeleportOperationsAsync ( teleportParams , teleportInSameRealmOperation , LOG_NAME , 1 , trackDuration : true , ct ) ;
335366 return result ;
336367 } ;
337368 }
0 commit comments