diff --git a/Source/DisplayClusterExtensions/Private/VirtualRealityPawn.cpp b/Source/DisplayClusterExtensions/Private/VirtualRealityPawn.cpp
index 1c22a257e1704155943a67f47d1afe8a2dc957c9..c9c719b38331a72d7f8f1bcadd57b4f2dc95c924 100644
--- a/Source/DisplayClusterExtensions/Private/VirtualRealityPawn.cpp
+++ b/Source/DisplayClusterExtensions/Private/VirtualRealityPawn.cpp
@@ -1,6 +1,8 @@
 #include "VirtualRealityPawn.h"
 
+#include "Camera/CameraComponent.h"
 #include "Cluster/IDisplayClusterClusterManager.h"
+#include "Engine/Engine.h"
 #include "Engine/World.h"
 #include "Game/IDisplayClusterGameManager.h"
 #include "GameFramework/InputSettings.h"
@@ -8,18 +10,14 @@
 #include "Kismet/GameplayStatics.h"
 #include "DisplayClusterSettings.h"
 #include "IDisplayCluster.h"
-
-#include "IDisplayClusterConfigManager.h"
-#include "IXRTrackingSystem.h"
-#include "Engine/Engine.h"
-#include "Camera/CameraComponent.h"
+#include "VirtualRealityUtilities.h"
 
 AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
 {
 	bUseControllerRotationYaw = true;
 	bUseControllerRotationPitch = true;
 	bUseControllerRotationRoll = true;
-	
+
 	AutoPossessPlayer = EAutoReceiveInput::Player0; // Necessary for receiving motion controller events.
 
 	Movement = CreateDefaultSubobject<UFloatingPawnMovement>(TEXT("Movement"));
@@ -31,6 +29,10 @@ AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitial
 	RotatingMovement->PivotTranslation = FVector::ZeroVector;
 	RotatingMovement->RotationRate = FRotator::ZeroRotator;
 
+	Head = CreateDefaultSubobject<USceneComponent>(TEXT("Head"));
+	RightHand = CreateDefaultSubobject<USceneComponent>(TEXT("RightHand"));
+	LeftHand = CreateDefaultSubobject<USceneComponent>(TEXT("LeftHand"));
+
 	HmdLeftMotionController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("HmdLeftMotionController"));
 	HmdLeftMotionController->SetupAttachment(RootComponent);
 	HmdLeftMotionController->SetTrackingSource(EControllerHand::Left);
@@ -47,7 +49,7 @@ AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitial
 void AVirtualRealityPawn::OnForward_Implementation(float Value)
 {
 	// Check if this function triggers correctly on ROLV.
-	if (RightHand && (NavigationMode == EVRNavigationModes::nav_mode_fly || IsDesktopMode() || IsHeadMountedMode()))
+	if (RightHand && (NavigationMode == EVRNavigationModes::nav_mode_fly || UVirtualRealityUtilities::IsDesktopMode() || UVirtualRealityUtilities::IsHeadMountedMode()))
 	{
 		AddMovementInput(RightHand->GetForwardVector(), Value);
 	}
@@ -55,7 +57,7 @@ void AVirtualRealityPawn::OnForward_Implementation(float Value)
 
 void AVirtualRealityPawn::OnRight_Implementation(float Value)
 {
-	if (RightHand && (NavigationMode == EVRNavigationModes::nav_mode_fly || IsDesktopMode() || IsHeadMountedMode()))
+	if (RightHand && (NavigationMode == EVRNavigationModes::nav_mode_fly || UVirtualRealityUtilities::IsDesktopMode() || UVirtualRealityUtilities::IsHeadMountedMode()))
 	{
 		AddMovementInput(RightHand->GetRightVector(), Value);
 	}
@@ -81,7 +83,7 @@ void AVirtualRealityPawn::OnTurnRate_Implementation(float Rate)
 
 void AVirtualRealityPawn::OnLookUpRate_Implementation(float Rate)
 {
-	if (IsRoomMountedMode())
+	if (UVirtualRealityUtilities::IsRoomMountedMode())
 	{
 		// User-centered projection causes simulation sickness on look up interaction hence not implemented.
 	}
@@ -99,31 +101,6 @@ void AVirtualRealityPawn::OnAction_Implementation(bool Pressed, int32 Index)
 {
 }
 
-bool AVirtualRealityPawn::IsDesktopMode()
-{
-	return !IsRoomMountedMode() && !IsHeadMountedMode();
-}
-
-bool AVirtualRealityPawn::IsRoomMountedMode()
-{
-	return IDisplayCluster::Get().GetOperationMode() == EDisplayClusterOperationMode::Cluster;
-}
-
-bool AVirtualRealityPawn::IsHeadMountedMode()
-{
-	return GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed();
-}
-
-FString AVirtualRealityPawn::GetNodeName()
-{
-	return IsRoomMountedMode() ? IDisplayCluster::Get().GetClusterMgr()->GetNodeId() : FString(TEXT("Localhost"));
-}
-
-float AVirtualRealityPawn::GetEyeDistance()
-{
-	return IDisplayCluster::Get().GetConfigMgr()->GetConfigStereo().EyeDist;
-}
-
 float AVirtualRealityPawn::GetBaseTurnRate() const
 {
 	return BaseTurnRate;
@@ -149,6 +126,16 @@ UDisplayClusterSceneComponent* AVirtualRealityPawn::GetFlystickComponent()
 	return Flystick;
 }
 
+UDisplayClusterSceneComponent* AVirtualRealityPawn::GetRightHandtargetComponent()
+{
+	return RightHandTarget;
+}
+
+UDisplayClusterSceneComponent* AVirtualRealityPawn::GetLeftHandtargetComponent()
+{
+	return LeftHandTarget;
+}
+
 UMotionControllerComponent* AVirtualRealityPawn::GetHmdLeftMotionControllerComponent()
 {
 	return HmdLeftMotionController;
@@ -174,9 +161,9 @@ USceneComponent* AVirtualRealityPawn::GetRightHandComponent()
 	return RightHand;
 }
 
-USceneComponent* AVirtualRealityPawn::GetCaveOriginComponent()
+USceneComponent* AVirtualRealityPawn::GetTrackingOriginComponent()
 {
-	return CaveOrigin;
+	return TrackingOrigin;
 }
 
 USceneComponent* AVirtualRealityPawn::GetCaveCenterComponent()
@@ -189,9 +176,22 @@ USceneComponent* AVirtualRealityPawn::GetShutterGlassesComponent()
 	return ShutterGlasses;
 }
 
-UDisplayClusterSceneComponent* AVirtualRealityPawn::GetClusterComponent(const FString& Name)
+void AVirtualRealityPawn::ClusterExecute(const FString& Command)
+{
+  FDisplayClusterClusterEvent event;
+  event.Name = "NDisplayCMD: " + Command;
+  event.Type = "NDisplayCMD";
+  event.Category = "VRPawn";
+  event.Parameters.Add("Command", Command);
+  IDisplayCluster::Get().GetClusterMgr()->EmitClusterEvent(event, false);
+}
+
+void AVirtualRealityPawn::HandleClusterEvent(const FDisplayClusterClusterEvent& Event)
 {
-	return IDisplayCluster::Get().GetGameMgr()->GetNodeById(Name);
+	if (Event.Category.Equals("VRPawn") && Event.Type.Equals("NDisplayCMD") && Event.Parameters.Contains("Command"))
+	{
+		GEngine->Exec(GetWorld(), *Event.Parameters["Command"]);
+	}
 }
 
 void AVirtualRealityPawn::BeginPlay()
@@ -211,41 +211,76 @@ void AVirtualRealityPawn::BeginPlay()
 		BaseTurnRate = Settings->RotationSpeed;
 	}
 
-	if (IsRoomMountedMode())
+	if (UVirtualRealityUtilities::IsRoomMountedMode())
 	{
 		UInputSettings::GetInputSettings()->RemoveAxisMapping(FInputAxisKeyMapping("TurnRate", EKeys::MouseX));
 		UInputSettings::GetInputSettings()->RemoveAxisMapping(FInputAxisKeyMapping("LookUpRate", EKeys::MouseY));
 
-		InitComponentReferences();
-
-		RootComponent->SetWorldLocation(FVector(0, 2, 0), false, nullptr, ETeleportType::None);
+		InitRoomMountedComponentReferences();
 	}
-	else if (IsHeadMountedMode())
+	else if (UVirtualRealityUtilities::IsHeadMountedMode())
 	{
 		UInputSettings::GetInputSettings()->RemoveAxisMapping(FInputAxisKeyMapping("TurnRate", EKeys::MouseX));
 		UInputSettings::GetInputSettings()->RemoveAxisMapping(FInputAxisKeyMapping("LookUpRate", EKeys::MouseY));
 
-		HmdLeftMotionController->SetVisibility(true);
-		HmdRightMotionController->SetVisibility(true);
+		HmdLeftMotionController->SetVisibility(ShowHMDControllers);
+		HmdRightMotionController->SetVisibility(ShowHMDControllers);
 
-		LeftHand = HmdLeftMotionController;
-		RightHand = HmdRightMotionController;
-		Head = GetCameraComponent();
+		LeftHand->AttachToComponent(HmdLeftMotionController, FAttachmentTransformRules::KeepRelativeTransform);
+		RightHand->AttachToComponent(HmdRightMotionController, FAttachmentTransformRules::KeepRelativeTransform);
+		Head->AttachToComponent(GetCameraComponent(), FAttachmentTransformRules::KeepRelativeTransform);
 	}
 	else //Desktop
 	{
-		LeftHand = RootComponent;
-		RightHand = RootComponent;
-		Head = GetCameraComponent();
+		Head->AttachToComponent(GetCameraComponent(), FAttachmentTransformRules::KeepRelativeTransform);
+
+		//also attach the hands to the camera component so we can use them for interaction
+		LeftHand->AttachToComponent(GetCameraComponent(), FAttachmentTransformRules::KeepRelativeTransform);
+		RightHand->AttachToComponent(GetCameraComponent(), FAttachmentTransformRules::KeepRelativeTransform);
+
+
+		//move to eyelevel
+		GetCameraComponent()->SetRelativeLocation(FVector(0, 0, 160));
+	}
+
+	//In ADisplayClusterPawn::BeginPlay() input is disabled on all slaves, so we cannot react to button presses, e.g. on the flystick correctly.
+	//Therefore, we activate it again:
+	UWorld* World = GetWorld();
+	if (World)
+	{
+		APlayerController* PlayerController = World->GetFirstPlayerController();
+		if (PlayerController)
+		{
+			this->EnableInput(PlayerController);
+		}
+	}
+
+	// Register cluster event listeners
+	IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
+	if (ClusterManager && !ClusterEventListenerDelegate.IsBound())
+	{
+		ClusterEventListenerDelegate = FOnClusterEventListener::CreateUObject(this, &AVirtualRealityPawn::HandleClusterEvent);
+		ClusterManager->AddClusterEventListener(ClusterEventListenerDelegate);
 	}
 }
 
+void AVirtualRealityPawn::EndPlay(const EEndPlayReason::Type EndPlayReason)
+{
+	IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
+	if (ClusterManager && ClusterEventListenerDelegate.IsBound())
+	{
+		ClusterManager->RemoveClusterEventListener(ClusterEventListenerDelegate);
+	}
+
+	Super::EndPlay(EndPlayReason);
+}
+
 void AVirtualRealityPawn::Tick(float DeltaSeconds)
 {
 	Super::Tick(DeltaSeconds);
 
 	//Flystick might not be available at start, hence is checked every frame.
-	InitComponentReferences();
+	InitRoomMountedComponentReferences();
 }
 
 void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
@@ -265,47 +300,37 @@ UPawnMovementComponent* AVirtualRealityPawn::GetMovementComponent() const
 	return Movement;
 }
 
-void AVirtualRealityPawn::InitComponentReferences()
+void AVirtualRealityPawn::InitRoomMountedComponentReferences()
 {
-	if (!IsRoomMountedMode()) return;
-	if (!CaveOrigin) CaveOrigin = GetClusterComponent("cave_origin");
-	if (!CaveCenter) CaveCenter = GetClusterComponent("cave_center");
+	if (!UVirtualRealityUtilities::IsRoomMountedMode()) return;
+
+	//check whether the nodes already exist (otherwise GetClusterComponent() returns nullptr and prints a warning) and assign them
+	if (!TrackingOrigin) TrackingOrigin = UVirtualRealityUtilities::GetClusterComponent("cave_origin");
+	if (!TrackingOrigin) TrackingOrigin = UVirtualRealityUtilities::GetClusterComponent("rolv_origin");
+	if (!CaveCenter) CaveCenter = UVirtualRealityUtilities::GetClusterComponent("cave_center");
 	if (!ShutterGlasses)
 	{
-		ShutterGlasses = GetClusterComponent("shutter_glasses");
-		Head = ShutterGlasses;
+		ShutterGlasses = UVirtualRealityUtilities::GetClusterComponent("shutter_glasses");
+		Head->AttachToComponent(ShutterGlasses, FAttachmentTransformRules::KeepRelativeTransform);
 	}
 	if (!Flystick)
 	{
-		Flystick = GetClusterComponent("flystick");
-
-		LeftHand = Flystick;
-		RightHand = Flystick;
+		Flystick = UVirtualRealityUtilities::GetClusterComponent("flystick");
+		if (AttachRightHandInCAVE == EAttachementType::AT_FLYSTICK)
+			RightHand->AttachToComponent(Flystick, FAttachmentTransformRules::KeepRelativeTransform);
+		if (AttachLeftHandInCAVE == EAttachementType::AT_FLYSTICK)
+			LeftHand->AttachToComponent(Flystick, FAttachmentTransformRules::KeepRelativeTransform);
 	}
-}
-
-EEyeType AVirtualRealityPawn::GetNodeEyeType() {
-	FDisplayClusterConfigClusterNode CurrentNodeConfig;
-	IDisplayCluster::Get().GetConfigMgr()->GetClusterNode(GetNodeName(), CurrentNodeConfig);
-
-	FString s = CurrentNodeConfig.ToString();
-
-	if (s.Contains("mono_eye")) {
-		TArray<FString> stringArray;
-		int32 count = s.ParseIntoArray(stringArray, TEXT(","));
-		for (int x = 0; x < count; x++) {
-			if (!stringArray[x].Contains("mono_eye")) continue;
-			if (stringArray[x].Contains("left")) {
-				return EEyeType::ET_STEREO_LEFT;
-			}
-			if (stringArray[x].Contains("right")) {
-				return EEyeType::ET_STEREO_RIGHT;
-			}
-		}
+	if (!LeftHandTarget)
+	{
+		LeftHandTarget = UVirtualRealityUtilities::GetClusterComponent("left_hand_target");
+		if (AttachLeftHandInCAVE == EAttachementType::AT_HANDTARGET)
+			LeftHand->AttachToComponent(LeftHandTarget, FAttachmentTransformRules::KeepRelativeTransform);
 	}
-	else {
-		return EEyeType::ET_MONO;
+	if (!RightHandTarget)
+	{
+		RightHandTarget = UVirtualRealityUtilities::GetClusterComponent("right_hand_target");
+		if (AttachRightHandInCAVE == EAttachementType::AT_HANDTARGET)
+			RightHand->AttachToComponent(RightHandTarget, FAttachmentTransformRules::KeepRelativeTransform);
 	}
-	return EEyeType::ET_MONO;
 }
-
diff --git a/Source/DisplayClusterExtensions/Private/VirtualRealityUtilities.cpp b/Source/DisplayClusterExtensions/Private/VirtualRealityUtilities.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..30a85dd360f560bdc7772418b0e3a653b2a2dfb8
--- /dev/null
+++ b/Source/DisplayClusterExtensions/Private/VirtualRealityUtilities.cpp
@@ -0,0 +1,66 @@
+#include "VirtualRealityUtilities.h"
+
+#include "Cluster/IDisplayClusterClusterManager.h"
+#include "Engine/Engine.h"
+#include "Game/IDisplayClusterGameManager.h"
+#include "IDisplayCluster.h"
+#include "IDisplayClusterConfigManager.h"
+#include "IXRTrackingSystem.h"
+
+bool UVirtualRealityUtilities::IsDesktopMode()
+{
+	return !IsRoomMountedMode() && !IsHeadMountedMode();
+}
+bool UVirtualRealityUtilities::IsRoomMountedMode()
+{
+	return IDisplayCluster::Get().GetOperationMode() == EDisplayClusterOperationMode::Cluster;
+}
+bool UVirtualRealityUtilities::IsHeadMountedMode()
+{
+	return GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed();
+}
+
+FString UVirtualRealityUtilities::GetNodeName()
+{
+	return IsRoomMountedMode() ? IDisplayCluster::Get().GetClusterMgr()->GetNodeId() : FString(TEXT("Localhost"));
+}
+float UVirtualRealityUtilities::GetEyeDistance()
+{
+	return IDisplayCluster::Get().GetConfigMgr()->GetConfigStereo().EyeDist;
+}
+
+EEyeType UVirtualRealityUtilities::GetNodeEyeType()
+{
+	FDisplayClusterConfigClusterNode CurrentNodeConfig;
+	IDisplayCluster::Get().GetConfigMgr()->GetClusterNode(GetNodeName(), CurrentNodeConfig);
+
+	FString s = CurrentNodeConfig.ToString();
+
+	if (s.Contains("mono_eye"))
+	{
+		TArray<FString> stringArray;
+		int32 count = s.ParseIntoArray(stringArray, TEXT(","));
+		for (int x = 0; x < count; x++)
+		{
+			if (!stringArray[x].Contains("mono_eye")) continue;
+			if (stringArray[x].Contains("left"))
+			{
+				return EEyeType::ET_STEREO_LEFT;
+			}
+			if (stringArray[x].Contains("right"))
+			{
+				return EEyeType::ET_STEREO_RIGHT;
+			}
+		}
+	}
+	else
+	{
+		return EEyeType::ET_MONO;
+	}
+	return EEyeType::ET_MONO;
+}
+
+UDisplayClusterSceneComponent* UVirtualRealityUtilities::GetClusterComponent(const FString& Name)
+{
+	return IDisplayCluster::Get().GetGameMgr()->GetNodeById(Name);
+}
diff --git a/Source/DisplayClusterExtensions/Public/VirtualRealityPawn.h b/Source/DisplayClusterExtensions/Public/VirtualRealityPawn.h
index 8267bddb37c216099c4da8cd71e242e605684b7e..d5b3b4c8db035bbd1e0095f25d570de5940d0620 100644
--- a/Source/DisplayClusterExtensions/Public/VirtualRealityPawn.h
+++ b/Source/DisplayClusterExtensions/Public/VirtualRealityPawn.h
@@ -1,106 +1,119 @@
 #pragma once
 
-#include "GameFramework/FloatingPawnMovement.h"
-#include "GameFramework/PawnMovementComponent.h"
-#include "GameFramework/RotatingMovementComponent.h"
 #include "CoreMinimal.h"
+#include "Cluster/DisplayClusterClusterEvent.h"
+#include "Cluster/IDisplayClusterClusterManager.h"
 #include "DisplayClusterPawn.h"
 #include "DisplayClusterSceneComponent.h"
+#include "GameFramework/FloatingPawnMovement.h"
+#include "GameFramework/PawnMovementComponent.h"
+#include "GameFramework/RotatingMovementComponent.h"
 #include "MotionControllerComponent.h"
 #include "VirtualRealityPawn.generated.h"
 
 UENUM(BlueprintType)
-enum class EVRNavigationModes : uint8{
-  nav_mode_none UMETA(DisplayName = "Navigation Mode None"),
-  nav_mode_fly UMETA(DisplayName = "Navigation Mode Fly")
+enum class EVRNavigationModes : uint8
+{
+	nav_mode_none UMETA(DisplayName = "Navigation Mode None"),
+	nav_mode_fly UMETA(DisplayName = "Navigation Mode Fly")
 };
+
 UENUM(BlueprintType)
-enum class EEyeType : uint8 {
-	ET_MONO 			UMETA(DisplayName = "mono"),
-	ET_STEREO_RIGHT 	UMETA(DisplayName = "stero_right"),
-	ET_STEREO_LEFT		UMETA(DisplayName = "stereo_left")
+enum class EAttachementType : uint8
+{
+	AT_NONE UMETA(DisplayName = "not attached"),
+	AT_HANDTARGET UMETA(DisplayName = "to the right/left hand target"),
+	AT_FLYSTICK UMETA(DisplayName = "to the Flystick")
 };
 
 UCLASS()
 class DISPLAYCLUSTEREXTENSIONS_API AVirtualRealityPawn : public ADisplayClusterPawn
 {
-  GENERATED_UCLASS_BODY()
+	GENERATED_UCLASS_BODY()
 
 public:
-  UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnForward   (float Value  );
-  UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnRight     (float Value  );
-  UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnTurnRate  (float Rate   );
-  UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnLookUpRate(float Rate   );
-  UFUNCTION(BlueprintNativeEvent, BlueprintCallable, BlueprintCallable, Category = "Pawn") void OnFire(bool Pressed);
-  UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnAction(bool Pressed, int32 Index);
-
-  UFUNCTION(BlueprintPure, Category = "Pawn") static bool IsDesktopMode();
-  UFUNCTION(BlueprintPure, Category = "Pawn") static bool IsRoomMountedMode();
-  UFUNCTION(BlueprintPure, Category = "Pawn") static bool IsHeadMountedMode();
-
-  UFUNCTION(BlueprintPure, Category = "Pawn") static FString GetNodeName();
-  UFUNCTION(BlueprintPure, Category = "Pawn") static float GetEyeDistance();
-
-  UFUNCTION(BlueprintPure, Category = "Pawn") static EEyeType GetNodeEyeType();
-
-  UFUNCTION(Category = "Pawn") float GetBaseTurnRate() const;
-  UFUNCTION(Category = "Pawn") void SetBaseTurnRate(float Value);
-  UFUNCTION(Category = "Pawn") UFloatingPawnMovement* GetFloatingPawnMovement();
-  UFUNCTION(Category = "Pawn") URotatingMovementComponent* GetRotatingMovementComponent();
-
-  //Bunch of Getter Functions for components to avoid users having to know the names
-
-  UFUNCTION(Category = "Pawn") UDisplayClusterSceneComponent* GetFlystickComponent();
-  UFUNCTION(Category = "Pawn") UMotionControllerComponent* GetHmdLeftMotionControllerComponent();
-  UFUNCTION(Category = "Pawn") UMotionControllerComponent* GetHmdRightMotionControllerComponent();
+	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnForward(float Value);
+	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnRight(float Value);
+	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnTurnRate(float Rate);
+	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnLookUpRate(float Rate);
+	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, BlueprintCallable, Category = "Pawn") void OnFire(bool Pressed);
+	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnAction(bool Pressed, int32 Index);
+
+	UFUNCTION(Category = "Pawn") float GetBaseTurnRate() const;
+	UFUNCTION(Category = "Pawn") void SetBaseTurnRate(float Value);
+	UFUNCTION(Category = "Pawn") UFloatingPawnMovement* GetFloatingPawnMovement();
+	UFUNCTION(Category = "Pawn") URotatingMovementComponent* GetRotatingMovementComponent();
+
+	//Bunch of Getter Functions for components to avoid users having to know the names
+	UFUNCTION(Category = "Pawn") UDisplayClusterSceneComponent* GetFlystickComponent();
+	UFUNCTION(Category = "Pawn") UDisplayClusterSceneComponent* GetRightHandtargetComponent();
+	UFUNCTION(Category = "Pawn") UDisplayClusterSceneComponent* GetLeftHandtargetComponent();
+	UFUNCTION(Category = "Pawn") UMotionControllerComponent* GetHmdLeftMotionControllerComponent();
+	UFUNCTION(Category = "Pawn") UMotionControllerComponent* GetHmdRightMotionControllerComponent();
+
+	UFUNCTION(Category = "Pawn") USceneComponent* GetHeadComponent();
+	UFUNCTION(Category = "Pawn") USceneComponent* GetLeftHandComponent();
+	UFUNCTION(Category = "Pawn") USceneComponent* GetRightHandComponent();
+
+	UFUNCTION(Category = "Pawn") USceneComponent* GetTrackingOriginComponent();
+private:
+	UFUNCTION(Category = "Pawn") USceneComponent* GetCaveCenterComponent();
+	UFUNCTION(Category = "Pawn") USceneComponent* GetShutterGlassesComponent();
 
-  UFUNCTION(Category = "Pawn") USceneComponent* GetHeadComponent();
-  UFUNCTION(Category = "Pawn") USceneComponent* GetLeftHandComponent();
-  UFUNCTION(Category = "Pawn") USceneComponent* GetRightHandComponent();
+public:
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") EVRNavigationModes NavigationMode = EVRNavigationModes::nav_mode_fly;
 
-  UFUNCTION(Category = "Pawn") USceneComponent* GetCaveOriginComponent();
-  UFUNCTION(Category = "Pawn") USceneComponent* GetCaveCenterComponent();
-  UFUNCTION(Category = "Pawn") USceneComponent* GetShutterGlassesComponent();
+  //Execute specified console command on all nDisplayCluster Nodes
+  UFUNCTION(Exec, BlueprintCallable, Category = "DisplayCluster") static void ClusterExecute(const FString& Command);
 
-  //Get Compenent of Display Cluster by it's name, which is specified in the nDisplay config
-  UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Pawn") static UDisplayClusterSceneComponent* GetClusterComponent(const FString& Name);
-   
-  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") EVRNavigationModes NavigationMode = EVRNavigationModes::nav_mode_fly;
+private:
+	FOnClusterEventListener ClusterEventListenerDelegate;
+	UFUNCTION() void HandleClusterEvent(const FDisplayClusterClusterEvent& Event);
 
 protected:
 	DECLARE_DELEGATE_OneParam(FFireDelegate, bool);
 	DECLARE_DELEGATE_TwoParams(FActionDelegate, bool, int32);
 
-  virtual void                    BeginPlay                ()                                            override;
-  virtual void                    Tick                     (float            DeltaSeconds        )       override;
-  virtual void                    SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)       override;
-  virtual UPawnMovementComponent* GetMovementComponent     ()                                      const override;
-  
-  UPROPERTY(EditAnywhere   , BlueprintReadWrite, Category = "Pawn", meta = (AllowPrivateAccess = "true")) float                          BaseTurnRate          = 45.0f  ;
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly , Category = "Pawn", meta = (AllowPrivateAccess = "true")) UFloatingPawnMovement*         Movement              = nullptr;
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly , Category = "Pawn", meta = (AllowPrivateAccess = "true")) URotatingMovementComponent*    RotatingMovement      = nullptr;
-  
-  // Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. CAVE/ROLV flystick.
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly , Category = "Pawn", meta = (AllowPrivateAccess = "true")) UDisplayClusterSceneComponent* Flystick              = nullptr;
-  // Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. HMD left  motion controller.
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly , Category = "Pawn", meta = (AllowPrivateAccess = "true")) UMotionControllerComponent*    HmdLeftMotionController  = nullptr;
-  // Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. HMD right motion controller.
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly , Category = "Pawn", meta = (AllowPrivateAccess = "true")) UMotionControllerComponent*    HmdRightMotionController = nullptr;
-
-  // PC: Camera, HMD: Camera, CAVE/ROLV: Shutter glasses.
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true"))  USceneComponent*				Head						= nullptr;
-  // PC: RootComponent, HMD: HmdLeftMotionController , CAVE/ROLV: Flystick. Useful for line trace (e.g. for holding objects).
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true"))  USceneComponent*				LeftHand					= nullptr;
-  // PC: RootComponent, HMD: HmdRightMotionController, CAVE/ROLV: Flystick. Useful for line trace (e.g. for holding objects).
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true"))  USceneComponent*				RightHand					= nullptr;
-
-  // Holding the Cave Origin Component that is attached to this Pawn
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) USceneComponent*				CaveOrigin					= nullptr;
-  // Holding the Cave Center Component that is attached to this Pawn
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) USceneComponent*				CaveCenter					= nullptr;
-  // Holding the Shutter Glasses Component that is attached to this Pawn
-  UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) USceneComponent*				ShutterGlasses				= nullptr;
+	virtual void BeginPlay() override;
+	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
+	virtual void Tick(float DeltaSeconds) override;
+	virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
+	virtual UPawnMovementComponent* GetMovementComponent() const override;
+
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn", meta = (AllowPrivateAccess = "true")) float BaseTurnRate = 45.0f;
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) UFloatingPawnMovement* Movement = nullptr;
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) URotatingMovementComponent* RotatingMovement = nullptr;
+
+	// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. CAVE/ROLV flystick.
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) UDisplayClusterSceneComponent* Flystick = nullptr;
+	// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. CAVE/ROLV flystick.
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) UDisplayClusterSceneComponent* RightHandTarget = nullptr;
+	// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. CAVE/ROLV flystick.
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) UDisplayClusterSceneComponent* LeftHandTarget = nullptr;
+
+	// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. HMD left  motion controller.
+	UMotionControllerComponent* HmdLeftMotionController = nullptr;
+	// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. HMD right motion controller.
+	UMotionControllerComponent* HmdRightMotionController = nullptr;
+
+	// PC: Camera, HMD: Camera, CAVE/ROLV: Shutter glasses.
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) USceneComponent* Head = nullptr;
+	// PC: RootComponent, HMD: HmdLeftMotionController , CAVE/ROLV: regarding to AttachRightHandInCAVE. Useful for line trace (e.g. for holding objects).
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) USceneComponent* RightHand = nullptr;
+	// PC: RootComponent, HMD: HmdRightMotionController, CAVE/ROLV: regarding to AttachLeftHandInCAVE. Useful for line trace (e.g. for holding objects).
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) USceneComponent* LeftHand = nullptr;
+
+	// Holding the Cave/rolv Origin Component that is attached to this Pawn
+	UPROPERTY() USceneComponent* TrackingOrigin = nullptr;
+	// Holding the Cave Center Component that is attached to this Pawn, it is needed for the internal transform of nDisplay
+	UPROPERTY() USceneComponent* CaveCenter = nullptr;
+	// Holding the Shutter Glasses Component that is attached to this Pawn
+	UPROPERTY() USceneComponent* ShutterGlasses = nullptr;
+
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") bool ShowHMDControllers = true;
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") EAttachementType AttachRightHandInCAVE = EAttachementType::AT_FLYSTICK;
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") EAttachementType AttachLeftHandInCAVE = EAttachementType::AT_NONE;
 
 private:
-	void InitComponentReferences();
-};
+	void InitRoomMountedComponentReferences();
+};
\ No newline at end of file
diff --git a/Source/DisplayClusterExtensions/Public/VirtualRealityUtilities.h b/Source/DisplayClusterExtensions/Public/VirtualRealityUtilities.h
new file mode 100644
index 0000000000000000000000000000000000000000..1aef9acc66c039c996c28bb32a1ee830108f2730
--- /dev/null
+++ b/Source/DisplayClusterExtensions/Public/VirtualRealityUtilities.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "CoreMinimal.h"
+#include "DisplayClusterSceneComponent.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "VirtualRealityUtilities.generated.h"
+
+UENUM(BlueprintType)
+enum class EEyeType : uint8
+{
+	ET_MONO UMETA(DisplayName = "mono"),
+	ET_STEREO_RIGHT UMETA(DisplayName = "stero_right"),
+	ET_STEREO_LEFT UMETA(DisplayName = "stereo_left")
+};
+
+UCLASS()
+class DISPLAYCLUSTEREXTENSIONS_API UVirtualRealityUtilities : public UBlueprintFunctionLibrary
+{
+	GENERATED_BODY()
+
+public:
+	UFUNCTION(BlueprintPure, Category = "DisplayCluster") static bool IsDesktopMode();
+	UFUNCTION(BlueprintPure, Category = "DisplayCluster") static bool IsRoomMountedMode();
+	UFUNCTION(BlueprintPure, Category = "DisplayCluster") static bool IsHeadMountedMode();
+
+	UFUNCTION(BlueprintPure, Category = "DisplayCluster") static FString GetNodeName();
+	UFUNCTION(BlueprintPure, Category = "DisplayCluster") static float GetEyeDistance();
+
+	UFUNCTION(BlueprintPure, Category = "DisplayCluster") static EEyeType GetNodeEyeType();
+
+	//Get Compenent of Display Cluster by it's name, which is specified in the nDisplay config
+	UFUNCTION(BlueprintPure, BlueprintCallable, Category = "DisplayCluster") static UDisplayClusterSceneComponent* GetClusterComponent(const FString& Name);
+};