Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions Explorer/Assets/DCL/RealmNavigation/TeleportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ public void StartTeleportToSpawnPoint(SceneEntityDefinition sceneDataSceneEntity

if (sceneDef != null && !TeleportUtils.IsRoad(sceneDef.metadata.OriginalJson.AsSpan()))
{
// Landing on the scene's base parcel has no sub-parcel to target, so take the
// spawn-point path: the landing then matches map/chat teleports to the same scene.
if (landOnParcel && parcel == sceneDef.metadata.scene.DecodedBase)
landOnParcel = false;

// When landing on the exact parcel, keep the requested parcel; otherwise snap to the
// scene base so the spawn point is used.
if (!landOnParcel)
Expand Down
8 changes: 8 additions & 0 deletions Explorer/Assets/DCL/RealmNavigation/Tests.meta

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
@@ -0,0 +1,3 @@
{
"reference": "GUID:da80994a355e49d5b84f91c0a84a721f"
}

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
@@ -0,0 +1,108 @@
using Arch.Core;
using Cysharp.Threading.Tasks;
using DCL.Character.Components;
using DCL.CharacterMotion.Components;
using DCL.Ipfs;
using DCL.Utilities;
using ECS.SceneLifeCycle.Reporting;
using NSubstitute;
using NUnit.Framework;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

namespace DCL.RealmNavigation.Tests
{
public class TeleportControllerShould
{
private World world;
private Entity playerEntity;
private TeleportController controller;
private IRetrieveScene retrieveScene;

[SetUp]
public void SetUp()
{
world = World.Create();
playerEntity = world.Create(new PlayerComponent());

retrieveScene = Substitute.For<IRetrieveScene>();

controller = new TeleportController(Substitute.For<ISceneReadinessReportQueue>())
{
World = world,
SceneProviderStrategy = retrieveScene,
};
}

[TearDown]
public void TearDown() =>
world.Dispose();

// Regression test for #9546: the Discover -> Events page jump-in ("BBQ Sauce Recipe" @ -148,141)
// targets coordinates that ARE the scene's own base parcel, yet EventCardActionsController still
// passes landOnParcel: true. That flag must not survive when there is no sub-parcel to single out,
// otherwise TeleportPositionCalculationSystem takes the raw parcel-center branch instead of the
// authored spawn point ("SpawnArea1") that every other teleport entry point (chat/map) resolves to.
[Test]
public async Task ClearLandOnParcelWhenTargetParcelIsTheSceneBase()
{
var baseParcel = new Vector2Int(-148, 141);

SceneEntityDefinition sceneDef = BuildSceneDefWithDefaultSpawnPoint(baseParcel);

retrieveScene.ByParcelAsync(baseParcel, Arg.Any<CancellationToken>())
.Returns(UniTask.FromResult<SceneEntityDefinition?>(sceneDef));

AsyncLoadProcessReport loadReport = AsyncLoadProcessReport.Create(CancellationToken.None);

await controller.TeleportToSceneSpawnPointAsync(baseParcel, loadReport, CancellationToken.None, landOnParcel: true);

Assert.That(world.Has<PlayerTeleportIntent>(playerEntity), Is.True, "A PlayerTeleportIntent should have been queued on the player entity");

var intent = world.Get<PlayerTeleportIntent>(playerEntity);

// Unpatched: LandOnParcel stays true, so the position system takes the parcel-center path.
// Patched: base-parcel targets have no sub-parcel to single out, so the flag is cleared and
// spawn-point resolution (incl. the spawn's cameraTarget) runs instead - matching map/chat.
Assert.That(intent.LandOnParcel, Is.False,
"landOnParcel must be cleared when the requested parcel equals the scene's base parcel, so spawn-point resolution is used instead of the raw parcel center");
}

private static SceneEntityDefinition BuildSceneDefWithDefaultSpawnPoint(Vector2Int baseParcel)
{
var sceneSection = new SceneMetadataScene
{
DecodedBase = baseParcel,
DecodedParcels = new[] { baseParcel },
};

var spawnPoint = new SceneMetadata.SpawnPoint
{
name = "SpawnArea1",
@default = true,
position = new SceneMetadata.SpawnPoint.Position
{
x = new SceneMetadata.SpawnPoint.Coordinate { MultiValue = new[] { 0f, 3f } },
y = new SceneMetadata.SpawnPoint.Coordinate { SingleValue = 0f },
z = new SceneMetadata.SpawnPoint.Coordinate { MultiValue = new[] { 0f, 3f } },
},
cameraTarget = new SceneMetadata.SpawnPoint.Position
{
x = new SceneMetadata.SpawnPoint.Coordinate { SingleValue = 8f },
y = new SceneMetadata.SpawnPoint.Coordinate { SingleValue = 1f },
z = new SceneMetadata.SpawnPoint.Coordinate { SingleValue = 8f },
},
};

var metadata = new SceneMetadata
{
scene = sceneSection,
spawnPoints = new List<SceneMetadata.SpawnPoint> { spawnPoint },
};

return new SceneEntityDefinition("grill-master-week-2", metadata);
}
}
}

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