Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ public void FinalizeAssetBundleEmoteLoadingCorrectly()
Entity emoteEntity = CreateEmoteEntityWithPromise<AssetBundleData, GetAssetBundleIntention>(mockEmote, intention, bodyShape, out AssetBundlePromise promise);

// Mocking promise result
var assetBundleData = new AssetBundleData(null, null, new []{mockGameObject}, null, null);
var assetBundleData = new AssetBundleData(null, new []{mockGameObject}, null, null);
var promiseResult = new StreamableLoadingResult<AssetBundleData>(assetBundleData);
world.Add(promise.Entity, promiseResult);

Expand Down Expand Up @@ -303,7 +303,7 @@ public void FinalizeAssetBundleEmoteLoadingUnisexCorrectly()
Entity emoteEntity = CreateEmoteEntityWithPromise<AssetBundleData, GetAssetBundleIntention>(mockEmote, intention, loadingBodyShape, out AssetBundlePromise promise);
Entity resultHolderEntity = promise.Entity;

var assetBundleData = new AssetBundleData(null, null, new []{mockGameObject}, null, null);
var assetBundleData = new AssetBundleData(null, new []{mockGameObject}, null, null);
world.Add(resultHolderEntity, new StreamableLoadingResult<AssetBundleData>(assetBundleData));

system.Update(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Arch.Core;
using DCL.SceneRunner.Scene;
using Arch.SystemGroups;
using Arch.SystemGroups.DefaultSystemGroups;
using DCL.AvatarRendering.Emotes;
Expand All @@ -14,6 +15,7 @@
using ECS.SceneLifeCycle.SceneDefinition;
using ECS.SceneLifeCycle.Systems;
using ECS.StreamableLoading.AssetBundles;
using ECS.StreamableLoading.AssetBundles.InitialSceneState;
using ECS.StreamableLoading.AudioClips;
using ECS.StreamableLoading.DeferredLoading;
using ECS.StreamableLoading.GLTF;
Expand Down Expand Up @@ -42,6 +44,7 @@ static GlobalDeferredLoadingSystem()
{
CreateQuery<GetSceneDefinitionList, SceneDefinitions>(),
CreateQuery<GetSceneDefinition, SceneEntityDefinition>(),
CreateQuery<GetISSDescriptorIntention, ISSDescriptorMetadata>(),
CreateQuery<GetSceneFacadeIntention, ISceneFacade>(),
CreateQuery<GetWearableDTOByPointersIntention, WearablesDTOList>(),
CreateQuery<GetTrimmedWearableByParamIntention, IWearable[]>(),
Expand All @@ -59,7 +62,8 @@ static GlobalDeferredLoadingSystem()
COMPONENT_HANDLERS_SCENES = new[]
{
CreateQuery<GetSceneDefinitionList, SceneDefinitions>(),
CreateQuery<GetSceneDefinition, SceneEntityDefinition>()
CreateQuery<GetSceneDefinition, SceneEntityDefinition>(),
CreateQuery<GetISSDescriptorIntention, ISSDescriptorMetadata>(),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using DCL.Ipfs;
using DCL.Multiplayer.Emotes;
using DCL.Multiplayer.Profiles.Bunches;
using DCL.SceneRunner.Scene;
using NUnit.Framework;
using SceneRunner.Scene;
using System;
Expand Down Expand Up @@ -330,7 +329,7 @@ private class MockSceneData : ISceneData
{
public bool SceneLoadingConcluded { get; set; } = true;

public IInitialSceneState InitialSceneStateInfo { get; } = new ISceneData.FakeInitialSceneState();
public DCL.SceneRunner.Scene.ISSDescriptor? ISSDescriptor => DCL.SceneRunner.Scene.ISSDescriptor.NONE;
public SceneShortInfo SceneShortInfo { get; set; } = new (Vector2Int.zero, "mockScene");
public IReadOnlyList<Vector2Int> Parcels { get; set; } = new List<Vector2Int>();
public ISceneContent SceneContent => new SceneNonHashedContent(URLDomain.FromString("file://mock/"));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using Arch.Core;
using Arch.System;
using Arch.SystemGroups;
using CommunicationData.URLHelpers;
using DCL.Character.Components;
using DCL.Ipfs;
using DCL.LOD;
using DCL.LOD.Components;
using DCL.Multiplayer.Connections.DecentralandUrls;
using DCL.Roads.Components;
using DCL.SceneRunner.Scene;
using ECS.Abstract;
using ECS.LifeCycle;
using ECS.LifeCycle.Components;
Expand All @@ -16,6 +15,7 @@
using ECS.SceneLifeCycle.Components;
using ECS.SceneLifeCycle.SceneDefinition;
using ECS.SceneLifeCycle.Systems;
using ECS.StreamableLoading.AssetBundles.InitialSceneState;
using ECS.StreamableLoading.Common;
using SceneRunner.Scene;
using System.Collections.Generic;
Expand Down Expand Up @@ -108,20 +108,20 @@ protected override void Update(float t)
[Query]
[None(typeof(SceneLoadingState), typeof(DeleteEntityIntention), typeof(RoadInfo))]
private void AddNewSceneDefinitionToList(in Entity entity, in PartitionComponent partitionComponent,
in SceneDefinitionComponent sceneDefinitionComponent)
in SceneDefinitionComponent sceneDefinitionComponent, ISSDescriptor issDescriptor)
{
if (sceneDefinitionComponent.IsPortableExperience)
{
//Portable experiences shouldnt be analyzed. Create straight away
World.Add(entity, AssetPromise<ISceneFacade, GetSceneFacadeIntention>.Create(World,
new GetSceneFacadeIntention(sceneDefinitionComponent), partitionComponent), SceneLoadingState.CreatePortableExperience());
new GetSceneFacadeIntention(sceneDefinitionComponent, issDescriptor), partitionComponent), SceneLoadingState.CreatePortableExperience());
}
else
{
var sceneLoadingState = new SceneLoadingState();

//Sizes should always be the same
orderedDataManaged.Add(new OrderedDataManaged(entity, sceneDefinitionComponent, partitionComponent, sceneLoadingState));
orderedDataManaged.Add(new OrderedDataManaged(entity, sceneDefinitionComponent, partitionComponent, sceneLoadingState, issDescriptor));
orderedDataNative.Add(new OrderedDataNative());
arraysInSync = false;
World.Add(entity, sceneLoadingState);
Expand Down Expand Up @@ -242,7 +242,7 @@ private void CreatePromisesFromOrderedData(IIpfsRealm ipfsRealm)
{
//The parcel we are teleporting to should be the first one
OrderedDataManaged data = orderedDataManaged[dataPtr[0].ReferenceListIndex];
UpdateLoadingState(ipfsRealm, data.Entity, data.SceneDefinitionComponent, data.PartitionComponent, data.SceneLoadingState);
UpdateLoadingState(ipfsRealm, data.Entity, data.SceneDefinitionComponent, data.PartitionComponent, data.SceneLoadingState, data.ISSDescriptor);
return;
}

Expand All @@ -253,7 +253,7 @@ private void CreatePromisesFromOrderedData(IIpfsRealm ipfsRealm)
if (dataPtr[i].RawSqrDistance < 0 || dataPtr[i].OutOfRange) continue;

OrderedDataManaged data = orderedDataManaged[dataPtr[i].ReferenceListIndex];
UpdateLoadingState(ipfsRealm, data.Entity, data.SceneDefinitionComponent, data.PartitionComponent, data.SceneLoadingState);
UpdateLoadingState(ipfsRealm, data.Entity, data.SceneDefinitionComponent, data.PartitionComponent, data.SceneLoadingState, data.ISSDescriptor);
}
}

Expand All @@ -279,7 +279,7 @@ private void Unload(in Entity entity, ref SceneLoadingState sceneState)
}

private void UpdateLoadingState(IIpfsRealm ipfsRealm, in Entity entity, in SceneDefinitionComponent sceneDefinitionComponent, in PartitionComponent partitionComponent,
SceneLoadingState sceneState)
SceneLoadingState sceneState, ISSDescriptor issDescriptor)
{
VisualSceneState candidateBy
= visualSceneStateResolver.ResolveVisualSceneState(partitionComponent, sceneDefinitionComponent, sceneState.VisualSceneState, ipfsRealm.SceneUrns.Count > 0);
Expand Down Expand Up @@ -322,6 +322,21 @@ VisualSceneState candidateBy
|| sceneState.VisualSceneState == candidateBy)
return;

// ISS descriptor gate: SHOWING_LOD / SHOWING_SCENE both need the descriptor to be resolved
// before consumers downstream (UpdateSceneLODInfoSystem reads it, GetSceneFacadeIntention
// captures it). If still in Uninitialized state, attach the resolver promise to the entity
// and bail; the next tick re-enters and re-checks. ResolveISSDescriptorSystem consumes the
// promise, mutates the same ISSDescriptor instance in place via MarkResolved, and removes
// the promise component. Because issDescriptor is a class reference cached in
// OrderedDataManaged, the gate sees the resolved state on the next tick without a refetch.
if (issDescriptor.CurrentState == ISSDescriptorState.Uninitialized)
{
if (!World.Has<AssetPromise<ISSDescriptorMetadata, GetISSDescriptorIntention>>(entity))
World.Add(entity, AssetPromise<ISSDescriptorMetadata, GetISSDescriptorIntention>.Create(
World, GetISSDescriptorIntention.For(sceneDefinitionComponent.Definition), partitionComponent));
return;
}

promisesCreated++;
sceneState.PromiseCreated = true;
sceneState.VisualSceneState = candidateBy;
Expand All @@ -339,7 +354,7 @@ VisualSceneState candidateBy
//Therefore, we need to make this check because we dont want to break the entity mutual exclusive state
if (!World.Has<ISceneFacade>(entity))
World.Add(entity, AssetPromise<ISceneFacade, GetSceneFacadeIntention>.Create(World,
new GetSceneFacadeIntention(sceneDefinitionComponent), partitionComponent));
new GetSceneFacadeIntention(sceneDefinitionComponent, issDescriptor), partitionComponent));
break;
}
}
Expand Down Expand Up @@ -392,16 +407,20 @@ public class OrderedDataManaged
public readonly SceneDefinitionComponent SceneDefinitionComponent;
public readonly SceneLoadingState SceneLoadingState;
public readonly PartitionComponent PartitionComponent;
// Class-typed component: same reference is mutated in place by ResolveISSDescriptorSystem,
// so the gate below sees live state without a World.Get refresh.
public readonly ISSDescriptor ISSDescriptor;

public int XCoordinate;


public OrderedDataManaged(Entity entity, SceneDefinitionComponent sceneDefinitionComponent, PartitionComponent partitionComponent, SceneLoadingState sceneLoadingState)
public OrderedDataManaged(Entity entity, SceneDefinitionComponent sceneDefinitionComponent, PartitionComponent partitionComponent, SceneLoadingState sceneLoadingState, ISSDescriptor issDescriptor)
{
Entity = entity;
SceneDefinitionComponent = sceneDefinitionComponent;
SceneLoadingState = sceneLoadingState;
PartitionComponent = partitionComponent;
ISSDescriptor = issDescriptor;
XCoordinate = sceneDefinitionComponent.Definition.metadata.scene.DecodedBase.x;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using DCL.Ipfs;
using ECS.SceneLifeCycle.IncreasingRadius;
using ECS.StreamableLoading.AssetBundles;
using ECS.StreamableLoading.AssetBundles.InitialSceneState;
using ECS.StreamableLoading.Common;
using Org.BouncyCastle.Utilities.Collections;
using System;
using System.Collections.Generic;
Expand All @@ -27,7 +29,6 @@ public struct SceneDefinitionComponent

public int InternalJobIndex { get; set; }


public float EstimatedMemoryUsageInMB;
public float EstimatedMemoryUsageForLODMB;
public float EstimatedMemoryUsageForQualityReductedLODMB;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Arch.Core;
using Arch.Core;
using Arch.SystemGroups;
using Arch.SystemGroups.DefaultSystemGroups;
using CommunicationData.URLHelpers;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Arch.Core;
using Arch.System;
using Arch.SystemGroups;
using DCL.Diagnostics;
using DCL.SceneRunner.Scene;
using ECS.Abstract;
using ECS.LifeCycle.Components;
using ECS.SceneLifeCycle;
using ECS.StreamableLoading.AssetBundles.InitialSceneState;
using ECS.StreamableLoading.Common;
using ECS.StreamableLoading.Common.Components;

namespace ECS.SceneLifeCycle.SceneDefinition
{
/// <summary>
/// Consumes the transient ISS descriptor promise that
/// <c>ResolveSceneStateByIncreasingRadiusSystem</c> attaches to a scene entity while its descriptor
/// is being resolved. On consume, mutates the entity's <see cref="ISSDescriptor"/> in place via
/// <see cref="ISSDescriptor.MarkResolved"/> and removes the promise component. The same descriptor
/// instance persists for the scene's lifetime, so cached class references elsewhere (e.g.
/// <c>OrderedDataManaged.ISSDescriptor</c>) see the resolved state without a refetch.
/// </summary>
[UpdateInGroup(typeof(RealmGroup))]
[LogCategory(ReportCategory.SCENE_LOADING)]
public partial class ResolveISSDescriptorSystem : BaseUnityLoopSystem
{
internal ResolveISSDescriptorSystem(World world) : base(world) { }

protected override void Update(float t)
{
ConsumePromiseQuery(World);
}

[Query]
[None(typeof(DeleteEntityIntention))]
private void ConsumePromise(in Entity entity, SceneDefinitionComponent sceneDefinitionComponent, ISSDescriptor descriptor, ref AssetPromise<ISSDescriptorMetadata, GetISSDescriptorIntention> promise)
{
if (!promise.TryConsume(World, out StreamableLoadingResult<ISSDescriptorMetadata> result))
return;

// The loader returns a failed result for both "no descriptor JSON" and "manifest predates ISS",
// so any failure here means this scene has no ISS and we fall back to the legacy LOD path.
if (result is { Succeeded: true, Asset: { } resolved })
descriptor.MarkResolved(resolved.assets);
else
{
ReportHub.Log(GetReportCategory(), $"ISSDescriptor is unavailable for scene {sceneDefinitionComponent.Definition.id} ({result.Exception?.Message ?? "unknown reason"}), fallback LOD will be used");
descriptor.MarkAsNone();
}

World.Remove<AssetPromise<ISSDescriptorMetadata, GetISSDescriptorIntention>>(entity);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using CommunicationData.URLHelpers;
using DCL.Ipfs;
using DCL.SceneRunner.Scene;
using ECS.SceneLifeCycle.SceneDefinition;
using ECS.StreamableLoading.AssetBundles.InitialSceneState;
using ECS.StreamableLoading.Common.Components;
using Ipfs;
using System;
Expand All @@ -19,9 +21,17 @@ public struct GetSceneFacadeIntention : ILoadingIntention, IEquatable<GetSceneFa

public readonly SceneDefinitionComponent DefinitionComponent;

public GetSceneFacadeIntention(SceneDefinitionComponent definitionComponent)
/// <summary>
/// ISSDescriptor for this scene, captured by reference at intention-creation time. The radius
/// system gates SHOWING_SCENE transitions on descriptor resolution, so by the time this intention
/// is constructed the descriptor is guaranteed to be in a resolved state.
/// </summary>
public readonly ISSDescriptor ISSDescriptor;

public GetSceneFacadeIntention(SceneDefinitionComponent definitionComponent, ISSDescriptor issDescriptor)
{
DefinitionComponent = definitionComponent;
ISSDescriptor = issDescriptor;

// URL = EntityId just for identification, it is used by LoadSystemBase, it won't be used as a URL
CommonArguments = new CommonLoadingArguments(definitionComponent.IpfsPath.EntityId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
using Arch.Core;
using DCL.SceneRunner.Scene;
using ECS.Prioritization.Components;
using ECS.SceneLifeCycle.Components;
using ECS.SceneLifeCycle.SceneDefinition;
using ECS.StreamableLoading.AssetBundles.InitialSceneState;
using ECS.StreamableLoading.Common;
using SceneRunner.Scene;

namespace ECS.SceneLifeCycle.SceneFacade
{
public static class CreateSceneFacadePromise
{
public static void Execute(World world, Entity entity, in SceneDefinitionComponent definitionComponent, IPartitionComponent partitionComponent)
public static void Execute(World world, Entity entity, in SceneDefinitionComponent definitionComponent, ISSDescriptor issDescriptor, IPartitionComponent partitionComponent)
{
world.Add(entity,
AssetPromise<ISceneFacade, GetSceneFacadeIntention>.Create(world,
new GetSceneFacadeIntention(definitionComponent), partitionComponent));
new GetSceneFacadeIntention(definitionComponent, issDescriptor), partitionComponent));
}
}
}
Loading
Loading