diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a04763c60e0f5c014a1dc04395d381df66262c53..bc970ad30f9378b3c57200a74021f755d3166261 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -80,8 +80,21 @@ Generate_Project:
     variables:
         RUN_SETUP: "false"
         GEN_DEPENDENCIES: "(
-            [master@UnrealDTrackPlugin]='https://github.com/VRGroupRWTH/UnrealDTrackPlugin.git')"
+            [master@UnrealDTrackPlugin]='https://github.com/VRGroupRWTH/UnrealDTrackPlugin.git'
+            [dev/5.3@RWTHVRCluster]='https://git-ce.rwth-aachen.de/vr-vis/VR-Group/unreal-development/plugins/rwth-vr-cluster-plugin.git'
+            )"
 
+Generate_Project_Without_Cluster:
+    extends: Generate_Project
+    variables:
+        GEN_DEPENDENCIES: "(
+            [master@UnrealDTrackPlugin]='https://github.com/VRGroupRWTH/UnrealDTrackPlugin.git'
+            )"
+    script: # if we don't do this, cooking will for some reason fail even though it should use the correct fallback
+      - !reference [Generate_Project, script]
+      - echo "Generating without cluster mode, removing nDisplay configs..."
+      - cd ${CI_PROJECT_DIR}
+      - sed -i '/DisplayCluster/d' Config/Linux/LinuxEngine.ini
 
 Build_Windows:
     rules: 
@@ -124,6 +137,14 @@ Build_Linux:
         - job: "Generate_Project"
           artifacts: true
 
+
+# Builds for linux without the cluster plugin
+Build_Linux_Without_Cluster:
+    extends: Build_Linux
+    needs:
+        - job: "Generate_Project_Without_Cluster"
+          artifacts: true
+
 # Deploys to vrdev
 Deploy_Windows:
     rules: 
diff --git a/Content/Examples/ContentExamples/ToolkitExamples.umap b/Content/Examples/ContentExamples/ToolkitExamples.umap
index c6e4c2d650345e901032eb8d009b9d65375209a0..a06be8deb09551c44e5a20209bb91bedffd98f8b 100644
Binary files a/Content/Examples/ContentExamples/ToolkitExamples.umap and b/Content/Examples/ContentExamples/ToolkitExamples.umap differ
diff --git a/Content/Pawn/Base/BP_RWTHVRPawn_Base.uasset b/Content/Pawn/Base/BP_RWTHVRPawn_Base.uasset
index 591070e44ae18b1c035eba19b12492d94c47c7e2..c56c9e266eacda82f508acf1eb330c1499818321 100644
Binary files a/Content/Pawn/Base/BP_RWTHVRPawn_Base.uasset and b/Content/Pawn/Base/BP_RWTHVRPawn_Base.uasset differ
diff --git a/Content/RWTHVRCluster/BP_CaveSetup.uasset b/Content/RWTHVRCluster/BP_CaveSetup.uasset
deleted file mode 100644
index fd882f7f9f9860373421e60fafde5f5b0c03a0cb..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/BP_CaveSetup.uasset and /dev/null differ
diff --git a/Content/RWTHVRCluster/CAVEOverlay/BP_CaveOverlay.uasset b/Content/RWTHVRCluster/CAVEOverlay/BP_CaveOverlay.uasset
deleted file mode 100644
index ab15ee9eb49fa5f0687da7bfff83330b505d4e03..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/CAVEOverlay/BP_CaveOverlay.uasset and /dev/null differ
diff --git a/Content/RWTHVRCluster/CAVEOverlay/DoorOverlay.uasset b/Content/RWTHVRCluster/CAVEOverlay/DoorOverlay.uasset
deleted file mode 100644
index 02f7992b4b6af7ca25e44a5f2f04618957fcaf28..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/CAVEOverlay/DoorOverlay.uasset and /dev/null differ
diff --git a/Content/RWTHVRCluster/CAVEOverlay/Sign.uasset b/Content/RWTHVRCluster/CAVEOverlay/Sign.uasset
deleted file mode 100644
index dba01725bd6912569f4dc77222176010a5698385..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/CAVEOverlay/Sign.uasset and /dev/null differ
diff --git a/Content/RWTHVRCluster/CAVEOverlay/StopMaterial.uasset b/Content/RWTHVRCluster/CAVEOverlay/StopMaterial.uasset
deleted file mode 100644
index 02124fe5bf6a340eba994fd314f01de1a1998b4f..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/CAVEOverlay/StopMaterial.uasset and /dev/null differ
diff --git a/Content/RWTHVRCluster/CAVEOverlay/StopSign.uasset b/Content/RWTHVRCluster/CAVEOverlay/StopSign.uasset
deleted file mode 100644
index e047f7cbd9dc8502782a3fd32df6d1ecf05ffc66..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/CAVEOverlay/StopSign.uasset and /dev/null differ
diff --git a/Content/RWTHVRCluster/CAVEOverlay/Stripes.uasset b/Content/RWTHVRCluster/CAVEOverlay/Stripes.uasset
deleted file mode 100644
index 4406d788871cb62ff42293d6f7b8fb00c942a7ff..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/CAVEOverlay/Stripes.uasset and /dev/null differ
diff --git a/Content/RWTHVRCluster/CAVEOverlay/TapeMesh.uasset b/Content/RWTHVRCluster/CAVEOverlay/TapeMesh.uasset
deleted file mode 100644
index 3cfc67f040145f93432b9c08551fbea851bb3acb..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/CAVEOverlay/TapeMesh.uasset and /dev/null differ
diff --git a/Content/RWTHVRCluster/Config/aixcave.uasset b/Content/RWTHVRCluster/Config/aixcave.uasset
deleted file mode 100644
index 52a33ae06135eef2201a8f1dc59094d3b8cbb73a..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/Config/aixcave.uasset and /dev/null differ
diff --git a/Content/RWTHVRCluster/Config/aixcave_two_player.uasset b/Content/RWTHVRCluster/Config/aixcave_two_player.uasset
deleted file mode 100644
index f55c6f86b133bd41711850009ee28765f435949c..0000000000000000000000000000000000000000
Binary files a/Content/RWTHVRCluster/Config/aixcave_two_player.uasset and /dev/null differ
diff --git a/RWTHVRToolkit.uplugin b/RWTHVRToolkit.uplugin
index 7f4a21c631b5af94e1d029792a9a56cb818fe251..4827226d981eeb2cd82d5e1593448f72f33755ff 100644
--- a/RWTHVRToolkit.uplugin
+++ b/RWTHVRToolkit.uplugin
@@ -15,15 +15,6 @@
 	"Installed": false,
 	"EnabledByDefault": true,
 	"Modules": [
-		{
-			"Name": "RWTHVRCluster",
-			"Type": "Runtime",
-			"LoadingPhase": "Default",
-			"WhitelistPlatforms": [
-				"Win64",
-				"Linux"
-			]
-		},
 		{
 			"Name": "RWTHVRToolkit",
 			"Type": "Runtime",
@@ -37,22 +28,18 @@
 	],
 	"Plugins": [
 		{
-			"Name": "nDisplay",
-			"Enabled": true
+			"Name": "RWTHVRCluster",
+			"Enabled": true,
+			"Optional": true,
+			"SupportedTargetPlatforms": [
+				"Win64",
+				"Linux"
+			]
 		},
 		{
 			"Name": "LiveLink",
 			"Enabled": true
 		},
-		{
-			"Name": "LiveLinkOvernDisplay",
-			"Enabled": true
-		},
-		{
-			"Name": "DTrackPlugin",
-			"Enabled": true,
-			"Optional": true
-		},
 		{
 			"Name": "EnhancedInput",
 			"Enabled": true
@@ -62,9 +49,8 @@
 			"Enabled": true
 		},
 		{
-			"Name": "Switchboard",
-			"Enabled": true,
-			"Optional": true
+		"Name": "XRBase",
+		"Enabled": true
 		}
 	]
 }
diff --git a/Source/RWTHVRCluster/Private/CAVEOverlay/CAVEOverlayController.cpp b/Source/RWTHVRCluster/Private/CAVEOverlay/CAVEOverlayController.cpp
deleted file mode 100644
index 08d44b21a4e33283daa9a6a29b556beec0289cfa..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Private/CAVEOverlay/CAVEOverlayController.cpp
+++ /dev/null
@@ -1,357 +0,0 @@
-#include "CAVEOverlay/CAVEOverlayController.h"
-
-#include "CoreMinimal.h"
-#include "EnhancedInputComponent.h"
-#include "IDisplayCluster.h"
-#include "MotionControllerComponent.h"
-#include "Camera/CameraComponent.h"
-#include "CAVEOverlay/DoorOverlayData.h"
-#include "Cluster/DisplayClusterClusterEvent.h"
-#include "Cluster/IDisplayClusterClusterManager.h"
-#include "Components/StaticMeshComponent.h"
-#include "Engine/CollisionProfile.h"
-#include "Logging/StructuredLog.h"
-#include "Materials/MaterialInstanceDynamic.h"
-#include "Pawn/RWTHVRPawn.h"
-#include "Utility/RWTHVRUtilities.h"
-
-
-DEFINE_LOG_CATEGORY(LogCAVEOverlay);
-
-// Helper function to check if a string is contained within an array of strings, ignoring case.
-bool ContainsFString(const TArray<FString>& Array, const FString& Entry)
-{
-	for (FString Current_Entry : Array)
-	{
-		if (Current_Entry.Equals(Entry, ESearchCase::IgnoreCase))
-			return true;
-	}
-	return false;
-}
-
-UStaticMeshComponent* ACAVEOverlayController::CreateMeshComponent(const FName& Name, USceneComponent* Parent)
-{
-	UStaticMeshComponent* Result = CreateDefaultSubobject<UStaticMeshComponent>(Name);
-	Result->SetupAttachment(Parent);
-	Result->SetVisibility(false);
-	Result->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
-	return Result;
-}
-
-// Sets default values
-ACAVEOverlayController::ACAVEOverlayController()
-{
-	// Set this actor to call Tick() every frame.
-	PrimaryActorTick.bCanEverTick = true;
-	bAllowTickBeforeBeginPlay = false;
-
-	// Creation of sub-components
-	Root = CreateDefaultSubobject<USceneComponent>("DefaultSceneRoot");
-	SetRootComponent(Root);
-
-	Tape = CreateMeshComponent("Tape", Root);
-	SignRightHand = CreateMeshComponent("SignRightHand", Root);
-	SignLeftHand = CreateMeshComponent("SignLeftHand", Root);
-}
-
-void ACAVEOverlayController::CycleDoorType()
-{
-	DoorCurrentMode = static_cast<EDoorMode>((DoorCurrentMode + 1) % DOOR_NUM_MODES);
-
-	// Send out a cluster event to the whole cluster that the door mode has been changed
-	if (auto* const Manager = IDisplayCluster::Get().GetClusterMgr())
-	{
-		FDisplayClusterClusterEventJson cluster_event;
-		cluster_event.Name = "CAVEOverlay Change Door to " + DoorModeNames[DoorCurrentMode];
-		cluster_event.Type = "DoorChange";
-		cluster_event.Category = "CAVEOverlay";
-		cluster_event.Parameters.Add("NewDoorState", FString::FromInt(DoorCurrentMode));
-		Manager->EmitClusterEventJson(cluster_event, true);
-	}
-}
-
-void ACAVEOverlayController::HandleClusterEvent(const FDisplayClusterClusterEventJson& Event)
-{
-	if (Event.Category.Equals("CAVEOverlay") && Event.Type.Equals("DoorChange") &&
-		Event.Parameters.Contains("NewDoorState"))
-	{
-		SetDoorMode(static_cast<EDoorMode>(FCString::Atoi(*Event.Parameters["NewDoorState"])));
-	}
-}
-
-void ACAVEOverlayController::SetDoorMode(const EDoorMode NewMode)
-{
-	DoorCurrentMode = NewMode;
-	switch (DoorCurrentMode)
-	{
-	case EDoorMode::DOOR_DEBUG:
-	case EDoorMode::DOOR_PARTIALLY_OPEN:
-		DoorCurrentOpeningWidthAbsolute = DoorOpeningWidthAbsolute;
-		if (ScreenType == SCREEN_DOOR)
-			Overlay->BlackBox->SetRenderScale(FVector2D(0, 1));
-		if (ScreenType == SCREEN_DOOR_PARTIAL)
-			Overlay->BlackBox->SetRenderScale(FVector2D(DoorOpeningWidthRelative, 1));
-		if (ScreenType == SCREEN_PRIMARY)
-			Overlay->BlackBox->SetRenderScale(FVector2D(0, 1));
-
-		Overlay->BlackBox->SetVisibility(ESlateVisibility::Visible);
-		break;
-	case EDoorMode::DOOR_OPEN:
-		DoorCurrentOpeningWidthAbsolute = WallDistance * 2;
-		if (ScreenType == SCREEN_DOOR)
-			Overlay->BlackBox->SetRenderScale(FVector2D(1, 1));
-		if (ScreenType == SCREEN_DOOR_PARTIAL)
-			Overlay->BlackBox->SetRenderScale(FVector2D(1, 1));
-		if (ScreenType == SCREEN_PRIMARY)
-			Overlay->BlackBox->SetRenderScale(FVector2D(0, 1));
-
-		Overlay->BlackBox->SetVisibility(ESlateVisibility::Visible);
-		break;
-	case EDoorMode::DOOR_CLOSED:
-		DoorCurrentOpeningWidthAbsolute = 0;
-		if (ScreenType == SCREEN_DOOR)
-			Overlay->BlackBox->SetRenderScale(FVector2D(0, 1));
-		if (ScreenType == SCREEN_DOOR_PARTIAL)
-			Overlay->BlackBox->SetRenderScale(FVector2D(0, 1));
-		if (ScreenType == SCREEN_PRIMARY)
-			Overlay->BlackBox->SetRenderScale(FVector2D(0, 1));
-
-		Overlay->BlackBox->SetVisibility(ESlateVisibility::Hidden);
-		break;
-	default:;
-	}
-
-	// On the secondary nodes that are not the door, hide the overlay completely
-	// It might make more sense to just not add it there...
-	if (ScreenType == SCREEN_NORMAL)
-		Overlay->BlackBox->SetRenderScale(FVector2D(0, 1));
-
-	UE_LOGFMT(LogCAVEOverlay, Log, "Switched door state to {State}. New opening width is {Width}.",
-			  *DoorModeNames[DoorCurrentMode], DoorCurrentOpeningWidthAbsolute);
-
-	// On the primary node, show which door mode is currently active.
-	if (ScreenType == SCREEN_PRIMARY)
-	{
-		Overlay->CornerText->SetText(FText::FromString(DoorModeNames[DoorCurrentMode]));
-	}
-}
-
-// Called when the game starts or when spawned
-void ACAVEOverlayController::BeginPlay()
-{
-	Super::BeginPlay();
-
-	// Don't do anything if we're a dedicated server. We shouldn't even exist there.
-	if (GetNetMode() == NM_DedicatedServer)
-		return;
-
-	// Currently, there is no support for multi-user systems in general, as we only depend on the local pawn.
-	// In a MU setting, the relevant pawn isn't our local one, but the primary node's pawn.
-	if (GetNetMode() != NM_Standalone)
-		return;
-
-	// This should return the respective client's local playercontroller or, if we're a listen server, our own PC.
-	auto* PC = GetWorld() ? GetWorld()->GetFirstPlayerController() : nullptr;
-
-	// it can happen that the PC is valid, but we have no player attached to it yet.
-	// Check for this - however, we should work around it by somehow getting notified when that happens.
-	// Not sure which place would be best...
-	const bool bValidPC = PC && PC->GetLocalPlayer();
-
-	if (!bValidPC || !URWTHVRUtilities::IsRoomMountedMode())
-		return;
-
-	// Input config
-	if (URWTHVRUtilities::IsPrimaryNode())
-	{
-		if (CycleDoorTypeInputAction == nullptr)
-		{
-			UE_LOGFMT(LogCAVEOverlay, Error, "Input action and mapping not set in CaveOverlayController!");
-			return;
-		}
-
-		UEnhancedInputComponent* Input = Cast<UEnhancedInputComponent>(PC->InputComponent);
-		Input->BindAction(CycleDoorTypeInputAction, ETriggerEvent::Triggered, this,
-						  &ACAVEOverlayController::CycleDoorType);
-	}
-
-	// Bind the cluster events that manage the door state.
-	IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
-	if (ClusterManager && !ClusterEventListenerDelegate.IsBound())
-	{
-		ClusterEventListenerDelegate =
-			FOnClusterEventJsonListener::CreateUObject(this, &ACAVEOverlayController::HandleClusterEvent);
-		ClusterManager->AddClusterEventJsonListener(ClusterEventListenerDelegate);
-	}
-
-	// Determine the screen-type for later usage
-	if (IDisplayCluster::Get().GetClusterMgr()->GetNodeId().Equals(ScreenMain, ESearchCase::IgnoreCase))
-	{
-		ScreenType = SCREEN_PRIMARY;
-	}
-	else if (ContainsFString(ScreensDoor, IDisplayCluster::Get().GetClusterMgr()->GetNodeId()))
-	{
-		ScreenType = SCREEN_DOOR;
-	}
-	else if (ContainsFString(ScreensDoorPartial, IDisplayCluster::Get().GetClusterMgr()->GetNodeId()))
-	{
-		ScreenType = SCREEN_DOOR_PARTIAL;
-	}
-	else
-	{
-		ScreenType = SCREEN_NORMAL;
-	}
-
-	// Create and add widget to local playercontroller.
-	if (!OverlayClass)
-	{
-		UE_LOGFMT(LogCAVEOverlay, Error, "OverlayClass not set in CaveOverlayController!");
-		return;
-	}
-
-	Overlay = CreateWidget<UDoorOverlayData>(PC, OverlayClass);
-	Overlay->AddToViewport(0);
-
-	// Set the default door mode (partially open)
-	SetDoorMode(DoorCurrentMode);
-
-	// Set Text to "" until someone presses the key for the first time
-	Overlay->CornerText->SetText(FText::FromString(""));
-
-	// Get the pawn so we can have access to head and hand positions
-	VRPawn = Cast<ARWTHVRPawn>(PC->GetPawnOrSpectator());
-	if (VRPawn)
-	{
-		// we're good to go!
-		bInitialized = true;
-	}
-	else
-	{
-		UE_LOGFMT(LogCAVEOverlay, Error, "No VirtualRealityPawn found which we could attach to!");
-	}
-
-	// Create dynamic materials at runtime
-	TapeMaterialDynamic = Tape->CreateDynamicMaterialInstance(0);
-	RightSignMaterialDynamic = SignRightHand->CreateDynamicMaterialInstance(0);
-	LeftSignMaterialDynamic = SignLeftHand->CreateDynamicMaterialInstance(0);
-
-	UE_LOGFMT(LogCAVEOverlay, Display, "CaveOverlay Initialization was successfull.");
-}
-
-void ACAVEOverlayController::EndPlay(const EEndPlayReason::Type EndPlayReason)
-{
-	IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
-	if (ClusterManager && ClusterEventListenerDelegate.IsBound())
-	{
-		ClusterManager->RemoveClusterEventJsonListener(ClusterEventListenerDelegate);
-	}
-
-	Super::EndPlay(EndPlayReason);
-}
-
-double ACAVEOverlayController::CalculateOpacityFromPosition(const FVector& Position) const
-{
-	// Calculate opacity value depending on how far we are from the walls. Further away == lower opacity,
-	// fully opaque when WallFadeDistance away from the wall. Could just use a lerp here..
-	return FMath::Max(
-		FMath::Clamp((FMath::Abs(Position.X) - (WallDistance - WallCloseDistance)) / WallFadeDistance, 0.0, 1.0),
-		FMath::Clamp((FMath::Abs(Position.Y) - (WallDistance - WallCloseDistance)) / WallFadeDistance, 0.0, 1.0));
-}
-
-bool ACAVEOverlayController::PositionInDoorOpening(const FVector& Position) const
-{
-	// The position of the corner with 10cm of buffer. In negative direction because the door is in negative direction
-	// of the cave
-	const float CornerValue = -(WallDistance + 10);
-
-	// Check whether our X position is within the door zone. This zone starts 10cm further away from the wall
-	// than the WallCloseDistance, and ends 10cm outside of the wall (door). As the door is in negative X direction,
-	// the signs need to be negated.
-	const bool bXWithinDoor =
-		FMath::IsWithinInclusive(Position.X, CornerValue, -(WallDistance - WallCloseDistance - 10));
-
-	// Checks whether our Y position is between the lower corner with some overlap and
-	// the door corner (CornerValue + DoorCurrentOpeningWidthAbsolute)
-	const bool bYWithinDoor =
-		FMath::IsWithinInclusive(Position.Y, CornerValue, CornerValue + DoorCurrentOpeningWidthAbsolute);
-	return bXWithinDoor && bYWithinDoor;
-}
-
-void ACAVEOverlayController::SetSignsForHand(UStaticMeshComponent* Sign, const FVector& HandPosition,
-											 UMaterialInstanceDynamic* HandMaterial) const
-{
-	const bool bHandIsCloseToWall =
-		FMath::IsWithinInclusive(HandPosition.GetAbsMax(), WallDistance - WallCloseDistance, WallDistance);
-	if (bHandIsCloseToWall && !PositionInDoorOpening(HandPosition))
-	{
-		Sign->SetVisibility(true);
-		HandMaterial->SetScalarParameterValue("SignOpacity", CalculateOpacityFromPosition(HandPosition));
-
-		// Which wall are we closest to? This is the wall we project the sign onto
-		const bool bXWallCloser = FMath::Abs(HandPosition.X) > FMath::Abs(HandPosition.Y);
-
-		// Set the position towards the closest wall to the wall itself, keep the other positions
-		const double X = bXWallCloser ? FMath::Sign(HandPosition.X) * WallDistance : HandPosition.X;
-		const double Y = bXWallCloser ? HandPosition.Y : FMath::Sign(HandPosition.Y) * WallDistance;
-		const double Z = HandPosition.Z;
-
-		// Rotate the sign by 90° if we're on a side wall
-		const auto Rot = bXWallCloser ? FRotator(0, 0, 0) : FRotator(0, 90, 0);
-		const auto Pos = FVector(X, Y, Z);
-		Sign->SetRelativeLocationAndRotation(Pos, Rot);
-	}
-	else
-	{
-		Sign->SetVisibility(false);
-	}
-}
-
-void ACAVEOverlayController::Tick(float DeltaTime)
-{
-	Super::Tick(DeltaTime);
-
-	// If we're not yet initialized, do nothing. This shouldn't really happen as we only spawn on the cave anyway
-	if (!bInitialized)
-	{
-		return;
-	}
-
-	// Head/Tape Logic
-	const FVector HeadPosition = VRPawn->HeadCameraComponent->GetRelativeTransform().GetLocation();
-	const bool bHeadIsCloseToWall =
-		FMath::IsWithinInclusive(HeadPosition.GetAbsMax(), WallDistance - WallCloseDistance, WallDistance);
-
-	// Only show the tape when close to a wall and not within the door opening
-	if (bHeadIsCloseToWall && !PositionInDoorOpening(HeadPosition))
-	{
-		Tape->SetVisibility(true);
-
-		// Offset the tape in z direction to always be at head height
-		Tape->SetRelativeLocation(HeadPosition * FVector(0, 0, 1));
-
-		TapeMaterialDynamic->SetScalarParameterValue("BarrierOpacity", CalculateOpacityFromPosition(HeadPosition));
-
-		if (FMath::IsWithin(FVector2D(HeadPosition).GetAbsMax(), WallDistance - WallWarningDistance, WallDistance))
-		{
-			// in warning distance == red tape
-			TapeMaterialDynamic->SetVectorParameterValue("StripeColor", FVector(1, 0, 0));
-		}
-		else
-		{
-			// otherwise we're just yellow
-			TapeMaterialDynamic->SetVectorParameterValue("StripeColor", FVector(1, 1, 0));
-		}
-	}
-	else
-	{
-		Tape->SetVisibility(false);
-	}
-
-	// Hand Logic
-	const FVector RightPosition = VRPawn->RightHand->GetRelativeTransform().GetLocation();
-	const FVector LeftPosition = VRPawn->LeftHand->GetRelativeTransform().GetLocation();
-
-	// Set the position rotation, opacity, visibility of the hand warning signs.
-	SetSignsForHand(SignRightHand, RightPosition, RightSignMaterialDynamic);
-	SetSignsForHand(SignLeftHand, LeftPosition, LeftSignMaterialDynamic);
-}
diff --git a/Source/RWTHVRCluster/Private/CaveSetup.cpp b/Source/RWTHVRCluster/Private/CaveSetup.cpp
deleted file mode 100644
index c39e47d4997863ee26b9fe5065c327efc6aa669f..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Private/CaveSetup.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-
-#include "CaveSetup.h"
-
-#include "Logging/StructuredLog.h"
-#include "Utility/RWTHVRUtilities.h"
-
-
-// Sets default values
-ACaveSetup::ACaveSetup()
-{
-	PrimaryActorTick.bCanEverTick = false;
-	SetActorEnableCollision(false);
-
-	// Actor needs to replicate, as it is attached to the pawn on the server.
-	bReplicates = true;
-}
-
-// Called when the game starts or when spawned
-void ACaveSetup::BeginPlay()
-{
-	Super::BeginPlay();
-
-	if (!URWTHVRUtilities::IsRoomMountedMode())
-	{
-		return;
-	}
-
-	// Spawn all actors that are set in the blueprint asset.
-	for (const auto ActorClass : ActorsToSpawnOnCave)
-	{
-		if (const auto World = GetWorld())
-		{
-			const auto Actor = World->SpawnActor(ActorClass);
-			Actor->AttachToActor(this, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
-			UE_LOGFMT(LogTemp, Display, "CaveSetup: Spawned Actor {Actor} on the Cave and attached it.",
-					  Actor->GetName());
-		}
-	}
-
-	// Apply the DTrack LiveLink Preset. Only do this if we are the primaryNode
-
-	if (URWTHVRUtilities::IsPrimaryNode())
-	{
-		if (LiveLinkPresetToApplyOnCave && LiveLinkPresetToApplyOnCave->IsValidLowLevelFast())
-		{
-			LiveLinkPresetToApplyOnCave->ApplyToClientLatent();
-
-			UE_LOGFMT(LogTemp, Display, "CaveSetup: Applied LiveLinkPreset {Preset} to Client.",
-					  LiveLinkPresetToApplyOnCave->GetName());
-		}
-	}
-}
diff --git a/Source/RWTHVRCluster/Private/ClusterConsole/ClusterConsole.cpp b/Source/RWTHVRCluster/Private/ClusterConsole/ClusterConsole.cpp
deleted file mode 100644
index a0a7c49523e7191163cb53a5c290f9459c73f3f1..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Private/ClusterConsole/ClusterConsole.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#include "ClusterConsole/ClusterConsole.h"
-
-#include "IDisplayCluster.h"
-#include "Cluster/DisplayClusterClusterEvent.h"
-
-void FClusterConsole::Register()
-{
-	/* Registering console command */
-	ClusterConsoleCommand = IConsoleManager::Get().RegisterConsoleCommand(
-		TEXT("ClusterExecute"),
-		TEXT("<Your Command> - Execute commands on every node of the nDisplay cluster by prepending ClusterExecute"),
-		FConsoleCommandWithArgsDelegate::CreateLambda(
-			[](const TArray<FString>& Args)
-			{
-				if (IDisplayCluster::Get().GetClusterMgr() == nullptr || Args.Num() == 0)
-					return;
-
-				/* Emitting cluster event */
-				FDisplayClusterClusterEventJson ClusterEvent;
-				ClusterEvent.Name = "ClusterExecute " + Args[0];
-				ClusterEvent.Type = Args[0];
-				ClusterEvent.Category = "NDisplayClusterExecute";
-				ClusterEvent.Parameters.Add("Command", FString::Join(Args, TEXT(" ")));
-
-				IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventJson(ClusterEvent, false);
-			}));
-
-	/* Register cluster event handling */
-	const IDisplayCluster* DisplayCluster = FModuleManager::LoadModulePtr<IDisplayCluster>(IDisplayCluster::ModuleName);
-	if (DisplayCluster && !ClusterEventListenerDelegate.IsBound())
-	{
-		ClusterEventListenerDelegate = FOnClusterEventJsonListener::CreateLambda(
-			[](const FDisplayClusterClusterEventJson& Event)
-			{
-				/* Actual handling */
-				if (Event.Category.Equals("NDisplayClusterExecute") && Event.Parameters.Contains("Command") && GEngine)
-				{
-					GEngine->Exec(GEngine->GetCurrentPlayWorld(), *Event.Parameters["Command"]);
-				}
-			});
-		DisplayCluster->GetClusterMgr()->AddClusterEventJsonListener(ClusterEventListenerDelegate);
-	}
-}
-
-void FClusterConsole::Unregister() const
-{
-	IConsoleManager::Get().UnregisterConsoleObject(ClusterConsoleCommand);
-	IDisplayCluster::Get().GetClusterMgr()->RemoveClusterEventJsonListener(ClusterEventListenerDelegate);
-}
diff --git a/Source/RWTHVRCluster/Private/RWTHVRCluster.cpp b/Source/RWTHVRCluster/Private/RWTHVRCluster.cpp
deleted file mode 100644
index 3f0955de6db6984ba796eb5c6ae793141456c25e..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Private/RWTHVRCluster.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "RWTHVRCluster.h"
-
-#define LOCTEXT_NAMESPACE "FRWTHVRClusterModule"
-
-void FRWTHVRClusterModule::StartupModule() { ClusterConsole.Register(); }
-
-void FRWTHVRClusterModule::ShutdownModule() { ClusterConsole.Unregister(); }
-
-#undef LOCTEXT_NAMESPACE
-
-IMPLEMENT_MODULE(FRWTHVRClusterModule, RWTHVRCluster)
diff --git a/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlayController.h b/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlayController.h
deleted file mode 100644
index 148897a8713c726bc9583de176cf04040b657074..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlayController.h
+++ /dev/null
@@ -1,143 +0,0 @@
-#pragma once
-
-#include "CoreMinimal.h"
-#include "GameFramework/Actor.h"
-#include "CAVEOverlay/DoorOverlayData.h"
-#include "Cluster/IDisplayClusterClusterManager.h"
-#include "Pawn/RWTHVRPawn.h"
-#include "CAVEOverlayController.generated.h"
-
-DECLARE_LOG_CATEGORY_EXTERN(LogCAVEOverlay, Log, All);
-
-/**
- * Actor which controls the cave overlay. The overlay displays a warning tape around the cave
- * when the user moves their head too close to the wall, and a warning sign when the hands are
- * too close.
- */
-UCLASS()
-class RWTHVRCLUSTER_API ACAVEOverlayController : public AActor
-{
-	GENERATED_BODY()
-
-public:
-	ACAVEOverlayController();
-
-protected:
-	virtual void BeginPlay() override;
-	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
-
-private:
-	// Types of cave screens defined in the cluster config.
-	enum EScreen_Type
-	{
-		// the primary node screen
-		SCREEN_PRIMARY,
-		// any secondary node screen
-		SCREEN_NORMAL,
-		// the screens that cover the partially opened door
-		SCREEN_DOOR_PARTIAL,
-		// additional screens that cover the door
-		SCREEN_DOOR
-	};
-
-	// which screen type this node is running on
-	EScreen_Type ScreenType = SCREEN_NORMAL;
-
-	// which additional node names define the screens that cover the door
-	const TArray<FString> ScreensDoor = {"node_bul_left_eye", "node_bul_right_eye", "node_bll_left_eye",
-										 "node_bll_right_eye"};
-
-	// which node names define the screens that cover the partial door
-	const TArray<FString> ScreensDoorPartial = {"node_bur_left_eye", "node_bur_right_eye", "node_blr_left_eye",
-												"node_blr_right_eye"};
-
-	const FString ScreenMain = "node_main";
-
-	// Door Mode
-	enum EDoorMode
-	{
-		DOOR_PARTIALLY_OPEN = 0,
-		DOOR_OPEN = 1,
-		DOOR_CLOSED = 2,
-		DOOR_DEBUG = 3,
-		DOOR_NUM_MODES = 4
-	};
-
-	const FString DoorModeNames[DOOR_NUM_MODES] = {"Partially Open", "Open", "Closed", "Debug"};
-	EDoorMode DoorCurrentMode = DOOR_PARTIALLY_OPEN;
-	const float DoorOpeningWidthRelative = 0.522; //%, used for the overlay width on the screen
-	const float DoorOpeningWidthAbsolute = 165; // cm, used for the non tape part at the door
-	const float WallDistance = 262.5; // cm, distance from center to a wall, *2 = wall width
-	const float WallCloseDistance = 75; // cm, the distance considered to be too close to the walls
-	const float WallFadeDistance = 35; // cm, the distance over which the tape is faded
-	const float WallWarningDistance = 40; // cm, distance on which the tape turns red, measured from wall
-	float DoorCurrentOpeningWidthAbsolute = 0;
-
-	// Helper function to create a mesh component in the constructor
-	UStaticMeshComponent* CreateMeshComponent(const FName& Name, USceneComponent* Parent);
-
-	// Calculates opacity value used for the dynamic materials of the tape and sign. The closer the more opaque.
-	double CalculateOpacityFromPosition(const FVector& Position) const;
-
-	// Check whether the given position is within the door area of the (partially) open door.
-	bool PositionInDoorOpening(const FVector& Position) const;
-
-	// Sets the position, orientation and opacity/visibility of the Sign according to the HandPosition.
-	void SetSignsForHand(UStaticMeshComponent* Sign, const FVector& HandPosition,
-						 UMaterialInstanceDynamic* HandMaterial) const;
-
-	// Only calculate positions and material values when we're fully initialized.
-	bool bInitialized = false;
-
-	// Reference to the currently active pawn that we're tracking positions of.
-	UPROPERTY()
-	ARWTHVRPawn* VRPawn;
-
-	// Cluster Events
-	FOnClusterEventJsonListener ClusterEventListenerDelegate;
-	void HandleClusterEvent(const FDisplayClusterClusterEventJson& Event);
-
-public:
-	virtual void Tick(float DeltaTime) override;
-
-	// Change door mode manually between open, partially open and closed.
-	void CycleDoorType();
-	void SetDoorMode(EDoorMode M);
-
-	// Root component
-	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true"))
-	USceneComponent* Root;
-
-	// Tape Static Mesh component. Reference to static mesh needs to be set in the corresponding BP.
-	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true"))
-	UStaticMeshComponent* Tape;
-
-	// Right Hand Sign Static Mesh component. Reference to static mesh needs to be set in the corresponding BP.
-	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true"))
-	UStaticMeshComponent* SignRightHand;
-
-	// Left Hand Sign Static Mesh component. Reference to static mesh needs to be set in the corresponding BP.
-	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true"))
-	UStaticMeshComponent* SignLeftHand;
-
-	// UI Overlay
-	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "CAVEOverlay")
-	TSubclassOf<UDoorOverlayData> OverlayClass;
-
-	// UI Overlay
-	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "CAVEOverlay")
-	UInputAction* CycleDoorTypeInputAction;
-
-	UPROPERTY()
-	UDoorOverlayData* Overlay;
-
-	// Dynamic Materials to control opacity
-	UPROPERTY()
-	UMaterialInstanceDynamic* TapeMaterialDynamic;
-
-	UPROPERTY()
-	UMaterialInstanceDynamic* RightSignMaterialDynamic;
-
-	UPROPERTY()
-	UMaterialInstanceDynamic* LeftSignMaterialDynamic;
-};
diff --git a/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlaySettings.h b/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlaySettings.h
deleted file mode 100644
index eee4b554ffa439a6d1f743144fa44689de5193d7..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlaySettings.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-#include "CoreMinimal.h"
-#include "Engine/DeveloperSettings.h"
-#include "CAVEOverlaySettings.generated.h"
-
-UENUM(BlueprintType)
-enum DefaultActivationType
-{
-	DefaultActivationType_OFF UMETA(DisplayName = "Off by default"),
-	DefaultActivationType_ON UMETA(DisplayName = "On by default")
-};
-
-UCLASS(config = Game, defaultconfig, meta = (DisplayName = "CAVE Overlay"))
-class RWTHVRCLUSTER_API UCAVEOverlaySettings : public UDeveloperSettings
-{
-	GENERATED_BODY()
-
-public:
-	UPROPERTY(EditAnywhere, config, Category = "General", meta = (DisplayName = "Default Activation Type"))
-	TEnumAsByte<DefaultActivationType> DefaultActivationType = DefaultActivationType_ON;
-
-	UPROPERTY(EditAnywhere, config, Category = Maps, meta = (AllowedClasses = "/Script/Engine.World"))
-	TArray<FSoftObjectPath> ExcludedMaps;
-};
diff --git a/Source/RWTHVRCluster/Public/CAVEOverlay/DoorOverlayData.h b/Source/RWTHVRCluster/Public/CAVEOverlay/DoorOverlayData.h
deleted file mode 100644
index b4a0fada65f6f01d398e73a33de60b4fec4dc307..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Public/CAVEOverlay/DoorOverlayData.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-#pragma once
-
-#include "CoreMinimal.h"
-#include "Blueprint/UserWidget.h"
-#include "Components/TextBlock.h"
-#include "Components/Border.h"
-#include "DoorOverlayData.generated.h"
-
-/**
- * Used as a parent-class in the overlay widget. Like this we can access the UMG properties in C++
- */
-UCLASS()
-class RWTHVRCLUSTER_API UDoorOverlayData : public UUserWidget
-{
-	GENERATED_BODY()
-
-public:
-	// These declarations are magically bound to the UMG blueprints elements,
-	// if they are named the same
-	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
-	UTextBlock* CornerText;
-
-	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
-	UBorder* BlackBox;
-};
diff --git a/Source/RWTHVRCluster/Public/CaveSetup.h b/Source/RWTHVRCluster/Public/CaveSetup.h
deleted file mode 100644
index 793d72c9ff419ce35aade0784a2d076cbff3a37e..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Public/CaveSetup.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-#pragma once
-
-#include "CoreMinimal.h"
-#include "LiveLinkPreset.h"
-#include "GameFramework/Actor.h"
-#include "CaveSetup.generated.h"
-
-/**
- * Simple Actor that needs to be added to the level which spawns Cave-related actors
- * such as the CaveOverlay.
- * It attaches itself to the Primary Node's Pawn and then replicates on the server.
- */
-
-UCLASS(hideCategories = (Rendering, Input, Actor, Base, Collision, Shape, Physics, HLOD))
-class RWTHVRCLUSTER_API ACaveSetup : public AActor
-{
-	GENERATED_BODY()
-
-public:
-	ACaveSetup();
-
-	UPROPERTY(EditAnywhere)
-	TArray<UClass*> ActorsToSpawnOnCave;
-
-	UPROPERTY(EditAnywhere, BlueprintReadWrite)
-	ULiveLinkPreset* LiveLinkPresetToApplyOnCave;
-
-protected:
-	// Called when the game starts or when spawned
-	virtual void BeginPlay() override;
-};
diff --git a/Source/RWTHVRCluster/Public/ClusterConsole/ClusterConsole.h b/Source/RWTHVRCluster/Public/ClusterConsole/ClusterConsole.h
deleted file mode 100644
index f09a948c64f5b82b1c50c62cfb5b4a7ca86118a8..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Public/ClusterConsole/ClusterConsole.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include "CoreMinimal.h"
-#include "HAL/IConsoleManager.h"
-#include "Cluster/IDisplayClusterClusterManager.h"
-#include "ClusterConsole.generated.h"
-
-/**
- * The ClusterConsole provides the console command "ClusterExecute"
- * The code catches your command, broadcasts it to every nDisplay node and executes it everywhere
- *
- * This class has to be registered and unregistered. This can easily be done in every StartupModule/ShutdownModule
- * functions.
- */
-USTRUCT()
-struct RWTHVRCLUSTER_API FClusterConsole
-{
-	GENERATED_BODY()
-
-private:
-	/* Used for ClusterExecute console command */
-	IConsoleCommand* ClusterConsoleCommand = nullptr;
-	FOnClusterEventJsonListener ClusterEventListenerDelegate;
-
-public:
-	void Register();
-	void Unregister() const;
-};
diff --git a/Source/RWTHVRCluster/Public/Events/DisplayClusterEventParameterHelper.h b/Source/RWTHVRCluster/Public/Events/DisplayClusterEventParameterHelper.h
deleted file mode 100644
index 49500b767751ada9db7d935be95de34311540ad5..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Public/Events/DisplayClusterEventParameterHelper.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#pragma once
-
-#include "Serialization/MemoryReader.h"
-#include "Serialization/MemoryWriter.h"
-#include "type_traits"
-
-template <typename ParameterType, typename... RemainingParameterTypes>
-inline void SerializeParameters(FMemoryWriter* MemoryWriter, ParameterType&& Parameter,
-								RemainingParameterTypes&&... RemainingParameters)
-{
-	using NonConstType = typename std::remove_cv_t<typename TRemoveReference<ParameterType>::Type>;
-	// const_cast because the same operator (<<) is used for reading and writing
-	(*MemoryWriter) << const_cast<NonConstType&>(Parameter);
-	SerializeParameters(MemoryWriter, Forward<RemainingParameterTypes>(RemainingParameters)...);
-}
-
-inline void SerializeParameters(FMemoryWriter* MemoryWriter) {}
-
-// This is a wrapper function to recursively fill the argument tuple. This overload is only used if the index indicating
-// the currently handled attribute is less than the number of total attributes. I.e., if the attribute index is valid.
-template <int CurrentIndex, typename... ArgTypes>
-inline typename TEnableIf<(CurrentIndex < sizeof...(ArgTypes))>::Type
-FillArgumentTuple(FMemoryReader* MemoryReader, TTuple<ArgTypes...>* ArgumentTuple)
-{
-	// Read the "<<" as ">>" operator here. FArchive uses the same for both and decides based on an internal type on
-	// what to do. So this statement parses the bytes that were passed into reader and puts the parsed object into the
-	// tuple at index CurrentIndex.
-	(*MemoryReader) << ArgumentTuple->template Get<CurrentIndex>();
-
-	// Recursive call for the remaining attributes.
-	FillArgumentTuple<CurrentIndex + 1>(MemoryReader, Forward<TTuple<ArgTypes...>*>(ArgumentTuple));
-}
-
-// The overload that is called if we are "passed the end" of attributes.
-template <int CurrentIndex, typename... ArgTypes>
-inline typename TEnableIf<(CurrentIndex >= sizeof...(ArgTypes))>::Type
-FillArgumentTuple(FMemoryReader* MemoryReader, TTuple<ArgTypes...>* ArgumentTuple)
-{
-}
-
-template <typename RetType, typename... ArgTypes>
-inline RetType CallDelegateWithParameterMap(const TDelegate<RetType, ArgTypes...>& Delegate,
-											const TMap<FString, FString>& Parameters)
-{
-	// Create a tuple that holds all arguments. This assumes that all argument types are default constructible. However,
-	// all types that overload the FArchive "<<" operator probably are.
-	TTuple<typename std::remove_cv_t<typename TRemoveReference<ArgTypes>::Type>...> ArgumentTuple;
-
-	// This call will parse the string map and fill all values in the tuple appropriately.
-	FillArgumentTuple<0>(&ArgumentTuple, Parameters);
-
-	// The lambda function is only necessary because delegates do not overload the ()-operator but use the Execute()
-	// method instead. So, the lambda acts as a wrapper.
-	return ArgumentTuple.ApplyBefore([Delegate](ArgTypes&&... Arguments)
-									 { Delegate.Execute(Forward<ArgTypes>(Arguments)...); });
-}
diff --git a/Source/RWTHVRCluster/Public/Events/DisplayClusterEventWrapper.h b/Source/RWTHVRCluster/Public/Events/DisplayClusterEventWrapper.h
deleted file mode 100644
index e08fed921287bbad6a76367e590cc21e2b476b5d..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Public/Events/DisplayClusterEventWrapper.h
+++ /dev/null
@@ -1,138 +0,0 @@
-#pragma once
-
-#include "IDisplayCluster.h"
-#include "Cluster/IDisplayClusterClusterManager.h"
-#include "Cluster/DisplayClusterClusterEvent.h"
-#include "DisplayClusterEventParameterHelper.h"
-#include "Templates/IsInvocable.h"
-
-static constexpr int32 CLUSTER_EVENT_WRAPPER_EVENT_ID = 1337420;
-
-template <typename MemberFunctionType, MemberFunctionType MemberFunction>
-class ClusterEventWrapperEvent;
-
-template <typename ObjectType, typename ReturnType, typename... ArgTypes,
-		  ReturnType (ObjectType::*MemberFunction)(ArgTypes...)>
-class ClusterEventWrapperEvent<ReturnType (ObjectType::*)(ArgTypes...), MemberFunction>
-{
-	static_assert(TIsDerivedFrom<ObjectType, UObject>::IsDerived, "Object needs to derive from UObject");
-
-public:
-	using MemberFunctionType = decltype(MemberFunction);
-
-	ClusterEventWrapperEvent(const TCHAR* MethodName) : MethodName{MethodName} {}
-
-	void Attach(ObjectType* NewObject)
-	{
-		checkf(Object == nullptr, TEXT("The event is already attached."));
-		Object = NewObject;
-		ObjectId = Object->GetUniqueID();
-
-		EDisplayClusterOperationMode OperationMode = IDisplayCluster::Get().GetOperationMode();
-		if (OperationMode == EDisplayClusterOperationMode::Cluster)
-		{
-			IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
-			check(ClusterManager != nullptr);
-
-			check(!ClusterEventListenerDelegate.IsBound());
-			ClusterEventListenerDelegate = FOnClusterEventBinaryListener::CreateLambda(
-				[this](const FDisplayClusterClusterEventBinary& Event)
-				{
-					if (Event.EventId != CLUSTER_EVENT_WRAPPER_EVENT_ID)
-					{
-						return;
-					}
-
-					FMemoryReader MemoryReader(Event.EventData);
-
-					uint32 EventObjectId;
-					// This reads the value!
-					MemoryReader << EventObjectId;
-					if (EventObjectId != ObjectId)
-					{
-						// Event does not belong to this object.
-						return;
-					}
-
-					FString EventMethodName;
-					// This reads the value!
-					MemoryReader << EventMethodName;
-					if (EventMethodName != MethodName)
-					{
-						// This event does not belong to this method.
-						return;
-					}
-
-					// Create a tuple that holds all arguments. This assumes that all
-					// argument types are default constructible. However, all
-					// types that overload the FArchive "<<" operator probably are.
-					TTuple<typename std::remove_cv_t<typename TRemoveReference<ArgTypes>::Type>...> ArgumentTuple;
-
-					// This call will deserialze the values and fill all values in the tuple appropriately.
-					FillArgumentTuple<0>(&MemoryReader, &ArgumentTuple);
-
-					ArgumentTuple.ApplyBefore([this](const ArgTypes&... Arguments)
-											  { (Object->*MemberFunction)(Forward<const ArgTypes&>(Arguments)...); });
-				});
-			ClusterManager->AddClusterEventBinaryListener(ClusterEventListenerDelegate);
-		}
-	}
-
-	void Detach()
-	{
-		checkf(Object != nullptr, TEXT("The event was never attached."));
-
-		EDisplayClusterOperationMode OperationMode = IDisplayCluster::Get().GetOperationMode();
-		if (OperationMode == EDisplayClusterOperationMode::Cluster)
-		{
-			IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
-			check(ClusterManager != nullptr);
-
-			// check(ClusterEventListenerDelegate.IsBound());
-			ClusterManager->RemoveClusterEventBinaryListener(ClusterEventListenerDelegate);
-		}
-	}
-
-	void Send(ArgTypes... Arguments)
-	{
-		checkf(Object != nullptr, TEXT("The event was not attached."));
-
-		EDisplayClusterOperationMode OperationMode = IDisplayCluster::Get().GetOperationMode();
-		if (OperationMode != EDisplayClusterOperationMode::Cluster)
-		{
-			(Object->*MemberFunction)(Forward<ArgTypes>(Arguments)...);
-		}
-		else
-		{
-			IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
-			check(ClusterManager != nullptr);
-
-			FDisplayClusterClusterEventBinary ClusterEvent;
-			ClusterEvent.EventId = CLUSTER_EVENT_WRAPPER_EVENT_ID;
-			ClusterEvent.bShouldDiscardOnRepeat = false;
-
-			FMemoryWriter MemoryWriter(ClusterEvent.EventData);
-			MemoryWriter << ObjectId;
-			MemoryWriter << const_cast<FString&>(MethodName);
-			SerializeParameters(&MemoryWriter, Forward<ArgTypes>(Arguments)...);
-
-			ClusterManager->EmitClusterEventBinary(ClusterEvent, true);
-		}
-	}
-
-private:
-	const FString MethodName;
-	uint32 ObjectId;
-	ObjectType* Object = nullptr;
-	FOnClusterEventBinaryListener ClusterEventListenerDelegate;
-};
-
-#define DCEW_STRINGIFY(x) #x
-#define DCEW_TOSTRING(x) DCEW_STRINGIFY(x)
-
-#define DECLARE_DISPLAY_CLUSTER_EVENT(OwningType, MethodIdentifier)                                                    \
-	ClusterEventWrapperEvent<decltype(&OwningType::MethodIdentifier), &OwningType::MethodIdentifier>                   \
-		MethodIdentifier##Event                                                                                        \
-	{                                                                                                                  \
-		TEXT(DCEW_TOSTRING(OwningType) DCEW_TOSTRING(MethodIdentifier))                                                \
-	}
diff --git a/Source/RWTHVRCluster/Public/RWTHVRCluster.h b/Source/RWTHVRCluster/Public/RWTHVRCluster.h
deleted file mode 100644
index 1c719c718aa503615557da6e7f713256a8232859..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/Public/RWTHVRCluster.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include "CoreMinimal.h"
-#include "Modules/ModuleManager.h"
-#include "ClusterConsole/ClusterConsole.h"
-
-class FRWTHVRClusterModule : public IModuleInterface
-{
-public:
-	virtual void StartupModule() override;
-	virtual void ShutdownModule() override;
-
-private:
-	FClusterConsole ClusterConsole;
-};
diff --git a/Source/RWTHVRCluster/RWTHVRCluster.Build.cs b/Source/RWTHVRCluster/RWTHVRCluster.Build.cs
deleted file mode 100644
index 6979cc65cea0cc0f7fca763bb7d7afa9c13f0577..0000000000000000000000000000000000000000
--- a/Source/RWTHVRCluster/RWTHVRCluster.Build.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using UnrealBuildTool;
-
-public class RWTHVRCluster : ModuleRules
-{
-	public RWTHVRCluster(ReadOnlyTargetRules Target) : base(Target)
-	{
-		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
-
-		PublicIncludePaths.AddRange(
-			new string[] { }
-		);
-
-		PrivateIncludePaths.AddRange(
-			new string[] { }
-		);
-
-		PublicDependencyModuleNames.AddRange(
-			new string[]
-			{
-				"Core",
-				"CoreUObject",
-				"Engine",
-				"DeveloperSettings",
-				"EnhancedInput",
-				"UMG",
-				"Slate",
-				"SlateCore",
-				"RWTHVRToolkit",
-				"LiveLink"
-			}
-		);
-
-		if (IsPluginEnabledForTarget("nDisplay", base.Target))
-		{
-			PublicDependencyModuleNames.AddRange(
-				new string[]
-				{
-					"DisplayCluster"
-				}
-			);
-		}
-
-		if (IsPluginEnabledForTarget("DTrackPlugin", base.Target))
-		{
-			PublicDependencyModuleNames.AddRange(
-				new string[]
-				{
-					"DTrackPlugin",
-					"DTrackInput"
-				}
-			);
-		}
-
-		PrivateDependencyModuleNames.AddRange(
-			new string[] { }
-		);
-
-		DynamicallyLoadedModuleNames.AddRange(
-			new string[] { }
-		);
-	}
-
-	private static bool IsPluginEnabledForTarget(string PluginName, ReadOnlyTargetRules Target)
-	{
-		var PL = Plugins.GetPlugin(PluginName);
-		return PL != null && Target.ProjectFile != null && Plugins.IsPluginEnabledForTarget(PL,
-			ProjectDescriptor.FromFile(Target.ProjectFile), Target.Platform, Target.Configuration, Target.Type);
-	}
-}
\ No newline at end of file
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactables/IntenSelect/IntenSelectable.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactables/IntenSelect/IntenSelectable.cpp
index 9ae825fc21b3fa370faec8bbaa1c7b063bb21b25..f99b7d65df959a5725996eebaab3ec8bc2fd89e9 100644
--- a/Source/RWTHVRToolkit/Private/Interaction/Interactables/IntenSelect/IntenSelectable.cpp
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactables/IntenSelect/IntenSelectable.cpp
@@ -4,6 +4,7 @@
 #include "Interaction/Interactables/IntenSelect/IntenSelectableScoring.h"
 #include "Interaction/Interactables/IntenSelect/IntenSelectableSinglePointScoring.h"
 #include "Kismet/KismetSystemLibrary.h"
+#include "Logging/StructuredLog.h"
 #include "Misc/MessageDialog.h"
 #include "Pawn/IntenSelectComponent.h"
 #include "Utility/RWTHVRUtilities.h"
@@ -46,9 +47,9 @@ void UIntenSelectable::BeginPlay()
 	{
 		if (ScoringBehaviours.Num() == 0)
 		{
-			URWTHVRUtilities::ShowErrorAndQuit(
-				"Please assign the Scoring Behaviour manually when using more than one IntenSelectable Component!",
-				false, this);
+			UE_LOGFMT(
+				Toolkit, Error,
+				"Please assign the Scoring Behaviour manually when using more than one IntenSelectable Component!");
 		}
 	}
 	else
diff --git a/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp b/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
index 56d56cf05728cc0ce2140f47d0b39a19e0dc665c..9801f83893cb31dec6b04d71c4f26e3696c2234b 100644
--- a/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
+++ b/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
@@ -16,6 +16,10 @@
 #include "Roles/LiveLinkTransformTypes.h"
 #include "Utility/RWTHVRUtilities.h"
 
+#if PLATFORM_SUPPORTS_CLUSTER
+#include "Components/DisplayClusterSceneComponentSyncParent.h"
+#endif
+
 ARWTHVRPawn::ARWTHVRPawn(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
 {
 	BaseEyeHeight = 160.0f;
@@ -36,14 +40,21 @@ 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 =
-		CreateDefaultSubobject<UDisplayClusterSceneComponentSyncParent>(TEXT("Parent Display Cluster Sync Component"));
-	SyncComponent->SetupAttachment(RootComponent);
+
+	SyncComponent = Cast<USceneComponent>(AddComponentByClass(UDisplayClusterSceneComponentSyncParent::StaticClass(),
+															  false, FTransform::Identity, false));
+	AddInstanceComponent(SyncComponent);
+#endif
 }
 
 void ARWTHVRPawn::Tick(float DeltaSeconds)
diff --git a/Source/RWTHVRToolkit/Private/Utility/RWTHVRUtilities.cpp b/Source/RWTHVRToolkit/Private/Utility/RWTHVRUtilities.cpp
index ed5bfea32b0297dc3efe72487784d202ac197ea9..1a57b331df01f70bf330c8e9c4826d309fa616a8 100644
--- a/Source/RWTHVRToolkit/Private/Utility/RWTHVRUtilities.cpp
+++ b/Source/RWTHVRToolkit/Private/Utility/RWTHVRUtilities.cpp
@@ -1,15 +1,5 @@
 #include "Utility/RWTHVRUtilities.h"
 
-#if PLATFORM_SUPPORTS_NDISPLAY
-#include "DisplayClusterConfigurationTypes.h"
-#include "DisplayClusterRootActor.h"
-#include "IDisplayCluster.h"
-#include "Cluster/IDisplayClusterClusterManager.h"
-#include "Components/DisplayClusterCameraComponent.h"
-#include "Config/IDisplayClusterConfigManager.h"
-#include "Game/IDisplayClusterGameManager.h"
-#endif
-
 #include "AudioDevice.h"
 #include "IHeadMountedDisplay.h"
 #include "IXRTrackingSystem.h"
@@ -17,20 +7,15 @@
 #include "Engine/LocalPlayer.h"
 #include "Kismet/GameplayStatics.h"
 
+#if PLATFORM_SUPPORTS_CLUSTER
+#include "Utility/RWTHVRClusterUtilities.h"
+#endif
+
 
 DEFINE_LOG_CATEGORY(Toolkit);
 
 bool URWTHVRUtilities::IsDesktopMode() { return !IsRoomMountedMode() && !IsHeadMountedMode(); }
 
-bool URWTHVRUtilities::IsRoomMountedMode()
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	return IDisplayCluster::Get().GetOperationMode() == EDisplayClusterOperationMode::Cluster;
-#else
-	return false;
-#endif
-}
-
 bool URWTHVRUtilities::IsHeadMountedMode()
 {
 	// In editor builds: checks for EdEngine->IsVRPreviewActive()
@@ -38,62 +23,20 @@ bool URWTHVRUtilities::IsHeadMountedMode()
 	return FAudioDevice::CanUseVRAudioDevice();
 }
 
-bool URWTHVRUtilities::IsCave()
+bool URWTHVRUtilities::IsRoomMountedMode()
 {
-#if PLATFORM_SUPPORTS_NDISPLAY
-	if (!IsRoomMountedMode())
-		return false;
-
-	const UDisplayClusterConfigurationData* ClusterConfig = IDisplayCluster::Get().GetConfigMgr()->GetConfig();
-	return ClusterConfig->CustomParameters.Contains("Hardware_Platform") &&
-		ClusterConfig->CustomParameters.Find("Hardware_Platform")->Equals("aixcave", ESearchCase::IgnoreCase);
-#else
-	return false;
+#if PLATFORM_SUPPORTS_CLUSTER
+	URWTHVRClusterUtilities::IsRoomMountedMode();
 #endif
-}
-
-bool URWTHVRUtilities::IsRolv()
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	if (!IsRoomMountedMode())
-		return false;
-
-	const UDisplayClusterConfigurationData* ClusterConfig = IDisplayCluster::Get().GetConfigMgr()->GetConfig();
-	return ClusterConfig->CustomParameters.Contains("Hardware_Platform") &&
-		ClusterConfig->CustomParameters.Find("Hardware_Platform")->Equals("ROLV", ESearchCase::IgnoreCase);
-#else
 	return false;
-#endif
 }
 
-/* Return true on the Primary in cluster mode and in a normal desktop session. Otherwise false */
 bool URWTHVRUtilities::IsPrimaryNode()
 {
-#if PLATFORM_SUPPORTS_NDISPLAY
-	if (!IDisplayCluster::IsAvailable())
-	{
-		return true;
-	}
-	IDisplayClusterClusterManager* Manager = IDisplayCluster::Get().GetClusterMgr();
-	if (Manager == nullptr)
-	{
-		return true; // if we are not in cluster mode, we are always the primary node
-	}
-	return Manager->IsPrimary() || !Manager->IsSecondary();
-#else
-	return true;
-#endif
-}
-
-bool URWTHVRUtilities::IsSecondaryNode() { return !IsPrimaryNode(); }
-
-FString URWTHVRUtilities::GetNodeName()
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	return IsRoomMountedMode() ? IDisplayCluster::Get().GetClusterMgr()->GetNodeId() : FString(TEXT("Localhost"));
-#else
-	return FString(TEXT("Localhost"));
+#if PLATFORM_SUPPORTS_CLUSTER
+	URWTHVRClusterUtilities::IsPrimaryNode();
 #endif
+	return false;
 }
 
 float URWTHVRUtilities::GetEyeDistance()
@@ -102,81 +45,7 @@ float URWTHVRUtilities::GetEyeDistance()
 	{
 		return GEngine->XRSystem->GetHMDDevice()->GetInterpupillaryDistance();
 	}
-	else
-	{
-#if PLATFORM_SUPPORTS_NDISPLAY
-		const ADisplayClusterRootActor* RootActor = IDisplayCluster::Get().GetGameMgr()->GetRootActor();
-		return (RootActor) ? RootActor->GetDefaultCamera()->GetInterpupillaryDistance() : 0.0f;
-#else
-		return 0.0f;
-#endif
-	}
-}
-
-EEyeStereoOffset URWTHVRUtilities::GetNodeEyeType()
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	const ADisplayClusterRootActor* RootActor = IDisplayCluster::Get().GetGameMgr()->GetRootActor();
-	return static_cast<EEyeStereoOffset>((RootActor) ? RootActor->GetDefaultCamera()->GetStereoOffset()
-													 : EDisplayClusterEyeStereoOffset::None);
-#else
-	return EDisplayClusterEyeStereoOffset::None;
-#endif
-}
-void URWTHVRUtilities::ShowErrorAndQuit(const FString& Message, bool ShouldQuit, const UObject* WorldContext)
-{
-	UE_LOG(LogTemp, Error, TEXT("%s"), *Message)
-#if WITH_EDITOR
-	FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Message));
-#endif
-	if (ShouldQuit)
-	{
-		UKismetSystemLibrary::QuitGame(WorldContext, nullptr, EQuitPreference::Quit, false);
-	}
-}
-
-USceneComponent* URWTHVRUtilities::GetClusterComponent(const FString& Name)
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	const ADisplayClusterRootActor* RootActor = IDisplayCluster::Get().GetGameMgr()->GetRootActor();
-	return (RootActor) ? RootActor->GetComponentByName<USceneComponent>(Name) : nullptr;
-#else
-	return nullptr;
-#endif
-}
-
-USceneComponent* URWTHVRUtilities::GetNamedClusterComponent(const ENamedClusterComponent& Component)
-{
-	switch (Component)
-	{
-	case ENamedClusterComponent::NCC_CAVE_ORIGIN:
-		return GetClusterComponent("cave_origin");
-	case ENamedClusterComponent::NCC_CAVE_CENTER:
-		return GetClusterComponent("cave_center");
-	case ENamedClusterComponent::NCC_CAVE_LHT:
-		return GetClusterComponent("left_hand_target");
-	case ENamedClusterComponent::NCC_CAVE_RHT:
-		return GetClusterComponent("right_hand_target");
-	case ENamedClusterComponent::NCC_SHUTTERGLASSES:
-		return GetClusterComponent("shutter_glasses");
-	case ENamedClusterComponent::NCC_ROLV_ORIGIN:
-		return GetClusterComponent("rolv_origin");
-	case ENamedClusterComponent::NCC_FLYSTICK:
-		return GetClusterComponent("flystick");
-	case ENamedClusterComponent::NCC_CALIBRATIO:
-		return GetClusterComponent("calibratio");
-	case ENamedClusterComponent::NCC_TRACKING_ORIGIN:
-		USceneComponent* Result;
-		if ((Result = GetClusterComponent("cave_origin")))
-			return Result;
-		if ((Result = GetClusterComponent("rolv_origin")))
-			return Result;
-		if ((Result = GetClusterComponent("tdw_origin_floor")))
-			return Result;
-		return nullptr;
-	default:
-		return nullptr;
-	}
+	return 0;
 }
 
 void URWTHVRUtilities::ShowErrorAndQuit(UWorld* WorldContext, const FString& Message)
diff --git a/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h b/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
index 490dffdf9445a9578e43cf15f6b062b73b70baf1..abe9255dd701b20595c5ee56caff2702f1d60a36 100644
--- a/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
+++ b/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
@@ -6,8 +6,6 @@
 #include "LiveLinkRole.h"
 #include "Pawn/Navigation/CollisionHandlingMovement.h"
 
-#include "Components/DisplayClusterSceneComponentSyncParent.h"
-
 #include "RWTHVRPawn.generated.h"
 
 class UInputMappingContext;
@@ -27,6 +25,8 @@ class RWTHVRTOOLKIT_API ARWTHVRPawn : public APawn
 public:
 	ARWTHVRPawn(const FObjectInitializer& ObjectInitializer);
 
+	virtual void BeginPlay() override;
+
 	virtual void Tick(float DeltaSeconds) override;
 
 	virtual void NotifyControllerChanged() override;
@@ -54,8 +54,8 @@ public:
 	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Camera")
 	UCameraComponent* HeadCameraComponent;
 
-	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Camera")
-	UDisplayClusterSceneComponentSyncParent* SyncComponent;
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn")
+	USceneComponent* SyncComponent;
 
 	// LiveLink functionality
 
diff --git a/Source/RWTHVRToolkit/Public/Utility/RWTHVRUtilities.h b/Source/RWTHVRToolkit/Public/Utility/RWTHVRUtilities.h
index 978b12b962c0e32bd655a74b80a55f77f6a6b0d3..c6b547336d00f2b88633806be95456597d2af12f 100644
--- a/Source/RWTHVRToolkit/Public/Utility/RWTHVRUtilities.h
+++ b/Source/RWTHVRToolkit/Public/Utility/RWTHVRUtilities.h
@@ -2,7 +2,6 @@
 
 #include "CoreMinimal.h"
 #include "Kismet/BlueprintFunctionLibrary.h"
-#include "UObject/ConstructorHelpers.h"
 
 #include "RWTHVRUtilities.generated.h"
 
@@ -12,75 +11,25 @@
  */
 DECLARE_LOG_CATEGORY_EXTERN(Toolkit, Log, All);
 
-UENUM(BlueprintType)
-enum class ENamedClusterComponent : uint8
-{
-	/* CAVE Specific */
-	NCC_CAVE_ORIGIN UMETA(DisplayName = "CAVE Origin"),
-	NCC_CAVE_CENTER UMETA(DisplayName = "CAVE Center"),
-	NCC_CAVE_LHT UMETA(DisplayName = "CAVE Left Hand Target"),
-	NCC_CAVE_RHT UMETA(DisplayName = "CAVE Right Hand Target"),
-
-	/* ROLV Specific */
-	NCC_ROLV_ORIGIN UMETA(DisplayName = "ROLV Origin"),
-
-	/* Non Specific */
-	NCC_CALIBRATIO UMETA(DisplayName = "Calibratio Motion to Photon Measurement Device"),
-	NCC_SHUTTERGLASSES UMETA(DisplayName = "CAVE/ROLV/TDW Shutter Glasses"),
-	NCC_FLYSTICK UMETA(DisplayName = "CAVE/ROLV/TDW Flystick"),
-	NCC_TRACKING_ORIGIN UMETA(DisplayName = "CAVE/ROLV/TDW Origin")
-};
-
-UENUM()
-enum class EEyeStereoOffset
-{
-	None,
-	Left,
-	Right
-};
-
 UCLASS()
 class RWTHVRTOOLKIT_API URWTHVRUtilities : public UBlueprintFunctionLibrary
 {
 	GENERATED_BODY()
 
 public:
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
+	UFUNCTION(BlueprintPure, Category = "RWTHVRToolkit|Platform")
 	static bool IsDesktopMode();
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
-	static bool IsRoomMountedMode();
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
+	UFUNCTION(BlueprintPure, Category = "RWTHVRToolkit|Platform")
 	static bool IsHeadMountedMode();
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
-	static bool IsCave();
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
-	static bool IsRolv();
-
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
+	UFUNCTION(BlueprintPure, Category = "RWTHVRToolkit|Platform")
+	static bool IsRoomMountedMode();
+	UFUNCTION(BlueprintPure, Category = "RWTHVRToolkit|Platform")
 	static bool IsPrimaryNode();
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
-	static bool IsSecondaryNode();
 
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
-	static FString GetNodeName();
 	/* Distance in meters */
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
+	UFUNCTION(BlueprintPure, Category = "RWTHVRToolkit")
 	static float GetEyeDistance();
 
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
-	static EEyeStereoOffset GetNodeEyeType();
-
-	static void ShowErrorAndQuit(const FString& Message, bool ShouldQuit, const UObject* WorldContext);
-
-	// Get Component of Display Cluster by it's name, which is specified in the nDisplay config
-	UE_DEPRECATED(5.4, "GetClusterComponent has been removed because it is obsolete.")
-	UFUNCTION(BlueprintPure, BlueprintCallable, Category = "DisplayCluster", meta = (DeprecatedFunction))
-	static USceneComponent* GetClusterComponent(const FString& Name);
-
-	UE_DEPRECATED(5.4, "GetNamedClusterComponent has been removed because it is obsolete.")
-	UFUNCTION(BlueprintPure, BlueprintCallable, Category = "DisplayCluster", meta = (DeprecatedFunction))
-	static USceneComponent* GetNamedClusterComponent(const ENamedClusterComponent& Component);
-
 	UFUNCTION(BlueprintCallable)
 	static void ShowErrorAndQuit(UWorld* WorldContext, const FString& Message);
 };
diff --git a/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs b/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs
index 930dc9129b0fbcf2ba9b275595cea42f42866238..a949553aaa90bd704c53e9ace2f749073a5441bf 100644
--- a/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs
+++ b/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs
@@ -49,15 +49,23 @@ public class RWTHVRToolkit : ModuleRules
 		DynamicallyLoadedModuleNames.AddRange(
 			new string[]{}
 		);
-		
-		if(Target.Platform == UnrealTargetPlatform.Win64 || Target.Platform == UnrealTargetPlatform.Linux)
+
+		if (IsPluginEnabledForTarget("RWTHVRCluster", base.Target))
 		{
-			PublicDependencyModuleNames.Add("DisplayCluster");
-			PublicDefinitions.Add("PLATFORM_SUPPORTS_NDISPLAY=1");
+			PrivateDependencyModuleNames.Add("RWTHVRCluster");
+			PrivateDependencyModuleNames.Add("DisplayCluster");
+			PublicDefinitions.Add("PLATFORM_SUPPORTS_CLUSTER=1");
 		}
 		else
-		{
-			PublicDefinitions.Add("PLATFORM_SUPPORTS_NDISPLAY=0");
+		{			
+			PublicDefinitions.Add("PLATFORM_SUPPORTS_CLUSTER=0");
 		}
 	}
+	
+	private static bool IsPluginEnabledForTarget(string PluginName, ReadOnlyTargetRules Target)
+	{
+		var PL = Plugins.GetPlugin(PluginName);
+		return PL != null && Target.ProjectFile != null && Plugins.IsPluginEnabledForTarget(PL,
+			ProjectDescriptor.FromFile(Target.ProjectFile), Target.Platform, Target.Configuration, Target.Type);
+	}
 }