Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: sdk other players transform propagation #1030

Merged
merged 22 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a2cae1f
added systems to propagate and write
pravusjif May 9, 2024
397a718
Merge branch 'main' of github.com:decentraland/unity-explorer into fe…
pravusjif Jun 13, 2024
b4aefb1
Merge branch 'main' into feat/sdk-other-players-transform-propagation
NickKhalow Jul 17, 2024
282a34e
remove unused arg
NickKhalow Jul 17, 2024
2e55d15
Merge branch 'main' into feat/sdk-other-players-transform-propagation
NickKhalow Jul 17, 2024
b84f2bc
Merge branch 'main' into feat/sdk-other-players-transform-propagation
NickKhalow Jul 17, 2024
ca5937b
transform propagation test
NickKhalow Jul 17, 2024
20ca672
Merge remote-tracking branch 'origin/feat/sdk-other-players-transform…
NickKhalow Jul 17, 2024
81f389e
fixed other players transform data not being propagated correctly
pravusjif Jul 17, 2024
8e73a7e
Merge branch 'main' into feat/sdk-other-players-transform-propagation
NickKhalow Jul 18, 2024
1befc16
Merge branch 'main' of github.com:decentraland/unity-explorer into fe…
pravusjif Jul 24, 2024
a76af77
fixed problems from main merge
pravusjif Jul 24, 2024
f710968
RealmLauncherSettings drawer property fix
pravusjif Jul 24, 2024
6cb76e1
Merge branch 'main' of github.com:decentraland/unity-explorer into fe…
pravusjif Sep 11, 2024
b6c7fd5
tackled some PR review feedback
pravusjif Sep 12, 2024
b5c6df3
Made SDKTransform implement IExplosedTransform to be able to use Expo…
pravusjif Sep 12, 2024
f03598b
Merge branch 'main' into feat/sdk-other-players-transform-propagation
pravusjif Sep 12, 2024
82608fb
fixed missing import on main merge
pravusjif Sep 12, 2024
931e5e2
fixed test
pravusjif Sep 12, 2024
c507c67
Merge branch 'main' of github.com:decentraland/unity-explorer into fe…
pravusjif Sep 17, 2024
7ee519e
removed 'fake' class/interfaces introduced and replaced them with NSu…
pravusjif Sep 17, 2024
3b13941
Merge branch 'main' of github.com:decentraland/unity-explorer into fe…
pravusjif Sep 17, 2024
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 @@ -101,6 +101,9 @@ private void Awake()
overrideController.ApplyOverrides(animationOverrides);
}

public Vector3 Position => transform.position;
public Quaternion Rotation => transform.rotation;
pravusjif marked this conversation as resolved.
Show resolved Hide resolved

public Transform GetTransform() =>
transform;

Expand Down Expand Up @@ -152,6 +155,10 @@ public void ReplaceEmoteAnimation(AnimationClip animationClip)

public interface IAvatarView
{
Vector3 Position { get; }

Quaternion Rotation { get; }

Transform GetTransform();

void SetAnimatorFloat(int hash, float value);
Expand All @@ -173,5 +180,62 @@ public interface IAvatarView
int GetAnimatorCurrentStateTag();

void ResetTrigger(int hash);

class Fake : IAvatarView
{
public Vector3 Position { get; }
public Quaternion Rotation { get; }

public Fake(Vector3 position, Quaternion rotation)
{
Position = position;
Rotation = rotation;
}

public Transform GetTransform() =>
throw new NotImplementedException();

public void SetAnimatorFloat(int hash, float value)
{
throw new NotImplementedException();
}

public void SetAnimatorInt(int hash, int value)
{
throw new NotImplementedException();
}

public void SetAnimatorTrigger(int hash)
{
throw new NotImplementedException();
}

public void SetAnimatorBool(int hash, bool value)
{
throw new NotImplementedException();
}

public bool GetAnimatorBool(int hash) =>
throw new NotImplementedException();

public void ReplaceEmoteAnimation(AnimationClip animationClip)
{
throw new NotImplementedException();
}

public float GetAnimatorFloat(int hash) =>
throw new NotImplementedException();

public bool IsAnimatorInTag(int hashTag) =>
throw new NotImplementedException();

public int GetAnimatorCurrentStateTag() =>
throw new NotImplementedException();

public void ResetTrigger(int hash)
{
throw new NotImplementedException();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace DCL.CharacterMotion.Systems
/// Executes on the scene level to propagate the shared transform data to the SDK Scene
/// </summary>
[UpdateInGroup(typeof(SyncedInitializationSystemGroup))]
public partial class WritePlayerTransformSystem : BaseUnityLoopSystem
public partial class WriteMainPlayerTransformSystem : BaseUnityLoopSystem
{
private readonly IECSToCRDTWriter ecsToCrdtWriter;
private readonly ISceneData sceneData;
Expand All @@ -29,7 +29,7 @@ public partial class WritePlayerTransformSystem : BaseUnityLoopSystem
private IComponentPool<SDKTransform> sdkTransformPool;
private readonly Entity playerEntity;

internal WritePlayerTransformSystem(World world, IECSToCRDTWriter ecsToCrdtWriter, ISceneData sceneData, IExposedTransform exposedTransform, IPartitionComponent scenePartition,
internal WriteMainPlayerTransformSystem(World world, IECSToCRDTWriter ecsToCrdtWriter, ISceneData sceneData, IExposedTransform exposedTransform, IPartitionComponent scenePartition,
byte bucketThreshold, IComponentPool<SDKTransform> sdkTransformPool, Entity playerEntity) : base(world)
{
this.ecsToCrdtWriter = ecsToCrdtWriter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public WorldPlugin(ExposedTransform exposedTransform, IExposedCameraData exposed

public void InjectToWorld(ref ArchSystemsWorldBuilder<World> builder, in ECSWorldInstanceSharedDependencies sharedDependencies, in PersistentEntities persistentEntities, List<IFinalizeWorldSystem> finalizeWorldSystems, List<ISceneIsCurrentListener> sceneIsCurrentListeners)
{
WritePlayerTransformSystem.InjectToWorld(ref builder, sharedDependencies.EcsToCRDTWriter, sharedDependencies.SceneData,
WriteMainPlayerTransformSystem.InjectToWorld(ref builder, sharedDependencies.EcsToCRDTWriter, sharedDependencies.SceneData,
exposedTransform, sharedDependencies.ScenePartition, bucketPropagationLimit, sdkTransformPool, persistentEntities.Player);

WriteCameraComponentsSystem.InjectToWorld(ref builder, sharedDependencies.EcsToCRDTWriter, exposedCameraData, sharedDependencies.SceneData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,20 @@ protected override void Update(float t)

[Query]
[None(typeof(DeleteEntityIntention))]
private void PropagateProfileToScene(Profile profile, PlayerCRDTEntity playerCRDTEntity)
private void PropagateProfileToScene(ref Profile profile, ref PlayerCRDTEntity playerCRDTEntity)
pravusjif marked this conversation as resolved.
Show resolved Hide resolved
{
if (playerCRDTEntity.IsDirty)
{
SetSceneProfile(profile, playerCRDTEntity);
PropagateComponent(ref profile, ref playerCRDTEntity);
return;
}

if (!profile.IsDirty) return;

SetSceneProfile(profile, playerCRDTEntity);
PropagateComponent(ref profile, ref playerCRDTEntity);
}

private void SetSceneProfile(Profile profile, PlayerCRDTEntity playerCRDTEntity)
private void PropagateComponent(ref Profile profile, ref PlayerCRDTEntity playerCRDTEntity)
{
SceneEcsExecutor sceneEcsExecutor = playerCRDTEntity.SceneFacade.EcsExecutor;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Arch.Core;
using Arch.System;
using Arch.SystemGroups;
using CrdtEcsBridge.Components;
using CrdtEcsBridge.Components.Transform;
using DCL.AvatarRendering.AvatarShape.UnityInterface;
using DCL.Diagnostics;
using DCL.Multiplayer.SDK.Components;
using ECS.Abstract;
using ECS.Groups;
using ECS.LifeCycle.Components;

namespace DCL.Multiplayer.SDK.Systems.GlobalWorld
{
[UpdateInGroup(typeof(SyncedPreRenderingSystemGroup))]
[LogCategory(ReportCategory.MULTIPLAYER_SDK_PLAYER_TRANSFORM_DATA)]
public partial class PlayerTransformPropagationSystem : BaseUnityLoopSystem
{
public PlayerTransformPropagationSystem(World world) : base(world) { }

protected override void Update(float t)
{
PropagateTransformToSceneQuery(World!);
}
pravusjif marked this conversation as resolved.
Show resolved Hide resolved

[Query]
[None(typeof(DeleteEntityIntention))]
private void PropagateTransformToScene(ref IAvatarView avatarView, ref PlayerCRDTEntity playerCRDTEntity)
mikhail-dcl marked this conversation as resolved.
Show resolved Hide resolved
{
// Main player Transform is handled by 'WriteMainPlayerTransformSystem'
if (playerCRDTEntity.CRDTEntity.Id == SpecialEntitiesID.PLAYER_ENTITY) return;

PropagateComponent(ref avatarView, ref playerCRDTEntity);
}

private static void PropagateComponent(ref IAvatarView avatarBase, ref PlayerCRDTEntity playerCRDTEntity)
{
World sceneEcsWorld = playerCRDTEntity.SceneFacade.EcsExecutor.World;

var sdkTransform = new SDKTransform
{
Position = avatarBase.Position, // updated to scene-relative on the writer system
Rotation = avatarBase.Rotation,
};
pravusjif marked this conversation as resolved.
Show resolved Hide resolved

if (sceneEcsWorld.Has<SDKTransform>(playerCRDTEntity.SceneWorldEntity))
sceneEcsWorld.Set(playerCRDTEntity.SceneWorldEntity, sdkTransform);
else
sceneEcsWorld.Add(playerCRDTEntity.SceneWorldEntity, sdkTransform);
}
}
}

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,61 @@
using Arch.Core;
using Arch.System;
using Arch.SystemGroups;
using CrdtEcsBridge.Components;
using CrdtEcsBridge.Components.Transform;
using CrdtEcsBridge.ECSToCRDTWriter;
using DCL.Multiplayer.SDK.Components;
using ECS.Abstract;
using ECS.Groups;
using ECS.LifeCycle.Components;
using ECS.Prioritization.Components;
using SceneRunner.Scene;
using Utility;

namespace DCL.Multiplayer.SDK.Systems.SceneWorld
{
[UpdateInGroup(typeof(SyncedPreRenderingSystemGroup))]
// [UpdateBefore(typeof(CleanUpGroup))]
// [LogCategory(ReportCategory.PLAYER_AVATAR_BASE)]
public partial class WritePlayerTransformSystem : BaseUnityLoopSystem
{
private readonly IECSToCRDTWriter ecsToCRDTWriter;
private readonly ISceneData sceneData;

public WritePlayerTransformSystem(World world, IECSToCRDTWriter ecsToCRDTWriter, ISceneData sceneData) : base(world)
{
this.ecsToCRDTWriter = ecsToCRDTWriter;
this.sceneData = sceneData;
}

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

[Query]
[None(typeof(DeleteEntityIntention))]
private void UpdateSDKTransform(ref PlayerCRDTEntity playerCRDTEntity, ref SDKTransform sdkTransform)
mikhail-dcl marked this conversation as resolved.
Show resolved Hide resolved
{
// Main player Transform is handled by 'WriteMainPlayerTransformSystem'
if (playerCRDTEntity.CRDTEntity.Id == SpecialEntitiesID.PLAYER_ENTITY) return;

// Patch position to be scene-relative
sdkTransform.Position = ParcelMathHelper.GetSceneRelativePosition(sdkTransform.Position, sceneData.Geometry.BaseParcelPosition);

ecsToCRDTWriter.PutMessage<SDKTransform, SDKTransform>(static (pbComponent, transform) =>
mikhail-dcl marked this conversation as resolved.
Show resolved Hide resolved
{
pbComponent.Position = transform.Position;
pbComponent.Rotation = transform.Rotation;
}, playerCRDTEntity.CRDTEntity, sdkTransform);
}

[Query]
[All(typeof(DeleteEntityIntention))]
private void HandleComponentRemoval(ref PlayerCRDTEntity playerCRDTEntity)
{
ecsToCRDTWriter.DeleteMessage<SDKTransform>(playerCRDTEntity.CRDTEntity);
}
}
}

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

109 changes: 109 additions & 0 deletions Explorer/Assets/DCL/Multiplayer/SDK/Tests/PropagateTransformTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#nullable enable

using Arch.Core;
using CRDT;
using CrdtEcsBridge.Components.Transform;
using CrdtEcsBridge.ECSToCRDTWriter;
using DCL.AvatarRendering.AvatarShape.UnityInterface;
using DCL.Diagnostics;
using DCL.Multiplayer.SDK.Components;
using DCL.Multiplayer.SDK.Systems.GlobalWorld;
using DCL.Multiplayer.SDK.Systems.SceneWorld;
using Google.Protobuf;
using NUnit.Framework;
using SceneRunner.Scene;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace DCL.Multiplayer.SDK.Tests
{
public class PropagateTransformTest
{
[Test]
public void PropagateTransform()
{
const int CRDT_ID = 100;

var world = World.Create();
var ecsWorld = World.Create();

var writer = new FakeECSToCRDTWriter();
var sceneData = new ISceneData.Fake();

var propagationSystem = new PlayerTransformPropagationSystem(world);
var writeSystem = new WritePlayerTransformSystem(ecsWorld, writer, sceneData);

var sceneWorldEntity = ecsWorld.Create();

var playerCRDTEntity = new PlayerCRDTEntity(
new CRDTEntity(CRDT_ID),
new ISceneFacade.Fake(
new SceneShortInfo(Vector2Int.zero, string.Empty),
new SceneStateProvider(),
new SceneEcsExecutor(ecsWorld),
false
),
sceneWorldEntity
);

world.Create(
new IAvatarView.Fake(Vector3.zero, Quaternion.identity) as IAvatarView,
playerCRDTEntity
);

ecsWorld.Add(sceneWorldEntity, playerCRDTEntity);

propagationSystem.Update(0);
writeSystem.Update(0);


Assert.AreEqual(1, writer.Messages.Count);

var message = writer.Messages.First();

Assert.AreEqual(typeof(SDKTransform), message.MessageType);
Assert.AreEqual(typeof(SDKTransform), message.Data.GetType());
Assert.AreEqual(CRDT_ID, message.Entity.Id);
}

private class FakeECSToCRDTWriter : IECSToCRDTWriter
{
public readonly struct Message
{
public readonly CRDTEntity Entity;
public readonly Type MessageType;
public readonly object Data;

public Message(CRDTEntity entity, Type messageType, object data)
{
Entity = entity;
MessageType = messageType;
Data = data;
}
}

public readonly List<Message> Messages = new ();

public TMessage PutMessage<TMessage, TData>(Action<TMessage, TData> prepareMessage, CRDTEntity entity, TData data) where TMessage: class, IMessage
{
Messages.Add(new Message(entity, typeof(TMessage), data));
return null;
}

public void PutMessage<TMessage>(TMessage message, CRDTEntity entity) where TMessage: class, IMessage
{
throw new NotImplementedException();
}

public TMessage AppendMessage<TMessage, TData>(Action<TMessage, TData> prepareMessage, CRDTEntity entity, int timestamp, TData data) where TMessage: class, IMessage =>
throw new NotImplementedException();

public void DeleteMessage<T>(CRDTEntity crdtID) where T: class, IMessage
{
throw new NotImplementedException();
}
}
}
}

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 @@ -218,6 +218,11 @@ public static class ReportCategory
/// </summary>
public const string MULTIPLAYER_SDK_EMOTE_COMMAND_DATA = nameof(MULTIPLAYER_SDK_EMOTE_COMMAND_DATA);

/// <summary>
/// Multiplayer SDK other players Transform component propagator
/// </summary>
public const string MULTIPLAYER_SDK_PLAYER_TRANSFORM_DATA = nameof(MULTIPLAYER_SDK_PLAYER_TRANSFORM_DATA);

/// <summary>
/// PBPlayerIdentityData component writer
/// </summary>
Expand Down
Loading
Loading