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

Merge branch 'feature/multiple_clusters' into 'dev/5.4'

Improved join/attachment logic for clusters to support multi-cluster connections at some point

See merge request !98
parents 084266e5 ff532a9a
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 #477084 passed
No preview for this file type
No preview for this file type
......@@ -7,9 +7,15 @@
#include "GameFramework/SpectatorPawn.h"
#include "Kismet/GameplayStatics.h"
#include "Logging/StructuredLog.h"
#include "Pawn/ClusterRepresentationActor.h"
#include "Utility/RWTHVRUtilities.h"
ARWTHVRGameModeBase::ARWTHVRGameModeBase(const FObjectInitializer& ObjectInitializer)
{
PlayerStateClass = ARWTHVRPlayerState::StaticClass();
}
FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerController, const FUniqueNetIdRepl& UniqueId,
const FString& Options, const FString& Portal)
{
......@@ -17,6 +23,7 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
// but I don't really want to introduce a hard dependency here.
const FString NodeNameKey = "node";
const FString PrimaryNodeIdKey = "PrimaryNodeId";
const FString ClusterIdKey = "ClusterId";
// Check if we're using our custom PlayerState so that we can save the player type there.
// If not, just ingore all related args.
......@@ -24,6 +31,7 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
if (State != nullptr)
{
int32 ClusterId = -1;
if (UGameplayStatics::HasOption(Options, PrimaryNodeIdKey))
{
const FString PrimaryNodeId = UGameplayStatics::ParseOption(Options, PrimaryNodeIdKey);
......@@ -34,10 +42,22 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
? UGameplayStatics::ParseOption(Options, NodeNameKey)
: PrimaryNodeId;
ClusterId = UGameplayStatics::HasOption(Options, ClusterIdKey)
? FCString::Atoi(*UGameplayStatics::ParseOption(Options, ClusterIdKey))
: -1;
const EPlayerType Type =
NodeName == PrimaryNodeId ? EPlayerType::nDisplayPrimary : EPlayerType::nDisplaySecondary;
State->RequestSetPlayerType(Type);
}
else if (GetNetMode() == NM_Standalone && URWTHVRUtilities::IsRoomMountedMode())
{
ClusterId = 0;
const EPlayerType Type =
URWTHVRUtilities::IsPrimaryNode() ? EPlayerType::nDisplayPrimary : EPlayerType::nDisplaySecondary;
State->RequestSetPlayerType(Type);
}
State->SetCorrespondingClusterId(ClusterId);
}
return Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);
......@@ -45,8 +65,47 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
void ARWTHVRGameModeBase::PostLogin(APlayerController* NewPlayer)
{
if (const ARWTHVRPlayerState* State = Cast<ARWTHVRPlayerState>(NewPlayer->PlayerState); State != nullptr)
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.
// On standalone, this is executed on every node.
int32 ClusterId = State->GetCorrespondingClusterId();
if (ClusterId >= 0) // we're either standalone (0) or in an acutal cluster
{
AClusterRepresentationActor** ClusterRepresentationPtr = ConnectedClusters.Find(ClusterId);
AClusterRepresentationActor* ClusterRepresentation;
if (!ClusterRepresentationPtr)
{
// No actor there yet, spawn it
FActorSpawnParameters SpawnParameters;
SpawnParameters.Name = FName(*FString::Printf(TEXT("ClusterRepresentation_%d"), ClusterId));
SpawnParameters.NameMode = FActorSpawnParameters::ESpawnActorNameMode::Requested;
ClusterRepresentation = GetWorld()->SpawnActor<AClusterRepresentationActor>(SpawnParameters);
UE_LOGFMT(Toolkit, Display,
"ARWTHVRGameModeBase: Spawned ClusterRepresentationActor {Name} for Cluster {Id}",
ClusterRepresentation->GetName(), ClusterId);
ConnectedClusters.Add(ClusterId, ClusterRepresentation);
}
else
{
ClusterRepresentation = *ClusterRepresentationPtr;
}
UE_LOGFMT(Toolkit, Display, "ARWTHVRGameModeBase: Using ClusterRepresentationActor {Name} for Cluster {Id}",
*ClusterRepresentation->GetName(), ClusterId);
// Double check for sanity
check(ClusterRepresentation != nullptr);
State->SetCorrespondingClusterActor(ClusterRepresentation);
if (State->GetPlayerType() == EPlayerType::nDisplayPrimary)
{
// We're the owner of the actor!
ClusterRepresentation->SetOwner(NewPlayer);
}
}
// Do we already have an auto-possessing pawn possessed?
if (NewPlayer->GetPawn() && NewPlayer->GetPawn()->IsValidLowLevelFast())
{
......@@ -56,10 +115,11 @@ void ARWTHVRGameModeBase::PostLogin(APlayerController* NewPlayer)
return;
}
// When we're not in standalone:
// If the new player is a secondary nDisplay node, spawn it only as a Spectator
// Potentially we can use MustSpectate instead.
UClass* PawnClass;
if (State->GetPlayerType() == EPlayerType::nDisplaySecondary)
if (GetNetMode() != NM_Standalone && State->GetPlayerType() == EPlayerType::nDisplaySecondary)
{
// For now, simply use the BP approach of spawning the pawn here. Can do this in a better way potentially.
PawnClass = SpectatorClass;
......@@ -88,6 +148,5 @@ void ARWTHVRGameModeBase::PostLogin(APlayerController* NewPlayer)
NewPlayer->Possess(ResultPawn);
}
Super::PostLogin(NewPlayer);
}
......@@ -3,8 +3,10 @@
#include "Core/RWTHVRPlayerState.h"
#include "Logging/StructuredLog.h"
#include "Net/UnrealNetwork.h"
#include "Net/Core/PushModel/PushModel.h"
#include "Utility/RWTHVRUtilities.h"
// Boilerplate, copies properties to new state
void ARWTHVRPlayerState::CopyProperties(class APlayerState* PlayerState)
......@@ -17,6 +19,8 @@ void ARWTHVRPlayerState::CopyProperties(class APlayerState* PlayerState)
if (IsValid(RWTHVRPlayerState))
{
RWTHVRPlayerState->SetPlayerType(GetPlayerType());
RWTHVRPlayerState->SetCorrespondingClusterId(CorrespondingClusterId);
RWTHVRPlayerState->SetCorrespondingClusterActor(CorrespondingClusterActor);
}
}
}
......@@ -32,6 +36,8 @@ void ARWTHVRPlayerState::OverrideWith(class APlayerState* PlayerState)
if (IsValid(RWTHVRPlayerState))
{
SetPlayerType(RWTHVRPlayerState->GetPlayerType());
SetCorrespondingClusterId(RWTHVRPlayerState->CorrespondingClusterId);
SetCorrespondingClusterActor(RWTHVRPlayerState->CorrespondingClusterActor);
}
}
}
......@@ -45,6 +51,8 @@ void ARWTHVRPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& O
SharedParams.bIsPushBased = true;
DOREPLIFETIME_WITH_PARAMS_FAST(ARWTHVRPlayerState, PlayerType, SharedParams);
DOREPLIFETIME_WITH_PARAMS_FAST(ARWTHVRPlayerState, CorrespondingClusterId, SharedParams);
DOREPLIFETIME_WITH_PARAMS_FAST(ARWTHVRPlayerState, CorrespondingClusterActor, SharedParams);
}
void ARWTHVRPlayerState::ServerSetPlayerTypeRpc_Implementation(const EPlayerType NewPlayerType)
......@@ -57,6 +65,28 @@ void ARWTHVRPlayerState::SetPlayerType(const EPlayerType NewPlayerType)
MARK_PROPERTY_DIRTY_FROM_NAME(ARWTHVRPlayerState, PlayerType, this);
PlayerType = NewPlayerType;
}
void ARWTHVRPlayerState::SetCorrespondingClusterId(int32 NewCorrespondingClusterId)
{
if (!HasAuthority())
{
UE_LOGFMT(Toolkit, Warning, "ARWTHVRPlayerState: Cannot set cluster Id on non-authority!");
return;
}
MARK_PROPERTY_DIRTY_FROM_NAME(ARWTHVRPlayerState, CorrespondingClusterId, this);
CorrespondingClusterId = NewCorrespondingClusterId;
}
void ARWTHVRPlayerState::SetCorrespondingClusterActor(AClusterRepresentationActor* NewCorrespondingClusterActor)
{
if (!HasAuthority())
{
UE_LOGFMT(Toolkit, Warning, "ARWTHVRPlayerState: Cannot set cluster actor ref on non-authority!");
return;
}
MARK_PROPERTY_DIRTY_FROM_NAME(ARWTHVRPlayerState, CorrespondingClusterActor, this);
CorrespondingClusterActor = NewCorrespondingClusterActor;
}
void ARWTHVRPlayerState::RequestSetPlayerType(const EPlayerType NewPlayerType)
{
......
// Fill out your copyright notice in the Description page of Project Settings.
#include "Pawn/ClusterRepresentationActor.h"
#include "Core/RWTHVRPlayerState.h"
#include "Kismet/GameplayStatics.h"
#include "Logging/StructuredLog.h"
#include "Utility/RWTHVRUtilities.h"
#if PLATFORM_SUPPORTS_CLUSTER
#include "DisplayClusterRootActor.h"
#include "IDisplayCluster.h"
#include "Config/IDisplayClusterConfigManager.h"
#include "Game/IDisplayClusterGameManager.h"
#endif
// Sets default values
AClusterRepresentationActor::AClusterRepresentationActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
bReplicates = true;
SetActorEnableCollision(false);
SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Origin")));
}
void AClusterRepresentationActor::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.
// 1. Either we are the local net owner -> Primary Node Pawn
// This is hard to do as things might not be synchronized yet.
// In this case I think this is save to do because either:
// - We are in standalone mode, then attach it for all nodes
// - If we are a client, this actor has been spawned on the server only. Therefore I assume that if we
// have replicated this actor to our client, we're good to go.
#if PLATFORM_SUPPORTS_CLUSTER
if (!URWTHVRUtilities::IsRoomMountedMode())
return;
if (bIsAttached)
{
UE_LOGFMT(Toolkit, Display, "{Name} AttachDCRAIfRequired: Already attached, skipping repeated attachment.",
GetName());
return;
}
UE_LOGFMT(Toolkit, Display, "{Name} AttachDCRAIfRequired: Starting DCRA Attachment process.", GetName());
// This should give us the first local player controller
const auto* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
// Only run this for the local controller - redundant, but double check
if (!PlayerController || !PlayerController->IsLocalController())
{
UE_LOGFMT(Toolkit, Warning,
"{Name} AttachDCRAIfRequired: PlayerController not valid or not locally controlled.", GetName());
return;
}
const auto* PlayerState =
OptionalPlayerState != nullptr ? OptionalPlayerState : PlayerController->GetPlayerState<ARWTHVRPlayerState>();
if (!PlayerState)
{
UE_LOGFMT(Toolkit, Error,
"{Name} AttachDCRAIfRequired: PlayerState is not valid or not of type "
"ARWTHVRPlayerState.",
GetName());
return;
}
const auto CCA = PlayerState->GetCorrespondingClusterActor();
if (CCA == nullptr) // this can happen often if property isn't replicated yet, this is okay.
return;
UE_LOGFMT(Toolkit, Display,
"{Name} AttachDCRAIfRequired: Player State is {PlayerState}, PlayerState->CCA is {CCA}.", GetName(),
PlayerState->GetName(), CCA->GetName());
// The local player this is executed on corresponds to this actor
if (CCA == this)
{
UE_LOGFMT(Toolkit, Display, "{Name} AttachDCRAIfRequired: Attaching DCRA to {Name}.", GetName(),
CCA->GetName());
bIsAttached = AttachDCRA();
}
#endif
}
#if PLATFORM_SUPPORTS_CLUSTER
bool AClusterRepresentationActor::AttachDCRA()
{
if (URWTHVRUtilities::IsRoomMountedMode())
{
UE_LOGFMT(Toolkit, Display, "{Name}: Trying to attach DCRA", GetName());
auto DCRA = IDisplayCluster::Get().GetGameMgr()->GetRootActor();
if (!IsValid(DCRA))
{
UE_LOGFMT(Toolkit, Display, "No Valid DCRA in BeginPlay. Spawning manually.");
DCRA = SpawnDCRA();
if (!IsValid(DCRA))
{
UE_LOGFMT(Toolkit, Error, "Failed to spawn correct DCRA, cannot attach it to ClusterRepresentation.");
return false;
}
}
else // if we just spawned the DCRA, it is not yet the primary one and this check makes no sense
{
if (!DCRA->IsPrimaryRootActor())
{
UE_LOGFMT(Toolkit, Error, "Found DCRA {Name} is not the primary DCRA of Cluster with Id!",
DCRA->GetName());
return false;
}
}
bool bAttached = DCRA->AttachToActor(this, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
UE_LOGFMT(Toolkit, Display, "Attaching DCRA {DCRA} to {this} returned {Res}", DCRA->GetName(), GetName(),
bAttached);
DCRA->SetActorEnableCollision(false);
}
return true;
}
ADisplayClusterRootActor* AClusterRepresentationActor::SpawnDCRA()
{
UDisplayClusterConfigurationData* ConfigData = IDisplayCluster::Get().GetConfigMgr()->GetConfig();
// Function similar to the one from DisplayClusterGameManager.
ADisplayClusterRootActor* RootActor = nullptr;
// We need to use generated class as it's the only one available in packaged buidls
const FString AssetPath =
(ConfigData->Info.AssetPath.EndsWith(TEXT("_C")) ? ConfigData->Info.AssetPath
: ConfigData->Info.AssetPath + TEXT("_C"));
if (UClass* ActorClass = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), nullptr, *AssetPath)))
{
// Spawn the actor
if (AActor* NewActor = GetWorld()->SpawnActor<AActor>(ActorClass))
{
RootActor = Cast<ADisplayClusterRootActor>(NewActor);
if (RootActor)
{
UE_LOG(Toolkit, Log, TEXT("Instantiated DCRA from asset '%s'"), *ConfigData->Info.AssetPath);
// Override actor settings in case the config file contains some updates
RootActor->OverrideFromConfig(ConfigData);
}
}
}
return RootActor;
}
#endif
......@@ -8,8 +8,8 @@
#include "ILiveLinkClient.h"
#include "InputMappingContext.h"
#include "Core/RWTHVRPlayerState.h"
#include "Kismet/GameplayStatics.h"
#include "Logging/StructuredLog.h"
#include "Pawn/ClusterRepresentationActor.h"
#include "Pawn/InputExtensionInterface.h"
#include "Pawn/Navigation/CollisionHandlingMovement.h"
#include "Pawn/ReplicatedCameraComponent.h"
......@@ -61,7 +61,7 @@ void ARWTHVRPawn::Tick(float DeltaSeconds)
* as connections now send their playertype over.
*/
// This pawn's controller has changed! This is called on both server and owning client. If we are the owning client
// and the master, request that the DCRA is attached to us.
// and the master, request that the Cluster is attached to us.
void ARWTHVRPawn::NotifyControllerChanged()
{
Super::NotifyControllerChanged();
......@@ -71,17 +71,17 @@ void ARWTHVRPawn::NotifyControllerChanged()
if (HasAuthority())
{
UE_LOG(Toolkit, Display,
TEXT("ARWTHVRPawn: Player Controller has changed, trying to change DCRA attachment if possible..."));
TEXT("ARWTHVRPawn: Player Controller has changed, trying to change Cluster attachment if possible..."));
if (const ARWTHVRPlayerState* State = GetPlayerState<ARWTHVRPlayerState>())
{
const EPlayerType Type = State->GetPlayerType();
// Only cluster types are valid here as they are set on connection.
// For all other player types this is a race condition
if (Type == EPlayerType::nDisplayPrimary || GetNetMode() == NM_Standalone)
if (Type == EPlayerType::nDisplayPrimary || Type == EPlayerType::nDisplaySecondary)
{
UE_LOGFMT(Toolkit, Display, "ARWTHVRPawn: Attaching DCRA to Pawn {Pawn}.", GetName());
AttachDCRAtoPawn();
UE_LOGFMT(Toolkit, Display, "ARWTHVRPawn: Attaching Cluster to Pawn {Pawn}.", GetName());
AttachClustertoPawn();
}
}
}
......@@ -259,35 +259,35 @@ void ARWTHVRPawn::MulticastAddDCSyncComponent_Implementation()
#endif
}
// Executed on the server only: Finds and attaches the CaveSetup Actor, which contains the DCRA to the Pawn.
// Executed on the server only: Attaches the ClusterRepresentation 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.
void ARWTHVRPawn::AttachDCRAtoPawn()
void ARWTHVRPawn::AttachClustertoPawn()
{
if (!CaveSetupActorClass || !CaveSetupActorClass->IsValidLowLevelFast())
if (const ARWTHVRPlayerState* State = GetPlayerState<ARWTHVRPlayerState>())
{
UE_LOGFMT(Toolkit, Warning, "No CaveSetup Actor class set in pawn!");
return;
}
// Find our CaveSetup actor
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), CaveSetupActorClass, FoundActors);
if (!FoundActors.IsEmpty())
{
const auto CaveSetupActor = FoundActors[0];
FAttachmentTransformRules AttachmentRules = FAttachmentTransformRules::SnapToTargetNotIncludingScale;
CaveSetupActor->AttachToActor(this, AttachmentRules);
UE_LOGFMT(Toolkit, Display, "VirtualRealityPawn: Attaching CaveSetup to our pawn!");
const auto ClusterActor = State->GetCorrespondingClusterActor();
if (!ClusterActor)
{
UE_LOGFMT(
Toolkit, Error,
"ARWTHVRPawn::AttachClustertoPawn: GetCorrespondingClusterActor returned null! This won't work on "
"the Cave.");
return;
}
const FAttachmentTransformRules AttachmentRules = FAttachmentTransformRules::SnapToTargetNotIncludingScale;
bool bAttached = ClusterActor->AttachToComponent(GetRootComponent(), AttachmentRules);
// State->GetCorrespondingClusterActor()->OnAttached();
UE_LOGFMT(Toolkit, Display,
"ARWTHVRPawn: Attaching corresponding cluster actor to our pawn returned: {Attached}", bAttached);
}
else
{
UE_LOGFMT(Toolkit, Warning,
"No CaveSetup Actor found which can be attached to the Pawn! This won't work on the Cave.");
UE_LOGFMT(Toolkit, Error,
"ARWTHVRPawn::AttachClustertoPawn: No ARWTHVRPlayerState set! This won't work on the Cave.");
}
// if (HasAuthority()) // Should always be the case here, but double check
// MulticastAddDCSyncComponent();
if (HasAuthority()) // Should always be the case here, but double check
MulticastAddDCSyncComponent();
}
void ARWTHVRPawn::SetupMotionControllerSources()
......
......@@ -4,6 +4,7 @@
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Pawn/ClusterRepresentationActor.h"
#include "RWTHVRGameModeBase.generated.h"
/**
......@@ -15,6 +16,8 @@ UCLASS()
class RWTHVRTOOLKIT_API ARWTHVRGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
ARWTHVRGameModeBase(const FObjectInitializer& ObjectInitializer);
protected:
/**
......@@ -29,4 +32,8 @@ protected:
* possess. If not, spawn a DefaultPawnClass Pawn and Possess it (Should be BP_VirtualRealityPawn to make sense).
*/
virtual void PostLogin(APlayerController* NewPlayer) override;
private:
UPROPERTY()
TMap<int32, AClusterRepresentationActor*> ConnectedClusters;
};
......@@ -5,8 +5,13 @@
#include "CoreMinimal.h"
#include "PlayerType.h"
#include "GameFramework/PlayerState.h"
#include "Logging/StructuredLog.h"
#include "Pawn/ClusterRepresentationActor.h"
#include "Utility/RWTHVRUtilities.h"
#include "RWTHVRPlayerState.generated.h"
class AClusterRepresentationActor;
enum class EPlayerType : uint8;
/**
* Extension of the PlayerState that additionally holds information about what type the player is.
......@@ -22,6 +27,26 @@ private:
UPROPERTY(Replicated, Category = PlayerState, BlueprintGetter = GetPlayerType, meta = (AllowPrivateAccess))
EPlayerType PlayerType = EPlayerType::Desktop;
/** Replicated cluster ID for this player. Is -1 in case player is not a cluster*/
UPROPERTY(Replicated, Category = PlayerState, BlueprintGetter = GetCorrespondingClusterId,
meta = (AllowPrivateAccess))
int32 CorrespondingClusterId = -1;
/** Replicated cluster actor for this player. Is nullptr in case player is not a cluster.
* As this is not guaranteed to be valid on BeginPlay, we need to do a callback to the CorrespondingClusterActor
* here...
*/
UPROPERTY(ReplicatedUsing = OnRep_CorrespondingClusterActor)
TObjectPtr<AClusterRepresentationActor> CorrespondingClusterActor;
UFUNCTION()
virtual void OnRep_CorrespondingClusterActor()
{
// Only execute this on the local owning player
if (HasLocalNetOwner())
CorrespondingClusterActor->AttachDCRAIfRequired(this);
}
UFUNCTION(Reliable, Server)
void ServerSetPlayerTypeRpc(EPlayerType NewPlayerType);
......@@ -31,6 +56,15 @@ public:
UFUNCTION(BlueprintGetter)
EPlayerType GetPlayerType() const { return PlayerType; }
UFUNCTION(BlueprintGetter)
int32 GetCorrespondingClusterId() const { return CorrespondingClusterId; }
UFUNCTION(BlueprintGetter)
AClusterRepresentationActor* GetCorrespondingClusterActor() const { return CorrespondingClusterActor; }
void SetCorrespondingClusterId(int32 NewCorrespondingClusterId);
void SetCorrespondingClusterActor(AClusterRepresentationActor* NewCorrespondingClusterActor);
UFUNCTION(BlueprintCallable)
void RequestSetPlayerType(EPlayerType NewPlayerType);
......
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "ClusterRepresentationActor.generated.h"
class ARWTHVRPlayerState;
#if PLATFORM_SUPPORTS_CLUSTER
class ADisplayClusterRootActor;
#endif
UCLASS()
class RWTHVRTOOLKIT_API AClusterRepresentationActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AClusterRepresentationActor();
virtual void BeginPlay() override;
void AttachDCRAIfRequired(const ARWTHVRPlayerState* OptionalPlayerState = nullptr);
private:
bool bIsAttached = false;
#if PLATFORM_SUPPORTS_CLUSTER
bool AttachDCRA();
ADisplayClusterRootActor* SpawnDCRA();
#endif
};
......@@ -79,10 +79,6 @@ public:
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Pawn|LiveLink")
bool bWorldTransform = false;
/* The class which to search for DCRA attachment. TODO: Make this better it's ugly */
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Pawn|LiveLink")
TSubclassOf<AActor> CaveSetupActorClass;
protected:
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
void AddInputMappingContext(const APlayerController* PC, const UInputMappingContext* Context) const;
......@@ -107,8 +103,8 @@ protected:
UFUNCTION(Reliable, NetMulticast)
void MulticastAddDCSyncComponent();
/* Attaches the DCRA to the pawn */
void AttachDCRAtoPawn();
/* Attaches the Cluster representation to the pawn */
void AttachClustertoPawn();
/* Set device specific motion controller sources (None, L/R, Livelink) */
void SetupMotionControllerSources();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment