From 743f6114e6c4d296e069749c0687a2a29ab71442 Mon Sep 17 00:00:00 2001
From: David Gilbert <gilbert@vr.rwth-aachen.de>
Date: Mon, 8 Jul 2024 17:43:01 +0200
Subject: [PATCH] fix(pawn, replication): Fixes DC Sync Parent Component
 interfering with multiplayer.

---
 .../RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp | 47 ++++++++++++-------
 Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h |  4 ++
 2 files changed, 34 insertions(+), 17 deletions(-)

diff --git a/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp b/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
index 7c91f2f1..f2745a6d 100644
--- a/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
+++ b/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
@@ -41,21 +41,7 @@ ARWTHVRPawn::ARWTHVRPawn(const FObjectInitializer& ObjectInitializer) : Super(Ob
 	LeftHand = CreateDefaultSubobject<UReplicatedMotionControllerComponent>(TEXT("Left Hand MCC"));
 	LeftHand->SetupAttachment(RootComponent);
 }
-void ARWTHVRPawn::BeginPlay()
-{
-	Super::BeginPlay();
-
-#if PLATFORM_SUPPORTS_CLUSTER
-	// Add an nDisplay Parent Sync Component. It syncs the parent's transform from master to clients.
-	// This is required because for collision based movement, it can happen that the physics engine
-	// for some reason acts different on the nodes, therefore leading to a potential desync when
-	// e.g. colliding with an object while moving.
-
-	SyncComponent = Cast<USceneComponent>(AddComponentByClass(UDisplayClusterSceneComponentSyncParent::StaticClass(),
-															  false, FTransform::Identity, false));
-	AddInstanceComponent(SyncComponent);
-#endif
-}
+void ARWTHVRPawn::BeginPlay() { Super::BeginPlay(); }
 
 void ARWTHVRPawn::Tick(float DeltaSeconds)
 {
@@ -79,26 +65,32 @@ void ARWTHVRPawn::NotifyControllerChanged()
 {
 	Super::NotifyControllerChanged();
 
-
 	UE_LOG(Toolkit, Display,
 		   TEXT("ARWTHVRPawn: Player Controller has changed, trying to change DCRA attachment if possible..."));
 
-
 	// Only do this for all local controlled pawns
 	if (IsLocallyControlled())
 	{
+		UE_LOG(Toolkit, Display,
+			   TEXT("ARWTHVRPawn: Player Controller has changed for local pawn, trying to change DCRA attachment if "
+					"possible..."));
 		// Only do this for the primary node or when we're running in standalone
 		if (URWTHVRUtilities::IsRoomMountedMode() &&
 			(URWTHVRUtilities::IsPrimaryNode() || GetNetMode() == NM_Standalone))
 		{
+			UE_LOG(Toolkit, Display,
+				   TEXT("ARWTHVRPawn: We're the primary node, we want the DCRA attached to our pawn."));
 			// If we are also the authority (standalone or listen server), directly attach it to us.
 			// If we are not (client), ask the server to do it.
 			if (HasAuthority())
 			{
+				UE_LOG(Toolkit, Display, TEXT("ARWTHVRPawn: We have authority, do the attachment."));
 				AttachDCRAtoPawn();
 			}
 			else
 			{
+				UE_LOG(Toolkit, Display, TEXT("ARWTHVRPawn: We don't have authority, ask the sercer to attach."));
+
 				ServerAttachDCRAtoPawnRpc();
 			}
 		}
@@ -247,6 +239,24 @@ void ARWTHVRPawn::UpdateRightHandForDesktopInteraction() const
 	}
 }
 
+void ARWTHVRPawn::MulticastAddDCSyncComponent_Implementation()
+{
+#if PLATFORM_SUPPORTS_CLUSTER
+	// Add an nDisplay Parent Sync Component. It syncs the parent's transform from master to clients.
+	// This is required because for collision based movement, it can happen that the physics engine
+	// for some reason acts different on the nodes, therefore leading to a potential desync when
+	// e.g. colliding with an object while moving.
+
+	if (URWTHVRUtilities::IsRoomMountedMode())
+	{
+		SyncComponent = Cast<USceneComponent>(AddComponentByClass(
+			UDisplayClusterSceneComponentSyncParent::StaticClass(), false, FTransform::Identity, false));
+		AddInstanceComponent(SyncComponent);
+		UE_LOGFMT(Toolkit, Display, "RWTHVRPawn: Added Sync Component to pawn {Pawn}", GetName());
+	}
+#endif
+}
+
 // Todo rewrite this in some other way or attach it differently, this is horrible
 // Executed on the server only: Finds and attaches the CaveSetup Actor, which contains the DCRA to the Pawn.
 // It is only executed on the server because attachments are synced to all clients, but not from client to server.
@@ -274,6 +284,9 @@ void ARWTHVRPawn::AttachDCRAtoPawn()
 		UE_LOGFMT(Toolkit, Warning,
 				  "No CaveSetup Actor found which can be attached to the Pawn! This won't work on the Cave.");
 	}
+
+	if (HasAuthority()) // Should always be the case here, but double check
+		MulticastAddDCSyncComponent();
 }
 
 void ARWTHVRPawn::SetupMotionControllerSources()
diff --git a/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h b/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
index abe9255d..2f150db2 100644
--- a/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
+++ b/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
@@ -107,6 +107,10 @@ protected:
 	UFUNCTION(Reliable, Server)
 	void ServerAttachDCRAtoPawnRpc();
 
+	/* Add a sync component to all instances of this pawn */
+	UFUNCTION(Reliable, NetMulticast)
+	void MulticastAddDCSyncComponent();
+
 	/* Attaches the DCRA to the pawn */
 	void AttachDCRAtoPawn();
 
-- 
GitLab