Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,11 @@ public void TickDuration(float deltaTime)
if (duration <= 0)
IsPointing = false;
}

public void StopPointing()
{
IsPointing = false;
RefreshDuration(0f);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
using DCL.Multiplayer.Movement;
using ECS.Abstract;
using ECS.LifeCycle.Components;
using ECS.SceneLifeCycle;
using SceneRunner.Scene;
using System.Runtime.CompilerServices;
using UnityEngine;

Expand Down Expand Up @@ -52,26 +54,69 @@ public HitInfo(bool forceInterrupt, Vector3 hitPoint)
private static readonly int RAYCAST_LAYER_MASK = PhysicsLayers.CHARACTER_ONLY_MASK | (1 << PhysicsLayers.OTHER_AVATARS_LAYER);

private readonly DCLInput dclInput;
private readonly IScenesCache scenesCache;

private SingleInstanceEntity camera;
private bool currentSceneLost;

private HandPointAtSystem(World world) : base(world)
internal HandPointAtSystem(World world, IScenesCache scenesCache) : base(world)
{
dclInput = DCLInput.Instance;
this.scenesCache = scenesCache;

scenesCache.CurrentScene.OnUpdate += OnCurrentSceneChanged;
}

public override void Initialize()
{
camera = World.CacheCamera();
}

protected override void OnDispose()
{
scenesCache.CurrentScene.OnUpdate -= OnCurrentSceneChanged;
}

// Fires synchronously from whichever code sets CurrentScene, so only latch — never mutate components here.
private void OnCurrentSceneChanged(ISceneFacade? currentScene)
{
if (currentScene == null)
currentSceneLost = true;
}

protected override void Update(float t)
{
if (currentSceneLost)
{
currentSceneLost = false;
ResetPointAtOnCurrentSceneLostQuery(World);
}

ResetPointAtOnTeleportQuery(World);
CancelPointAtIfEmotingQuery(World);
UpdateHandPointAtQuery(World, in camera.GetCameraComponent(World), t);
ApplyPointAtIKQuery(World, t);
}

// Movement already clears IsPointing before the parcel can change, so losing CurrentScene while pointing only happens when the scene is torn down under the player (reload, ban, realm change).
[Query]
[All(typeof(PlayerComponent))]
[None(typeof(DeleteEntityIntention))]
private void ResetPointAtOnCurrentSceneLost(ref HandPointAtComponent handPointAtComponent)
{
if (handPointAtComponent.IsPointing)
handPointAtComponent.StopPointing();
}

[Query]
[All(typeof(PlayerComponent), typeof(PlayerTeleportIntent))]
[None(typeof(DeleteEntityIntention))]
private void ResetPointAtOnTeleport(ref HandPointAtComponent handPointAtComponent)
{
if (handPointAtComponent.IsPointing)
handPointAtComponent.StopPointing();
}

private CursorInfo HandleCursorLogic(ref HandPointAtComponent handPointAtComponent)
{
bool isPressed = dclInput.Player.PointAt.IsPressed();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using Arch.Core;
using DCL.Character.CharacterMotion.Components;
using DCL.Character.CharacterMotion.Systems;
using DCL.Character.Components;
using DCL.CharacterCamera;
using DCL.CharacterMotion.Components;
using DCL.Utilities;
using ECS.SceneLifeCycle;
using ECS.TestSuite;
using NSubstitute;
using NUnit.Framework;
using SceneRunner.Scene;
using System.Threading;
using UnityEngine;

namespace DCL.Character.CharacterMotion.Tests
{
public class HandPointAtStaleResetShould : UnitySystemTestBase<HandPointAtSystem>
{
private static readonly Vector3 STALE_TARGET = new (1234f, 56f, 7890f);

private ReactiveProperty<ISceneFacade?> currentScene = null!;

[SetUp]
public void SetUp()
{
world.Create(new CameraComponent());

currentScene = new ReactiveProperty<ISceneFacade?>(Substitute.For<ISceneFacade>());

var scenesCache = Substitute.For<IScenesCache>();
scenesCache.CurrentScene.Returns(currentScene);

system = new HandPointAtSystem(world, scenesCache);
system.Initialize();
}

[Test]
public void StopPointingWhileTeleporting()
{
Entity player = CreatePointingPlayer(
new PlayerTeleportIntent(null, Vector2Int.zero, Vector3.zero, CancellationToken.None));

system!.Update(0.1f);

Assert.That(world.Get<HandPointAtComponent>(player).IsPointing, Is.False);
}

[Test]
public void StopPointingWhenCurrentSceneIsLost()
{
Entity player = CreatePointingPlayer();

currentScene.Value = null;
system!.Update(0.1f);

Assert.That(world.Get<HandPointAtComponent>(player).IsPointing, Is.False);
}

[Test]
public void KeepPointingWhenCurrentSceneChanges()
{
Entity player = CreatePointingPlayer();

currentScene.Value = Substitute.For<ISceneFacade>();
system!.Update(0.1f);

Assert.That(world.Get<HandPointAtComponent>(player).IsPointing, Is.True);
}

[Test]
public void KeepPointingWhenNotTeleporting()
{
Entity player = CreatePointingPlayer();

system!.Update(0.1f);

Assert.That(world.Get<HandPointAtComponent>(player).IsPointing, Is.True);
}

private Entity CreatePointingPlayer() =>
world.Create(
new PlayerComponent(),
new HandPointAtComponent { IsPointing = true, WorldHitPoint = STALE_TARGET });

private Entity CreatePointingPlayer(PlayerTeleportIntent teleportIntent) =>
world.Create(
new PlayerComponent(),
new HandPointAtComponent { IsPointing = true, WorldHitPoint = STALE_TARGET },
teleportIntent);
}
}

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
Expand Up @@ -117,7 +117,7 @@ private async UniTask DisposeAndRestartAsync(Entity entity, ISceneFacade current
// from the file path, not content, so an updated model keeps the same hash and cache
// hits would return stale assets. Draining guarantees fresh loads.
cacheCleaner.UnloadCache(budgeted: false);
Resources.UnloadUnusedAssets();
_ = Resources.UnloadUnusedAssets();
}

await WaitUntilNewSceneIsFullyLoadedAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public void InjectToWorld(ref ArchSystemsWorldBuilder<Arch.Core.World> builder,

if (FeaturesRegistry.Instance.IsEnabled(FeatureId.PointAt))
{
HandPointAtSystem.InjectToWorld(ref builder);
HandPointAtSystem.InjectToWorld(ref builder, scenesCache);
PointAtMarkerSystem.InjectToWorld(ref builder, pointAtMarkerPool, web3IdentityCache, friendsCache, settings.PointAtMarkerVisibilitySettings);
builder.World.Create(PointAtThumbnailCache.Create());
PointAtMarkerCleanUpSystem.InjectToWorld(ref builder, pointAtMarkerPool);
Expand Down
Loading