Skip to content
Snippets Groups Projects
Commit 23c33f1b authored by David Gilbert's avatar David Gilbert :bug:
Browse files

feature(cave, replication): Makes cluster join logic more robust

parent 059ce84f
No related branches found
No related tags found
2 merge requests!107UE5.4-2024.1-rc1,!98Improved join/attachment logic for clusters to support multi-cluster connections at some point
Pipeline #440269 failed
...@@ -29,8 +29,6 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle ...@@ -29,8 +29,6 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
// If not, just ingore all related args. // If not, just ingore all related args.
ARWTHVRPlayerState* State = Cast<ARWTHVRPlayerState>(NewPlayerController->PlayerState); ARWTHVRPlayerState* State = Cast<ARWTHVRPlayerState>(NewPlayerController->PlayerState);
bool bIsClusterNode = false;
if (State != nullptr) if (State != nullptr)
{ {
int32 ClusterId = -1; int32 ClusterId = -1;
...@@ -51,20 +49,25 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle ...@@ -51,20 +49,25 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
const EPlayerType Type = const EPlayerType Type =
NodeName == PrimaryNodeId ? EPlayerType::nDisplayPrimary : EPlayerType::nDisplaySecondary; NodeName == PrimaryNodeId ? EPlayerType::nDisplayPrimary : EPlayerType::nDisplaySecondary;
State->RequestSetPlayerType(Type); State->RequestSetPlayerType(Type);
bIsClusterNode = true;
} }
else if (GetNetMode() == NM_Standalone && URWTHVRUtilities::IsRoomMountedMode()) else if (GetNetMode() == NM_Standalone && URWTHVRUtilities::IsRoomMountedMode())
{ {
ClusterId = 0; ClusterId = 0;
bIsClusterNode = true; }
State->SetCorrespondingClusterId(ClusterId);
}
return Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);
} }
void ARWTHVRGameModeBase::PostLogin(APlayerController* NewPlayer)
{
if (ARWTHVRPlayerState* State = Cast<ARWTHVRPlayerState>(NewPlayer->PlayerState); State != nullptr)
{
// If we're in none-standalone netmode, this is only executed on the server, as the GM only exists there. // If we're in none-standalone netmode, this is only executed on the server, as the GM only exists there.
// On standalone, this is executed on every node. // On standalone, this is executed on every node.
int32 ClusterId = State->GetCorrespondingClusterId();
State->SetCorrespondingClusterId(ClusterId); if (ClusterId >= 0) // we're either standalone (0) or in an acutal cluster
if (bIsClusterNode)
{ {
AClusterRepresentationActor** ClusterRepresentationPtr = ConnectedClusters.Find(ClusterId); AClusterRepresentationActor** ClusterRepresentationPtr = ConnectedClusters.Find(ClusterId);
AClusterRepresentationActor* ClusterRepresentation; AClusterRepresentationActor* ClusterRepresentation;
...@@ -75,7 +78,6 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle ...@@ -75,7 +78,6 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
SpawnParameters.Name = FName(*FString::Printf(TEXT("ClusterRepresentation_%d"), ClusterId)); SpawnParameters.Name = FName(*FString::Printf(TEXT("ClusterRepresentation_%d"), ClusterId));
SpawnParameters.NameMode = FActorSpawnParameters::ESpawnActorNameMode::Requested; SpawnParameters.NameMode = FActorSpawnParameters::ESpawnActorNameMode::Requested;
ClusterRepresentation = GetWorld()->SpawnActor<AClusterRepresentationActor>(SpawnParameters); ClusterRepresentation = GetWorld()->SpawnActor<AClusterRepresentationActor>(SpawnParameters);
ClusterRepresentation->ClusterId = ClusterId;
UE_LOGFMT(Toolkit, Display, UE_LOGFMT(Toolkit, Display,
"ARWTHVRGameModeBase: Spawned ClusterRepresentationActor {Name} for Cluster {Id}", "ARWTHVRGameModeBase: Spawned ClusterRepresentationActor {Name} for Cluster {Id}",
ClusterRepresentation->GetName(), ClusterId); ClusterRepresentation->GetName(), ClusterId);
...@@ -90,25 +92,17 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle ...@@ -90,25 +92,17 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
*ClusterRepresentation->GetName(), ClusterId); *ClusterRepresentation->GetName(), ClusterId);
// Double check for sanity // Double check for sanity
check(ClusterId == ClusterRepresentation->ClusterId); check(ClusterRepresentation != nullptr);
State->SetCorrespondingClusterActor(ClusterRepresentation); State->SetCorrespondingClusterActor(ClusterRepresentation);
if (State->GetPlayerType() == EPlayerType::nDisplayPrimary) if (State->GetPlayerType() == EPlayerType::nDisplayPrimary)
{ {
// We're the owner of the actor! // We're the owner of the actor!
ClusterRepresentation->SetOwner(NewPlayerController); ClusterRepresentation->SetOwner(NewPlayer);
}
} }
} }
return Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);
}
void ARWTHVRGameModeBase::PostLogin(APlayerController* NewPlayer)
{
if (const ARWTHVRPlayerState* State = Cast<ARWTHVRPlayerState>(NewPlayer->PlayerState); State != nullptr)
{
// Do we already have an auto-possessing pawn possessed? // Do we already have an auto-possessing pawn possessed?
if (NewPlayer->GetPawn() && NewPlayer->GetPawn()->IsValidLowLevelFast()) if (NewPlayer->GetPawn() && NewPlayer->GetPawn()->IsValidLowLevelFast())
{ {
......
...@@ -26,7 +26,13 @@ AClusterRepresentationActor::AClusterRepresentationActor() ...@@ -26,7 +26,13 @@ AClusterRepresentationActor::AClusterRepresentationActor()
void AClusterRepresentationActor::BeginPlay() void AClusterRepresentationActor::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
// will fail if we're in replicated mode and PlayerState has not yet replicated fully
// Therefore we also execute this
AttachDCRAIfRequired();
}
void AClusterRepresentationActor::AttachDCRAIfRequired(const ARWTHVRPlayerState* OptionalPlayerState)
{
// We need to identify the correct ClusterRepresentationActor to do the attachment. // We need to identify the correct ClusterRepresentationActor to do the attachment.
// 1. Either we are the local net owner -> Primary Node Pawn // 1. Either we are the local net owner -> Primary Node Pawn
// This is hard to do as things might not be synchronized yet. // This is hard to do as things might not be synchronized yet.
...@@ -38,30 +44,43 @@ void AClusterRepresentationActor::BeginPlay() ...@@ -38,30 +44,43 @@ void AClusterRepresentationActor::BeginPlay()
if (!URWTHVRUtilities::IsRoomMountedMode()) if (!URWTHVRUtilities::IsRoomMountedMode())
return; return;
if (bIsAttached)
{
UE_LOGFMT(Toolkit, Display,
"AClusterRepresentationActor::AttachDCRAIfRequired: Already attached, skipping repeated attachment.");
return;
}
UE_LOGFMT(Toolkit, Display, "AClusterRepresentationActor::AttachDCRAIfRequired: Starting DCRA Attachment process.");
// This should give us the first local player controller // This should give us the first local player controller
const auto* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0); const auto* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
// Only run this for the local controller - redundant, but double check // Only run this for the local controller - redundant, but double check
if (!PlayerController || !PlayerController->IsLocalController()) if (!PlayerController || !PlayerController->IsLocalController())
{
UE_LOGFMT(
Toolkit, Warning,
"AClusterRepresentationActor::AttachDCRAIfRequired: PlayerController not valid or not locally controlled.");
return; return;
}
const auto* PlayerState = PlayerController->GetPlayerState<ARWTHVRPlayerState>(); const auto* PlayerState =
OptionalPlayerState != nullptr ? OptionalPlayerState : PlayerController->GetPlayerState<ARWTHVRPlayerState>();
if (!PlayerState) if (!PlayerState)
{ {
UE_LOGFMT( UE_LOGFMT(Toolkit, Error,
Toolkit, Error, "AClusterRepresentationActor::AttachDCRAIfRequired: PlayerState is not valid or not of type "
"AClusterRepresentationActor::BeginPlay: PlayerState is not valid or not of type ARWTHVRPlayerState."); "ARWTHVRPlayerState.");
return; return;
} }
// The local player this is executed on corresponds to this actor // The local player this is executed on corresponds to this actor
if (PlayerState->GetCorrespondingClusterActor() == this) if (PlayerState->GetCorrespondingClusterActor() == this)
{ {
check(PlayerState->GetCorrespondingClusterId() == ClusterId); UE_LOGFMT(Toolkit, Display, "AClusterRepresentationActor::AttachDCRAIfRequired: Attaching DCRA to {Name}.",
UE_LOGFMT(Toolkit, Display, "AClusterRepresentationActor::BeginPlay: Attaching DCRA to {Name} with Id: {Id}.", GetName());
GetName(), ClusterId);
AttachDCRA(); bIsAttached = AttachDCRA();
} }
} }
...@@ -76,7 +95,7 @@ bool AClusterRepresentationActor::AttachDCRA() ...@@ -76,7 +95,7 @@ bool AClusterRepresentationActor::AttachDCRA()
if (URWTHVRUtilities::IsRoomMountedMode()) if (URWTHVRUtilities::IsRoomMountedMode())
{ {
UE_LOGFMT(Toolkit, Display, "{Name}: Trying to attach DCRA for ClusterId {Id}", GetName(), ClusterId); UE_LOGFMT(Toolkit, Display, "{Name}: Trying to attach DCRA", GetName());
auto DCRA = IDisplayCluster::Get().GetGameMgr()->GetRootActor(); auto DCRA = IDisplayCluster::Get().GetGameMgr()->GetRootActor();
if (!IsValid(DCRA)) if (!IsValid(DCRA))
...@@ -94,8 +113,8 @@ bool AClusterRepresentationActor::AttachDCRA() ...@@ -94,8 +113,8 @@ bool AClusterRepresentationActor::AttachDCRA()
{ {
if (!DCRA->IsPrimaryRootActor()) if (!DCRA->IsPrimaryRootActor())
{ {
UE_LOGFMT(Toolkit, Error, "Found DCRA {Name} is not the primary DCRA of Cluster with Id {Id}!", UE_LOGFMT(Toolkit, Error, "Found DCRA {Name} is not the primary DCRA of Cluster with Id!",
DCRA->GetName(), ClusterId); DCRA->GetName());
return false; return false;
} }
} }
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "PlayerType.h" #include "PlayerType.h"
#include "GameFramework/PlayerState.h" #include "GameFramework/PlayerState.h"
#include "Logging/StructuredLog.h"
#include "Pawn/ClusterRepresentationActor.h"
#include "Utility/RWTHVRUtilities.h"
#include "RWTHVRPlayerState.generated.h" #include "RWTHVRPlayerState.generated.h"
...@@ -28,10 +31,17 @@ private: ...@@ -28,10 +31,17 @@ private:
UPROPERTY(Replicated, Category = PlayerState, BlueprintGetter = GetCorrespondingClusterId, meta = (AllowPrivateAccess)) UPROPERTY(Replicated, Category = PlayerState, BlueprintGetter = GetCorrespondingClusterId, meta = (AllowPrivateAccess))
int32 CorrespondingClusterId = -1; int32 CorrespondingClusterId = -1;
/** Replicated cluster actor for this player. Is nullptr in case player is not a cluster*/ /** Replicated cluster actor for this player. Is nullptr in case player is not a cluster.
UPROPERTY(Replicated, Category = PlayerState, BlueprintGetter = GetCorrespondingClusterActor, * As this is not guaranteed to be valid on BeginPlay, we need to do a callback to the CorrespondingClusterActor here...
meta = (AllowPrivateAccess)) */
AClusterRepresentationActor* CorrespondingClusterActor = nullptr; UPROPERTY(ReplicatedUsing = OnRep_CorrespondingClusterActor)
TObjectPtr<AClusterRepresentationActor> CorrespondingClusterActor;
UFUNCTION()
virtual void OnRep_CorrespondingClusterActor()
{
CorrespondingClusterActor->AttachDCRAIfRequired(this);
}
UFUNCTION(Reliable, Server) UFUNCTION(Reliable, Server)
void ServerSetPlayerTypeRpc(EPlayerType NewPlayerType); void ServerSetPlayerTypeRpc(EPlayerType NewPlayerType);
......
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
#include "ClusterRepresentationActor.generated.h" #include "ClusterRepresentationActor.generated.h"
class ARWTHVRPlayerState;
class ADisplayClusterRootActor; class ADisplayClusterRootActor;
UCLASS() UCLASS()
class RWTHVRTOOLKIT_API AClusterRepresentationActor : public AActor class RWTHVRTOOLKIT_API AClusterRepresentationActor : public AActor
{ {
...@@ -17,13 +19,13 @@ public: ...@@ -17,13 +19,13 @@ public:
// Sets default values for this actor's properties // Sets default values for this actor's properties
AClusterRepresentationActor(); AClusterRepresentationActor();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
int32 ClusterId = -1;
virtual void BeginPlay() override; virtual void BeginPlay() override;
void AttachDCRAIfRequired(const ARWTHVRPlayerState* OptionalPlayerState = nullptr);
private: private:
bool AttachDCRA(); bool AttachDCRA();
ADisplayClusterRootActor* SpawnDCRA(); ADisplayClusterRootActor* SpawnDCRA();
bool bIsAttached = false;
}; };
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment