diff --git a/Config/DefaultRWTHVRToolkit.ini b/Config/DefaultRWTHVRToolkit.ini
index e9585a9f68eb75f31e4e7455de23095ddf501da1..5e0f330705848d2342828dce06c75fa670d760d4 100644
--- a/Config/DefaultRWTHVRToolkit.ini
+++ b/Config/DefaultRWTHVRToolkit.ini
@@ -20,7 +20,13 @@
 +PropertyRedirects = (OldName="/Script/RWTHVRToolkit.DirectInteractionComponent.PreviousGrabBehavioursInRange",NewName="/Script/RWTHVRToolkit.DirectInteractionComponent.PreviousInteractableComponentsInRange")
 +PropertyRedirects = (OldName="/Script/RWTHVRToolkit.DirectInteractionComponent.CurrentGrabBehavioursInRange",NewName="/Script/RWTHVRToolkit.DirectInteractionComponent.CurrentInteractableComponentsInRange")
 +PropertyRedirects = (OldName="/Script/RWTHVRToolkit.TurnComponent.DesktopRotation",NewName="/Script/RWTHVRToolkit.TurnComponent.DesktopTurnCondition")
-+PropertyRedirects = (OldName="/Script/RWTHVRToolkit.TurnComponent.Turn",NewName="/Script/RWTHVRToolkit.TurnComponent.XRTurn")
-+PropertyRedirects = (OldName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.WidgetClickInputAction",NewName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.WidgetLeftClickInputAction")
-+FunctionRedirects = (OldName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.OnBeginClick",NewName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.OnBeginLeftClick")
-+FunctionRedirects = (OldName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.OnEndClick",NewName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.OnEndLeftClick")
\ No newline at end of file
++PropertyRedirects = (OldName="/Script/RWTHVRToolkit.TurnComponent.Turn",NewName="/Script/RWTHVRToolkit.TurnComponent.XRTurn")
++PropertyRedirects = (OldName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.WidgetClickInputAction",NewName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.WidgetLeftClickInputAction")
++FunctionRedirects = (OldName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.OnBeginClick",NewName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.OnBeginLeftClick")
++FunctionRedirects = (OldName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.OnEndClick",NewName="/Script/RWTHVRToolkit.RWTHVRWidgetInteractionComponent.OnEndLeftClick")
++FunctionRedirects=(OldName="/Script/RWTHVRToolkit.UBaseInteractionComponent.OnBeginInteraction",NewName="/Script/RWTHVRToolkit.UBaseInteractionComponent.OnBeginInteractionInputAction")
++FunctionRedirects=(OldName="/Script/RWTHVRToolkit.UBaseInteractionComponent.OnEndInteraction",NewName="/Script/RWTHVRToolkit.UBaseInteractionComponent.OnEndInteractionInputAction")
++FunctionRedirects=(OldName="/Script/RWTHVRToolkit.UBaseInteractionComponent.MulticastHoverBehaviourStartRpc",NewName="/Script/RWTHVRToolkit.UBaseInteractionComponent.MulticastHoverBehaviourReplicationStartRpc")
++FunctionRedirects=(OldName="/Script/RWTHVRToolkit.UBaseInteractionComponent.MulticastActionBehaviourStartRpc",NewName="/Script/RWTHVRToolkit.UBaseInteractionComponent.MulticastActionBehaviourReplicationStartRpc")
++FunctionRedirects=(OldName="/Script/RWTHVRToolkit.ActionBehaviour.OnActionStart",NewName="/Script/RWTHVRToolkit.ActionBehaviour.OnActionEvent")
++PropertyRedirects=(OldName="/Script/RWTHVRToolkit.ActionBehaviour.OnActionBeginEvent",NewName="/Script/RWTHVRToolkit.ActionBehaviour.OnActionEventEvent")
\ No newline at end of file
diff --git a/Content/Input/Default_IMC/IMC_General.uasset b/Content/Input/Default_IMC/IMC_General.uasset
index 86a5cc420d316fec6cfe82086febf9292630569a..bfc100b8d7b66753e20343576c91f76dc51cfaac 100644
Binary files a/Content/Input/Default_IMC/IMC_General.uasset and b/Content/Input/Default_IMC/IMC_General.uasset differ
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactables/ActionBehaviour.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactables/ActionBehaviour.cpp
index 4e9c3fd662dd3baa2a30fddd8f516022e4efa9ba..ff1d027208025476ce073a15bd224ca5c9ccb9f0 100644
--- a/Source/RWTHVRToolkit/Private/Interaction/Interactables/ActionBehaviour.cpp
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactables/ActionBehaviour.cpp
@@ -6,27 +6,14 @@
 // We disable ticking here, as we are mainly interested in the events
 UActionBehaviour::UActionBehaviour() { PrimaryComponentTick.bCanEverTick = false; }
 
-void UActionBehaviour::OnActionStart(USceneComponent* TriggeredComponent, const UInputAction* InputAction,
+void UActionBehaviour::OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
 									 const FInputActionValue& Value)
 {
 }
 
-void UActionBehaviour::OnActionEnd(USceneComponent* TriggeredComponent, const UInputAction* InputAction,
-								   const FInputActionValue& Value)
-{
-}
-
 void UActionBehaviour::BeginPlay()
 {
 	Super::BeginPlay();
 
-	OnActionBeginEvent.AddDynamic(this, &UActionBehaviour::OnActionStart);
-	OnActionEndEvent.AddDynamic(this, &UActionBehaviour::OnActionEnd);
-}
-
-// Called every frame
-void UActionBehaviour::TickComponent(float DeltaTime, ELevelTick TickType,
-									 FActorComponentTickFunction* ThisTickFunction)
-{
-	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+	OnActionEventEvent.AddDynamic(this, &UActionBehaviour::OnActionEvent);
 }
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactables/BaseBehaviour.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactables/BaseBehaviour.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..95488260668c095eb5dece6895555ce808e2fd11
--- /dev/null
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactables/BaseBehaviour.cpp
@@ -0,0 +1,4 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Interaction/Interactables/BaseBehaviour.h"
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactables/GrabBehavior.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactables/GrabBehavior.cpp
index 1537243da0b6c92a901decadce793bc8bd819bbb..502a4be532df57e6f3214e859db8eb4729b15482 100644
--- a/Source/RWTHVRToolkit/Private/Interaction/Interactables/GrabBehavior.cpp
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactables/GrabBehavior.cpp
@@ -3,10 +3,26 @@
 
 #include "Interaction/Interactables/GrabBehavior.h"
 #include "Interaction/Interactables/InteractableComponent.h"
+#include "Interaction/Interactables/InteractionEventType.h"
 #include "Logging/StructuredLog.h"
+#include "Pawn/Navigation/CollisionHandlingMovement.h"
 #include "Serialization/JsonTypes.h"
 #include "Utility/RWTHVRUtilities.h"
 
+UGrabBehavior::UGrabBehavior()
+{
+	SetIsReplicatedByDefault(true);
+	bExecuteOnServer = true;
+	bExecuteOnAllClients = false;
+}
+
+void UGrabBehavior::BeginPlay()
+{
+	Super::BeginPlay();
+
+	OnActionReplicationStartedOriginatorEvent.AddDynamic(this, &UGrabBehavior::ReplicationOriginaterClientCallback);
+}
+
 UPrimitiveComponent* UGrabBehavior::GetFirstComponentSimulatingPhysics(const AActor* TargetActor)
 {
 	TArray<UPrimitiveComponent*> PrimitiveComponents;
@@ -33,16 +49,79 @@ UPrimitiveComponent* UGrabBehavior::GetHighestParentSimulatingPhysics(UPrimitive
 
 	return Comp;
 }
+void UGrabBehavior::ReplicationOriginaterClientCallback(USceneComponent* TriggerComponent,
+														const EInteractionEventType EventType,
+														const FInputActionValue& Value)
+{
+	const USceneComponent* CurrentAttachParent = Cast<USceneComponent>(TriggerComponent->GetAttachParent());
+	HandleCollisionHandlingMovement(CurrentAttachParent, EventType);
+}
 
-void UGrabBehavior::OnActionStart(USceneComponent* TriggeredComponent, const UInputAction* InputAction,
+void UGrabBehavior::HandleCollisionHandlingMovement(const USceneComponent* CurrentAttachParent,
+													const EInteractionEventType EventType)
+{
+	auto CHM = CurrentAttachParent->GetOwner()->GetComponentByClass<UCollisionHandlingMovement>();
+	if (!CHM)
+		return;
+
+	if (EventType == EInteractionEventType::InteractionStart)
+	{
+		// Add to ignore list for collision handling movement, if it exists
+		if (bIgnoreGrabbedActorInCollisionMovement)
+		{
+			bWasAddedToIgnore = CHM->AddActorToIgnore(GetOwner());
+		}
+	}
+	else
+	{
+		// If our attach parent has a collision handling component, remove
+		if (bWasAddedToIgnore)
+		{
+			CHM->RemoveActorFromIgnore(GetOwner());
+		}
+	}
+}
+void UGrabBehavior::OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
 								  const FInputActionValue& Value)
+{
+	if (EventType == EInteractionEventType::InteractionStart)
+	{
+		StartGrab(TriggerComponent);
+	}
+	else
+	{
+		EndGrab(TriggerComponent);
+	}
+}
+
+bool UGrabBehavior::TryRelease()
+{
+	if (!bObjectGrabbed)
+	{
+		UE_LOGFMT(Toolkit, Display, "UGrabBehavior::TryRelease: bObjectGrabbed was false!");
+		return false;
+	}
+
+	if (MyPhysicsComponent)
+	{
+		MyPhysicsComponent->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
+		MyPhysicsComponent->SetSimulatePhysics(bWasSimulatingPhysics);
+	}
+	else
+	{
+		GetOwner()->GetRootComponent()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
+	}
+	bObjectGrabbed = false;
+	return true;
+}
+void UGrabBehavior::StartGrab(USceneComponent* TriggerComponent)
 {
 	if (bObjectGrabbed)
 	{
 		return;
 	}
 
-	USceneComponent* CurrentAttachParent = Cast<USceneComponent>(TriggeredComponent->GetAttachParent());
+	USceneComponent* CurrentAttachParent = Cast<USceneComponent>(TriggerComponent->GetAttachParent());
 	const FAttachmentTransformRules Rules = FAttachmentTransformRules(EAttachmentRule::KeepWorld, false);
 
 	if (MyPhysicsComponent = GetFirstComponentSimulatingPhysics(GetOwner()); MyPhysicsComponent != nullptr)
@@ -72,18 +151,19 @@ void UGrabBehavior::OnActionStart(USceneComponent* TriggeredComponent, const UIn
 		GetOwner()->GetComponents<UInteractableComponent>(Interactables, false);
 		for (UInteractableComponent* Interactable : Interactables)
 		{
-			Interactable->RestrictInteractionToComponent(TriggeredComponent);
+			Interactable->RestrictInteractionToComponent(TriggerComponent);
 		}
 	}
 
 	OnGrabStartEvent.Broadcast(CurrentAttachParent, MyPhysicsComponent);
+
+	// Add to ignore list for collision handling movement, if it exists
+	HandleCollisionHandlingMovement(CurrentAttachParent, InteractionStart);
 }
 
-void UGrabBehavior::OnActionEnd(USceneComponent* TriggeredComponent, const UInputAction* InputAction,
-								const FInputActionValue& Value)
+void UGrabBehavior::EndGrab(USceneComponent* TriggerComponent)
 {
-
-	USceneComponent* CurrentAttachParent = Cast<USceneComponent>(TriggeredComponent->GetAttachParent());
+	USceneComponent* CurrentAttachParent = Cast<USceneComponent>(TriggerComponent->GetAttachParent());
 
 	// We try to release the attached component. If it is not succesful we log and return. Otherwise, we continue.
 	if (!TryRelease())
@@ -106,24 +186,7 @@ void UGrabBehavior::OnActionEnd(USceneComponent* TriggeredComponent, const UInpu
 			Interactable->ResetRestrictInteraction();
 		}
 	}
-}
 
-bool UGrabBehavior::TryRelease()
-{
-	if (!bObjectGrabbed)
-	{
-		return false;
-	}
-
-	if (MyPhysicsComponent)
-	{
-		MyPhysicsComponent->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
-		MyPhysicsComponent->SetSimulatePhysics(bWasSimulatingPhysics);
-	}
-	else
-	{
-		GetOwner()->GetRootComponent()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
-	}
-	bObjectGrabbed = false;
-	return true;
+	// If our attach parent has a collision handling component, remove
+	HandleCollisionHandlingMovement(CurrentAttachParent, InteractionEnd);
 }
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactables/HoverBehaviour.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactables/HoverBehaviour.cpp
index 96897613cd2f536cb9625a9b395ce49181656e70..f78e75fabfa434b1396d7bffc1354adee7f49441 100644
--- a/Source/RWTHVRToolkit/Private/Interaction/Interactables/HoverBehaviour.cpp
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactables/HoverBehaviour.cpp
@@ -3,14 +3,13 @@
 
 #include "Interaction/Interactables/HoverBehaviour.h"
 
-void UHoverBehaviour::OnHoverStart(const USceneComponent* TriggeredComponent, FHitResult Hit) {}
-
-void UHoverBehaviour::OnHoverEnd(const USceneComponent* TriggeredComponent) {}
 
+void UHoverBehaviour::OnHoverEvent(const USceneComponent* TriggerComponent, EInteractionEventType EventType,
+								   const FHitResult& Hit)
+{
+}
 void UHoverBehaviour::BeginPlay()
 {
 	Super::BeginPlay();
-
-	OnHoverStartEvent.AddDynamic(this, &UHoverBehaviour::OnHoverStart);
-	OnHoverEndEvent.AddDynamic(this, &UHoverBehaviour::OnHoverEnd);
+	OnHoverEventEvent.AddDynamic(this, &UHoverBehaviour::OnHoverEvent);
 }
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactables/IntenSelect/IntenSelectable.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactables/IntenSelect/IntenSelectable.cpp
index 90a4e2956a3d06f1cb42d6219e4f7f1ff31ca050..276269b3dc0f389621e6415a71887cf3170a9e79 100644
--- a/Source/RWTHVRToolkit/Private/Interaction/Interactables/IntenSelect/IntenSelectable.cpp
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactables/IntenSelect/IntenSelectable.cpp
@@ -62,7 +62,8 @@ void UIntenSelectable::HandleOnSelectStartEvents(const UIntenSelectComponent* In
 {
 	for (const UHoverBehaviour* CurrentHoverBehaviour : OnHoverBehaviours)
 	{
-		CurrentHoverBehaviour->OnHoverStartEvent.Broadcast(IntenSelect, HitResult);
+		CurrentHoverBehaviour->OnHoverEventEvent.Broadcast(IntenSelect, EInteractionEventType::InteractionStart,
+														   HitResult);
 	}
 }
 
@@ -70,7 +71,9 @@ void UIntenSelectable::HandleOnSelectEndEvents(const UIntenSelectComponent* Inte
 {
 	for (const UHoverBehaviour* CurrentHoverBehaviour : OnHoverBehaviours)
 	{
-		CurrentHoverBehaviour->OnHoverEndEvent.Broadcast(IntenSelect);
+		FHitResult EmptyHit;
+		CurrentHoverBehaviour->OnHoverEventEvent.Broadcast(IntenSelect, EInteractionEventType::InteractionEnd,
+														   EmptyHit);
 	}
 }
 
@@ -79,8 +82,8 @@ void UIntenSelectable::HandleOnClickStartEvents(UIntenSelectComponent* IntenSele
 	for (const UActionBehaviour* CurrentActionBehaviour : OnActionBehaviours)
 	{
 		FInputActionValue EmptyInputActionValue{};
-		const UInputAction* EmptyInputAction{};
-		CurrentActionBehaviour->OnActionBeginEvent.Broadcast(IntenSelect, EmptyInputAction, EmptyInputActionValue);
+		CurrentActionBehaviour->OnActionEventEvent.Broadcast(IntenSelect, EInteractionEventType::InteractionStart,
+															 EmptyInputActionValue);
 	}
 }
 
@@ -88,8 +91,8 @@ void UIntenSelectable::HandleOnClickEndEvents(UIntenSelectComponent* IntenSelect
 {
 	for (const UActionBehaviour* CurrentActionBehaviour : OnActionBehaviours)
 	{
-		const UInputAction* EmptyInputActionValue{};
-		CurrentActionBehaviour->OnActionEndEvent.Broadcast(IntenSelect, EmptyInputActionValue, InputValue);
+		CurrentActionBehaviour->OnActionEventEvent.Broadcast(IntenSelect, EInteractionEventType::InteractionEnd,
+															 InputValue);
 	}
 }
 
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactables/InteractableComponent.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactables/InteractableComponent.cpp
index 29ee75b08e1d37147671228e167c09e4cde462f8..4f1c256218355ce5c142d010bcc3a460491f08f8 100644
--- a/Source/RWTHVRToolkit/Private/Interaction/Interactables/InteractableComponent.cpp
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactables/InteractableComponent.cpp
@@ -4,6 +4,9 @@
 #include "Interaction/Interactables/InteractableComponent.h"
 #include "Interaction/Interactables/ActionBehaviour.h"
 #include "Interaction/Interactables/HoverBehaviour.h"
+#include "Interaction/Interactors/UBaseInteractionComponent.h"
+#include "Logging/StructuredLog.h"
+#include "Utility/RWTHVRUtilities.h"
 
 void UInteractableComponent::RestrictInteractionToComponents(const TArray<USceneComponent*>& Components)
 {
@@ -39,9 +42,8 @@ void UInteractableComponent::BeginPlay()
 	InitDefaultBehaviourReferences();
 }
 
-// This functions dispatches the HoverStart Event to the attached Hover Behaviour Components
-void UInteractableComponent::HandleOnHoverStartEvents(USceneComponent* TriggerComponent,
-													  const EInteractorType Interactor)
+void UInteractableComponent::HandleOnHoverEvents(USceneComponent* TriggerComponent, const EInteractorType Interactor,
+												 const EInteractionEventType EventType)
 {
 	// We early return if there the InteractorFilter is set and the Interactor is allowed.
 	if (!(InteractorFilter == EInteractorType::None || InteractorFilter & Interactor))
@@ -52,34 +54,47 @@ void UInteractableComponent::HandleOnHoverStartEvents(USceneComponent* TriggerCo
 		return;
 
 	// Broadcast event to all HoverBehaviours
-	for (const UHoverBehaviour* b : OnHoverBehaviours)
+	for (UHoverBehaviour* HoverBehaviour : OnHoverBehaviours)
 	{
-		b->OnHoverStartEvent.Broadcast(TriggerComponent, HitResult);
-	}
-}
-
-// This functions dispatches the HoverEnd Event to the attached Hover Behaviour Components
-void UInteractableComponent::HandleOnHoverEndEvents(USceneComponent* TriggerComponent, const EInteractorType Interactor)
-{
-	// We early return if there the InteractorFilter is set and the Interactor is allowed.
-	if (!(InteractorFilter == EInteractorType::None || InteractorFilter & Interactor))
-		return;
-
-	// We early return if the source Interactor is not part of the allowed components
-	if (!IsComponentAllowed(TriggerComponent))
-		return;
-
-	// Broadcast event to all HoverBehaviours
-	for (const UHoverBehaviour* b : OnHoverBehaviours)
-	{
-		b->OnHoverEndEvent.Broadcast(TriggerComponent);
+		// Check if we need to replicate the action to the server
+		if (HoverBehaviour->bExecuteOnServer)
+		{
+			// Request the server to execute our behaviour.
+			// As we can only execute RPCs from an owned object, and we don't necessarily own this actor, pipe it
+			// through the interactor. Because behaviours can also be triggered from non-interactors, only do this on a
+			// successful cast
+
+			auto* InteractorComponent = Cast<UUBaseInteractionComponent>(TriggerComponent);
+			if (!InteractorComponent)
+			{
+				UE_LOGFMT(Toolkit, Warning,
+						  "Interaction: TriggerComponent {TriggerComponent} is not a UUBaseInteractionComponent. Only "
+						  "UUBaseInteractionComponent TriggerComponents can be replicated.",
+						  TriggerComponent->GetName());
+				return;
+			}
+			InteractorComponent->RequestHoverBehaviourReplicationStart(HoverBehaviour, EventType, HitResult);
+
+			// Broadcast local callback
+			HoverBehaviour->OnHoverReplicationStartedOriginatorEvent.Broadcast(TriggerComponent, EventType, HitResult);
+		}
+		else if (HoverBehaviour->bExecuteOnAllClients)
+		{
+			UE_LOGFMT(Toolkit, Warning,
+					  "Interaction: Behaviour {BehaviourName} has bExecuteOnAllClients=true, which requires "
+					  "bExecuteOnServer also set to true, which is not the case.",
+					  HoverBehaviour->GetName());
+		}
+		else // skip replication altogether (default case)
+		{
+			HoverBehaviour->OnHoverEventEvent.Broadcast(TriggerComponent, EventType, HitResult);
+		}
 	}
 }
 
 // This functions dispatches the ActionStart Event to the attached Action Behaviour Components
-void UInteractableComponent::HandleOnActionStartEvents(USceneComponent* TriggerComponent,
-													   const UInputAction* InputAction, const FInputActionValue& Value,
-													   const EInteractorType Interactor)
+void UInteractableComponent::HandleOnActionEvents(USceneComponent* TriggerComponent, const EInteractorType Interactor,
+												  const EInteractionEventType EventType, const FInputActionValue& Value)
 {
 	// We early return if there the InteractorFilter is set and the Interactor is allowed.
 	if (!(InteractorFilter == EInteractorType::None || InteractorFilter & Interactor))
@@ -90,28 +105,41 @@ void UInteractableComponent::HandleOnActionStartEvents(USceneComponent* TriggerC
 		return;
 
 	// Broadcast event to all ActionBehaviours
-	for (const UActionBehaviour* b : OnActionBehaviours)
+	for (UActionBehaviour* ActionBehaviour : OnActionBehaviours)
 	{
-		b->OnActionBeginEvent.Broadcast(TriggerComponent, InputAction, Value);
-	}
-}
-
-// This functions dispatches the ActionEnd Event to the attached Action Behaviour Components
-void UInteractableComponent::HandleOnActionEndEvents(USceneComponent* TriggerComponent, const UInputAction* InputAction,
-													 const FInputActionValue& Value, const EInteractorType Interactor)
-{
-	// We early return if there the InteractorFilter is set and the Interactor is allowed.
-	if (!(InteractorFilter == EInteractorType::None || InteractorFilter & Interactor))
-		return;
-
-	// We early return if the source Interactor is not part of the allowed components
-	if (!IsComponentAllowed(TriggerComponent))
-		return;
-
-	// Broadcast event to all ActionBehaviours
-	for (const UActionBehaviour* b : OnActionBehaviours)
-	{
-		b->OnActionEndEvent.Broadcast(TriggerComponent, InputAction, Value);
+		// Check if we need to replicate the action to the server
+		if (ActionBehaviour->bExecuteOnServer)
+		{
+			// Request the server to execute our behaviour.
+			// As we can only execute RPCs from an owned object, and we don't necessarily own this actor, pipe it
+			// through the interactor. Because behaviours can also be triggered from non-interactors, only do this on a
+			// successful cast
+
+			auto* InteractorComponent = Cast<UUBaseInteractionComponent>(TriggerComponent);
+			if (!InteractorComponent)
+			{
+				UE_LOGFMT(Toolkit, Warning,
+						  "Interaction: TriggerComponent {TriggerComponent} is not a UUBaseInteractionComponent. Only "
+						  "UUBaseInteractionComponent TriggerComponents can be replicated.",
+						  TriggerComponent->GetName());
+				return;
+			}
+			InteractorComponent->RequestActionBehaviourReplicationStart(ActionBehaviour, EventType, Value);
+
+			// Broadcast local callback
+			ActionBehaviour->OnActionReplicationStartedOriginatorEvent.Broadcast(TriggerComponent, EventType, Value);
+		}
+		else if (ActionBehaviour->bExecuteOnAllClients)
+		{
+			UE_LOGFMT(Toolkit, Warning,
+					  "Interaction: Behaviour {BehaviourName} has bExecuteOnAllClients=true, which requires "
+					  "bExecuteOnServer also set to true, which is not the case.",
+					  ActionBehaviour->GetName());
+		}
+		else // skip replication altogether (default case)
+		{
+			ActionBehaviour->OnActionEventEvent.Broadcast(TriggerComponent, EventType, Value);
+		}
 	}
 }
 
@@ -150,4 +178,4 @@ bool UInteractableComponent::IsComponentAllowed(USceneComponent* Component) cons
 		}
 	}
 	return true;
-}
+}
\ No newline at end of file
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactors/DirectInteractionComponent.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactors/DirectInteractionComponent.cpp
index 7c81cb805f86e3d9a4de4bfa2c594ab02a683719..18e515817875cb60c35090b4dbfb4300051b8dac 100644
--- a/Source/RWTHVRToolkit/Private/Interaction/Interactors/DirectInteractionComponent.cpp
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactors/DirectInteractionComponent.cpp
@@ -6,6 +6,7 @@
 #include "EnhancedInputComponent.h"
 #include "Interaction/Interactables/InteractableComponent.h"
 #include "Interaction/Interactables/InteractionBitSet.h"
+#include "Interaction/Interactables/InteractionEventType.h"
 
 #include "Kismet/GameplayStatics.h"
 #include "Logging/StructuredLog.h"
@@ -30,7 +31,7 @@ void UDirectInteractionComponent::TickComponent(float DeltaTime, ELevelTick Tick
 	TArray<FHitResult> OutHits;
 	const ETraceTypeQuery TraceType = UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_PhysicsBody);
 
-	auto DebugTrace = bShowDebugTrace ? EDrawDebugTrace::ForOneFrame : EDrawDebugTrace::None;
+	const auto DebugTrace = bShowDebugTrace ? EDrawDebugTrace::ForOneFrame : EDrawDebugTrace::None;
 
 	UKismetSystemLibrary::SphereTraceMulti(GetWorld(), GetAttachParent()->GetComponentLocation(),
 										   GetAttachParent()->GetComponentLocation(), InteractionSphereRadius,
@@ -59,7 +60,8 @@ void UDirectInteractionComponent::TickComponent(float DeltaTime, ELevelTick Tick
 		if (!PreviousInteractableComponentsInRange.Contains(CurrentInteractableComp))
 		{
 			PreviousInteractableComponentsInRange.AddUnique(CurrentInteractableComp);
-			CurrentInteractableComp->HandleOnHoverStartEvents(this, EInteractorType::Direct);
+			CurrentInteractableComp->HandleOnHoverEvents(this, EInteractorType::Direct,
+														 EInteractionEventType::InteractionStart);
 		}
 	}
 
@@ -71,7 +73,8 @@ void UDirectInteractionComponent::TickComponent(float DeltaTime, ELevelTick Tick
 		if (!CurrentInteractableCompsInRange.Contains(PrevInteractableComp))
 		{
 			ComponentsToRemove.AddUnique(PrevInteractableComp);
-			PrevInteractableComp->HandleOnHoverEndEvents(this, EInteractorType::Direct);
+			PrevInteractableComp->HandleOnHoverEvents(this, EInteractorType::Direct,
+													  EInteractionEventType::InteractionEnd);
 		}
 	}
 
@@ -97,12 +100,12 @@ void UDirectInteractionComponent::SetupPlayerInput(UInputComponent* PlayerInputC
 		return;
 
 	EI->BindAction(InteractionInputAction, ETriggerEvent::Started, this,
-				   &UDirectInteractionComponent::OnBeginInteraction);
+				   &UDirectInteractionComponent::OnBeginInteractionInputAction);
 	EI->BindAction(InteractionInputAction, ETriggerEvent::Completed, this,
-				   &UDirectInteractionComponent::OnEndInteraction);
+				   &UDirectInteractionComponent::OnEndInteractionInputAction);
 }
 
-void UDirectInteractionComponent::OnBeginInteraction(const FInputActionValue& Value)
+void UDirectInteractionComponent::OnBeginInteractionInputAction(const FInputActionValue& Value)
 {
 	const FVector InteractionLocation = GetAttachParent()->GetComponentLocation();
 
@@ -115,7 +118,7 @@ void UDirectInteractionComponent::OnBeginInteraction(const FInputActionValue& Va
 			CurrentInteractableComponentsInRange,
 			[&](auto Element)
 			{ return FVector(Element->GetOwner()->GetActorLocation() - InteractionLocation).Size(); });
-		MinElement->HandleOnActionStartEvents(this, InteractionInputAction, Value, EInteractorType::Direct);
+		MinElement->HandleOnActionEvents(this, EInteractorType::Direct, EInteractionEventType::InteractionStart, Value);
 		CurrentlyInteractedComponents = {MinElement};
 	}
 	else
@@ -125,19 +128,21 @@ void UDirectInteractionComponent::OnBeginInteraction(const FInputActionValue& Va
 											  CurrentInteractableComponentsInRange.Num());
 		for (UInteractableComponent* InteractableComp : CurrentInteractableComponentsInRange)
 		{
-			InteractableComp->HandleOnActionStartEvents(this, InteractionInputAction, Value, EInteractorType::Direct);
+			InteractableComp->HandleOnActionEvents(this, EInteractorType::Direct,
+												   EInteractionEventType::InteractionStart, Value);
 			CurrentlyInteractedComponents.Add(InteractableComp);
 		}
 	}
 }
 
-void UDirectInteractionComponent::OnEndInteraction(const FInputActionValue& Value)
+void UDirectInteractionComponent::OnEndInteractionInputAction(const FInputActionValue& Value)
 {
 	for (auto& Component : CurrentlyInteractedComponents)
 	{
 		if (Component.IsValid())
 		{
-			Component->HandleOnActionEndEvents(this, InteractionInputAction, Value, EInteractorType::Direct);
+			Component->HandleOnActionEvents(this, EInteractorType::Direct, EInteractionEventType::InteractionEnd,
+											Value);
 		}
 	}
 	CurrentlyInteractedComponents.Empty();
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactors/RaycastInteractionComponent.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactors/RaycastInteractionComponent.cpp
index 94afba4b4426fe1b861d787826f0e1cc11d6583b..42e1aeea61a94302b199769e8f79d50314c5cc89 100644
--- a/Source/RWTHVRToolkit/Private/Interaction/Interactors/RaycastInteractionComponent.cpp
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactors/RaycastInteractionComponent.cpp
@@ -5,6 +5,7 @@
 
 #include "EnhancedInputComponent.h"
 #include "Interaction/Interactables/InteractableComponent.h"
+#include "Interaction/Interactables/InteractionEventType.h"
 #include "Kismet/KismetSystemLibrary.h"
 
 // Sets default values for this component's properties
@@ -25,14 +26,13 @@ void URaycastInteractionComponent::TickComponent(float DeltaTime, ELevelTick Tic
 
 	UInteractableComponent* NewInteractableComponent = nullptr;
 
-
-	TArray<AActor*> ActorsToIgnore;
 	FHitResult Hit;
 	const ETraceTypeQuery TraceType = UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_PhysicsBody);
-	FVector TraceStart = GetAttachParent()->GetComponentLocation();
-	FVector TraceEnd = GetAttachParent()->GetComponentLocation() + TraceLength * GetAttachParent()->GetForwardVector();
+	const FVector TraceStart = GetAttachParent()->GetComponentLocation();
+	const FVector TraceEnd =
+		GetAttachParent()->GetComponentLocation() + TraceLength * GetAttachParent()->GetForwardVector();
 
-	auto DebugTrace = bShowDebugTrace ? EDrawDebugTrace::ForOneFrame : EDrawDebugTrace::None;
+	const auto DebugTrace = bShowDebugTrace ? EDrawDebugTrace::ForOneFrame : EDrawDebugTrace::None;
 
 	UKismetSystemLibrary::LineTraceSingle(GetWorld(), TraceStart, TraceEnd, TraceType, true, ActorsToIgnore, DebugTrace,
 										  Hit, true, FColor::Green);
@@ -53,24 +53,28 @@ void URaycastInteractionComponent::TickComponent(float DeltaTime, ELevelTick Tic
 	if (CurrentInteractable != PreviousInteractable)
 	{
 		if (CurrentInteractable && CurrentInteractable->HasInteractionTypeFlag(EInteractorType::Raycast))
-			CurrentInteractable->HandleOnHoverStartEvents(this, EInteractorType::Raycast);
+			CurrentInteractable->HandleOnHoverEvents(this, EInteractorType::Raycast,
+													 EInteractionEventType::InteractionStart);
 		if (PreviousInteractable && PreviousInteractable->HasInteractionTypeFlag(EInteractorType::Raycast))
-			PreviousInteractable->HandleOnHoverEndEvents(this, EInteractorType::Raycast);
+			PreviousInteractable->HandleOnHoverEvents(this, EInteractorType::Raycast,
+													  EInteractionEventType::InteractionEnd);
 	}
 
 	PreviousInteractable = CurrentInteractable;
 }
 
-void URaycastInteractionComponent::OnBeginInteraction(const FInputActionValue& Value)
+void URaycastInteractionComponent::OnBeginInteractionInputAction(const FInputActionValue& Value)
 {
 	if (CurrentInteractable && CurrentInteractable->HasInteractionTypeFlag(EInteractorType::Raycast))
-		CurrentInteractable->HandleOnActionStartEvents(this, InteractionInputAction, Value, EInteractorType::Raycast);
+		CurrentInteractable->HandleOnActionEvents(this, EInteractorType::Raycast,
+												  EInteractionEventType::InteractionStart, Value);
 }
 
-void URaycastInteractionComponent::OnEndInteraction(const FInputActionValue& Value)
+void URaycastInteractionComponent::OnEndInteractionInputAction(const FInputActionValue& Value)
 {
 	if (CurrentInteractable && CurrentInteractable->HasInteractionTypeFlag(EInteractorType::Raycast))
-		CurrentInteractable->HandleOnActionEndEvents(this, InteractionInputAction, Value, EInteractorType::Raycast);
+		CurrentInteractable->HandleOnActionEvents(this, EInteractorType::Raycast, EInteractionEventType::InteractionEnd,
+												  Value);
 }
 
 void URaycastInteractionComponent::SetupPlayerInput(UInputComponent* PlayerInputComponent)
@@ -86,7 +90,7 @@ void URaycastInteractionComponent::SetupPlayerInput(UInputComponent* PlayerInput
 		return;
 
 	EI->BindAction(InteractionInputAction, ETriggerEvent::Started, this,
-				   &URaycastInteractionComponent::OnBeginInteraction);
+				   &URaycastInteractionComponent::OnBeginInteractionInputAction);
 	EI->BindAction(InteractionInputAction, ETriggerEvent::Completed, this,
-				   &URaycastInteractionComponent::OnEndInteraction);
+				   &URaycastInteractionComponent::OnEndInteractionInputAction);
 }
diff --git a/Source/RWTHVRToolkit/Private/Interaction/Interactors/UBaseInteractionComponent.cpp b/Source/RWTHVRToolkit/Private/Interaction/Interactors/UBaseInteractionComponent.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..67d718c90cdff0dce7dab7cf7bff226f778877c9
--- /dev/null
+++ b/Source/RWTHVRToolkit/Private/Interaction/Interactors/UBaseInteractionComponent.cpp
@@ -0,0 +1,152 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Interaction/Interactors/UBaseInteractionComponent.h"
+
+#include "Interaction/Interactables/ActionBehaviour.h"
+#include "Interaction/Interactables/HoverBehaviour.h"
+#include "Logging/StructuredLog.h"
+#include "Utility/RWTHVRUtilities.h"
+
+// Sets default values for this component's properties
+UUBaseInteractionComponent::UUBaseInteractionComponent()
+{
+	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these
+	// features off to improve performance if you don't need them.
+	PrimaryComponentTick.bCanEverTick = true;
+	SetIsReplicatedByDefault(true);
+	// ...
+}
+
+void UUBaseInteractionComponent::SetupPlayerInput(UInputComponent* PlayerInputComponent)
+{
+	IInputExtensionInterface::SetupPlayerInput(PlayerInputComponent);
+}
+
+void UUBaseInteractionComponent::OnBeginInteractionInputAction(const FInputActionValue& Value) {}
+
+void UUBaseInteractionComponent::OnEndInteractionInputAction(const FInputActionValue& Value) {}
+
+void UUBaseInteractionComponent::RequestHoverBehaviourReplicationStart(UHoverBehaviour* Behaviour,
+																	   const EInteractionEventType EventType,
+																	   const FHitResult& Hit)
+{
+	if (GetOwner()->HasLocalNetOwner())
+	{
+		if (GetOwner()->HasAuthority())
+		{
+			HoverBehaviourReplicationStart(Behaviour, EventType, Hit);
+		}
+		else
+		{
+			// RPC
+			ServerRequestHoverBehaviourReplicationStartRpc(Behaviour, EventType, Hit);
+		}
+	}
+	else
+	{
+		UE_LOGFMT(Toolkit, Error,
+				  "Interaction: Trying to replicate HoverBehaviour {HoverBehaviour} in InteractionComponent "
+				  "{InteractionComponent} which has no local net owner!",
+				  Behaviour->GetName(), this->GetName());
+	}
+}
+
+void UUBaseInteractionComponent::HoverBehaviourReplicationStart(UHoverBehaviour* Behaviour,
+																const EInteractionEventType EventType,
+																const FHitResult& Hit)
+{
+	if (!Behaviour->bExecuteOnServer) // this should never happen
+	{
+		UE_LOGFMT(Toolkit, Error,
+				  "Interaction: Trying to execute HoverBehaviour {HoverBehaviour} in InteractionComponent "
+				  "{InteractionComponent} on the server, but bExecuteOnServer is false!",
+				  Behaviour->GetName(), this->GetName());
+		return;
+	}
+
+	// If desired, multicast to all clients (including originator)
+	if (Behaviour->bExecuteOnAllClients)
+	{
+		MulticastHoverBehaviourReplicationStartRpc(Behaviour, EventType, Hit);
+	}
+	else
+	{
+		// Execute the behaviour only on the server
+		Behaviour->OnHoverEventEvent.Broadcast(this, EventType, Hit);
+	}
+}
+
+void UUBaseInteractionComponent::ServerRequestHoverBehaviourReplicationStartRpc_Implementation(
+	UHoverBehaviour* Behaviour, const EInteractionEventType EventType, const FHitResult& Hit)
+{
+	HoverBehaviourReplicationStart(Behaviour, EventType, Hit);
+}
+
+void UUBaseInteractionComponent::MulticastHoverBehaviourReplicationStartRpc_Implementation(
+	UHoverBehaviour* Behaviour, const EInteractionEventType EventType, const FHitResult& Hit)
+{
+	Behaviour->OnHoverEventEvent.Broadcast(this, EventType, Hit);
+}
+
+void UUBaseInteractionComponent::RequestActionBehaviourReplicationStart(UActionBehaviour* Behaviour,
+																		const EInteractionEventType EventType,
+																		const FInputActionValue& Value)
+{
+	if (GetOwner()->HasLocalNetOwner())
+	{
+		if (GetOwner()->HasAuthority())
+		{
+			ActionBehaviourReplicationStart(Behaviour, EventType, Value);
+		}
+		else
+		{
+			// RPC
+			ServerRequestActionBehaviourReplicationStartRpc(Behaviour, EventType, Value);
+		}
+	}
+	else
+	{
+		UE_LOGFMT(Toolkit, Error,
+				  "Interaction: Trying to replicate HoverBehaviour {HoverBehaviour} in InteractionComponent "
+				  "{InteractionComponent} which has no local net owner!",
+				  Behaviour->GetName(), this->GetName());
+	}
+}
+
+void UUBaseInteractionComponent::ActionBehaviourReplicationStart(UActionBehaviour* Behaviour,
+																 const EInteractionEventType EventType,
+																 const FInputActionValue& Value)
+{
+	if (!Behaviour->bExecuteOnServer) // this should never happen
+	{
+		UE_LOGFMT(Toolkit, Error,
+				  "Interaction: Trying to execute ActionBehaviour {ActionBehaviour} in InteractionComponent "
+				  "{InteractionComponent} on the server, but bExecuteOnServer is false!",
+				  Behaviour->GetName(), this->GetName());
+		return;
+	}
+
+	// If desired, multicast to all clients (including originator)
+	if (Behaviour->bExecuteOnAllClients)
+	{
+		MulticastActionBehaviourReplicationStartRpc(Behaviour, EventType, Value);
+	}
+	else
+	{
+		// Execute the behaviour only on the server
+		Behaviour->OnActionEventEvent.Broadcast(this, EventType, Value);
+	}
+}
+
+void UUBaseInteractionComponent::ServerRequestActionBehaviourReplicationStartRpc_Implementation(
+	UActionBehaviour* Behaviour, const EInteractionEventType EventType, const FInputActionValue& Value)
+{
+	ActionBehaviourReplicationStart(Behaviour, EventType, Value);
+}
+
+void UUBaseInteractionComponent::MulticastActionBehaviourReplicationStartRpc_Implementation(
+	UActionBehaviour* Behaviour, const EInteractionEventType EventType, const FInputActionValue& Value)
+{
+	Behaviour->OnActionEventEvent.Broadcast(this, EventType, Value);
+}
diff --git a/Source/RWTHVRToolkit/Private/Pawn/Navigation/CollisionHandlingMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/Navigation/CollisionHandlingMovement.cpp
index d75c4d401caf16d8485458db2ed4f4e78c4689d3..62bfa4602491506185646dfd769a3876ca4b8ccb 100644
--- a/Source/RWTHVRToolkit/Private/Pawn/Navigation/CollisionHandlingMovement.cpp
+++ b/Source/RWTHVRToolkit/Private/Pawn/Navigation/CollisionHandlingMovement.cpp
@@ -1,6 +1,8 @@
 #include "Pawn/Navigation/CollisionHandlingMovement.h"
 
 #include "Kismet/KismetSystemLibrary.h"
+#include "Logging/StructuredLog.h"
+#include "Utility/RWTHVRUtilities.h"
 
 UCollisionHandlingMovement::UCollisionHandlingMovement(const FObjectInitializer& ObjectInitializer) :
 	Super(ObjectInitializer)
@@ -33,61 +35,76 @@ void UCollisionHandlingMovement::BeginPlay()
 void UCollisionHandlingMovement::TickComponent(float DeltaTime, enum ELevelTick TickType,
 											   FActorComponentTickFunction* ThisTickFunction)
 {
-	SetCapsuleColliderToUserSize();
 
-	FVector InputVector = GetPendingInputVector();
-
-	if (NavigationMode == EVRNavigationModes::NAV_WALK)
+	if (ShouldSkipUpdate(DeltaTime))
 	{
-		// you are only allowed to move horizontally in NAV_WALK
-		// everything else will be handled by stepping-up/gravity
-		// so rotate the input vector onto horizontal plane
-		const FRotator InputRot = FRotator(InputVector.Rotation());
-		const FRotator InputYaw = FRotator(0, InputRot.Yaw, 0);
-		InputVector = InputRot.UnrotateVector(InputVector);
-		InputVector = InputYaw.RotateVector(InputVector);
-		ConsumeInputVector();
-		AddInputVector(InputVector);
+		return;
 	}
 
-
-	if (NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK)
+	const AController* Controller = PawnOwner->GetController();
+	if (Controller && Controller->IsLocalController())
 	{
-		// if me managed to get into a collision revert the movement since last Tick
-		CheckAndRevertCollisionSinceLastTick();
-		// check whether we are still in collision e.g. if an object has moved and got us into collision
-		MoveOutOfNewDynamicCollisions();
+		SetCapsuleColliderToUserSize();
 
-		if (InputVector.Size() > 0.001)
+		FVector InputVector = GetPendingInputVector();
+
+		if (NavigationMode == EVRNavigationModes::NAV_WALK)
 		{
-			const FVector SafeSteeringInput = GetCollisionSafeVirtualSteeringVec(InputVector, DeltaTime);
-			if (SafeSteeringInput != InputVector)
+			// you are only allowed to move horizontally in NAV_WALK
+			// everything else will be handled by stepping-up/gravity
+			// so rotate the input vector onto horizontal plane
+			const FRotator InputRot = FRotator(InputVector.Rotation());
+			const FRotator InputYaw = FRotator(0, InputRot.Yaw, 0);
+			InputVector = InputRot.UnrotateVector(InputVector);
+			InputVector = InputYaw.RotateVector(InputVector);
+			ConsumeInputVector();
+			AddInputVector(InputVector);
+		}
+
+		if (NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK)
+		{
+			// if me managed to get into a collision revert the movement since last Tick
+			CheckAndRevertCollisionSinceLastTick();
+			// check whether we are still in collision e.g. if an object has moved and got us into collision
+			MoveOutOfNewDynamicCollisions();
+
+			if (InputVector.Size() > 0.001)
+			{
+				const FVector SafeSteeringInput = GetCollisionSafeVirtualSteeringVec(InputVector, DeltaTime);
+				if (SafeSteeringInput != InputVector)
+				{
+					// if we would move into something if we apply this input (estimating distance by max speed)
+					// we only apply its perpendicular part (unless it is facing away from the collision)
+					ConsumeInputVector();
+					AddInputVector(SafeSteeringInput);
+				}
+			}
+
+			// in case we are in a collision and collision checks are temporarily deactivated, we only allow physical
+			// movement without any checks, otherwise check collision during physical movement
+			if (bCollisionChecksTemporarilyDeactivated)
 			{
-				// if we would move into something if we apply this input (estimating distance by max speed)
-				// we only apply its perpendicular part (unless it is facing away from the collision)
 				ConsumeInputVector();
-				AddInputVector(SafeSteeringInput);
+			}
+			else
+			{
+				// so we add stepping-up (for both walk and fly)
+				// and gravity for walking only
+				MoveByGravityOrStepUp(DeltaTime);
+
+				// if we physically (in the tracking space) walked into something, move the world away (by moving the
+				// pawn)
+				CheckForPhysWalkingCollision();
 			}
 		}
 
-		// in case we are in a collision and collision checks are temporarily deactivated.
-		if (!bCollisionChecksTemporarilyDeactivated)
+		if (NavigationMode == EVRNavigationModes::NAV_NONE)
 		{
-			// so we add stepping-up (for both walk and fly)
-			// and gravity for walking only
-			MoveByGravityOrStepUp(DeltaTime);
-
-			// if we physically (in the tracking space) walked into something, move the world away (by moving the pawn)
-			CheckForPhysWalkingCollision();
+			// just remove whatever input is there
+			ConsumeInputVector();
 		}
 	}
 
-	if (NavigationMode == EVRNavigationModes::NAV_NONE)
-	{
-		// just remove whatever input is there
-		ConsumeInputVector();
-	}
-
 	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
 }
 
@@ -100,6 +117,34 @@ void UCollisionHandlingMovement::SetHeadComponent(USceneComponent* NewHeadCompon
 	CapsuleColliderComponent->SetWorldLocation(FVector(0.0f, 0.0f, -HalfHeight));
 }
 
+bool UCollisionHandlingMovement::AddActorToIgnore(AActor* ActorToIgnore)
+{
+	if (ActorToIgnore && IsValid(ActorToIgnore))
+	{
+		ActorsToIgnore.AddUnique(ActorToIgnore);
+		return true;
+	}
+	else
+	{
+		UE_LOGFMT(Toolkit, Warning, "UCollisionHandlingMovement::AddActorToIgnore: Cannot add invalid actor");
+		return false;
+	}
+}
+
+bool UCollisionHandlingMovement::RemoveActorFromIgnore(AActor* ActorToIgnore)
+{
+	if (ActorToIgnore && IsValid(ActorToIgnore))
+	{
+		const int32 NumRemoved = ActorsToIgnore.Remove(ActorToIgnore);
+		return NumRemoved > 0;
+	}
+	else
+	{
+		UE_LOGFMT(Toolkit, Warning, "UCollisionHandlingMovement::RemoveActorFromIgnore: Cannot remove invalid actor");
+		return false;
+	}
+}
+
 void UCollisionHandlingMovement::SetCapsuleColliderToUserSize() const
 {
 	// the collider should be placed
diff --git a/Source/RWTHVRToolkit/Public/Interaction/Interactables/ActionBehaviour.h b/Source/RWTHVRToolkit/Public/Interaction/Interactables/ActionBehaviour.h
index 755d876dc3205b0538f6bc394352def55b2da670..bf3afea3a1246994a0bcbfb1d96317083e2770e3 100644
--- a/Source/RWTHVRToolkit/Public/Interaction/Interactables/ActionBehaviour.h
+++ b/Source/RWTHVRToolkit/Public/Interaction/Interactables/ActionBehaviour.h
@@ -3,21 +3,18 @@
 #pragma once
 
 #include "CoreMinimal.h"
-#include "InputAction.h"
+#include "BaseBehaviour.h"
 #include "Components/SceneComponent.h"
 #include "InputActionValue.h"
-#include "InteractionBitSet.h"
 #include "ActionBehaviour.generated.h"
 
+enum EInteractionEventType : uint8;
 
-DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnActionBegin, USceneComponent*, TriggeredComponent,
-											   const UInputAction*, InputAction, const FInputActionValue&, Value);
-
-DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnActionEnd, USceneComponent*, TriggeredComponent, const UInputAction*,
-											   InputAction, const FInputActionValue&, Value);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnActionBegin, USceneComponent*, TriggerComponent,
+											   const EInteractionEventType, EventType, const FInputActionValue&, Value);
 
 UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
-class RWTHVRTOOLKIT_API UActionBehaviour : public USceneComponent
+class RWTHVRTOOLKIT_API UActionBehaviour : public UBaseBehaviour
 {
 	GENERATED_BODY()
 
@@ -26,22 +23,23 @@ public:
 	UActionBehaviour();
 
 	UPROPERTY(BlueprintAssignable)
-	FOnActionBegin OnActionBeginEvent;
-
+	FOnActionBegin OnActionEventEvent;
+
+	/**
+	 * Replication specific:
+	 * This is only executed on the local client which processed the interaction and requested the replication process
+	 * to be started. Can be used e.g. for local effects or things that should be done both on the server and local
+	 * client. Broadcast by UInteractableComponent when the originating client sends a server rpc to start the
+	 * interaction replication.
+	 */
 	UPROPERTY(BlueprintAssignable)
-	FOnActionEnd OnActionEndEvent;
+	FOnActionBegin OnActionReplicationStartedOriginatorEvent;
+
 
 protected:
 	UFUNCTION()
-	virtual void OnActionStart(USceneComponent* TriggeredComponent, const UInputAction* InputAction,
+	virtual void OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
 							   const FInputActionValue& Value);
 
-	UFUNCTION()
-	virtual void OnActionEnd(USceneComponent* TriggeredComponent, const UInputAction* InputAction,
-							 const FInputActionValue& Value);
-
 	virtual void BeginPlay() override;
-
-	virtual void TickComponent(float DeltaTime, ELevelTick TickType,
-							   FActorComponentTickFunction* ThisTickFunction) override;
 };
diff --git a/Source/RWTHVRToolkit/Public/Interaction/Interactables/BaseBehaviour.h b/Source/RWTHVRToolkit/Public/Interaction/Interactables/BaseBehaviour.h
new file mode 100644
index 0000000000000000000000000000000000000000..d1f379bb9431749786d2ac871d119e82f5bea46e
--- /dev/null
+++ b/Source/RWTHVRToolkit/Public/Interaction/Interactables/BaseBehaviour.h
@@ -0,0 +1,34 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Components/SceneComponent.h"
+#include "BaseBehaviour.generated.h"
+
+
+UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
+class RWTHVRTOOLKIT_API UBaseBehaviour : public USceneComponent
+{
+	GENERATED_BODY()
+
+public:
+	/**
+	 * Replication part
+	 */
+
+	/**
+	 * Defines whether this behaviour will be executed on the server instead of the local client.
+	 * If set to true, an RPC is sent to the server and it will not be run locally.
+	 * */
+	UPROPERTY(EditAnywhere, BlueprintReadWrite)
+	bool bExecuteOnServer = false;
+
+	/**
+	 * Defines whether this behaviour will be executed on all connected clients that are relevant, including the
+	 * local originator client. This only has an effect if bExecuteOnServer is true, as only the server can multicast
+	 * to all other clients.
+	 */
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "bExecuteOnServer"))
+	bool bExecuteOnAllClients = false;
+};
diff --git a/Source/RWTHVRToolkit/Public/Interaction/Interactables/GrabBehavior.h b/Source/RWTHVRToolkit/Public/Interaction/Interactables/GrabBehavior.h
index d10affdff4aa25e2e4c22818219375580f342c8f..65118646b4ca6edae19772cb3d5bf1df875534ee 100644
--- a/Source/RWTHVRToolkit/Public/Interaction/Interactables/GrabBehavior.h
+++ b/Source/RWTHVRToolkit/Public/Interaction/Interactables/GrabBehavior.h
@@ -20,14 +20,13 @@ class RWTHVRTOOLKIT_API UGrabBehavior : public UActionBehaviour
 	GENERATED_BODY()
 
 public:
+	UGrabBehavior();
+
 	UPROPERTY(EditAnywhere, Category = "Grabbing")
 	bool bBlockOtherInteractionsWhileGrabbed = true;
 
-	virtual void OnActionStart(USceneComponent* TriggeredComponent, const UInputAction* InputAction,
-							   const FInputActionValue& Value) override;
-	virtual void OnActionEnd(USceneComponent* TriggeredComponent, const UInputAction* InputAction,
-							 const FInputActionValue& Value) override;
-
+	UPROPERTY(EditAnywhere, Category = "Grabbing")
+	bool bIgnoreGrabbedActorInCollisionMovement = true;
 
 	/**
 	 * Called after the object was successfully attached to the hand
@@ -41,13 +40,25 @@ public:
 	UPROPERTY(BlueprintAssignable)
 	FOnGrabEnd OnGrabEndEvent;
 
+	UPROPERTY()
+	UPrimitiveComponent* MyPhysicsComponent;
+
+	virtual void BeginPlay() override;
+
 	UPrimitiveComponent* GetFirstComponentSimulatingPhysics(const AActor* TargetActor);
 
 	// recursively goes up the hierarchy and returns the highest parent simulating physics
 	UPrimitiveComponent* GetHighestParentSimulatingPhysics(UPrimitiveComponent* Comp);
 
-	UPROPERTY()
-	UPrimitiveComponent* MyPhysicsComponent;
+	UFUNCTION()
+	void ReplicationOriginaterClientCallback(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
+											 const FInputActionValue& Value);
+
+	void HandleCollisionHandlingMovement(const USceneComponent* CurrentAttachParent,
+										 const EInteractionEventType EventType);
+
+	virtual void OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
+							   const FInputActionValue& Value) override;
 
 	UFUNCTION(BlueprintPure)
 	bool IsObjectGrabbed() const { return bObjectGrabbed; }
@@ -61,7 +72,13 @@ private:
 	 */
 	bool TryRelease();
 
+	void StartGrab(USceneComponent* TriggerComponent);
+
+	void EndGrab(USceneComponent* TriggerComponent);
+
 	bool bObjectGrabbed = false;
 
 	bool bWasSimulatingPhysics;
+
+	bool bWasAddedToIgnore = false;
 };
diff --git a/Source/RWTHVRToolkit/Public/Interaction/Interactables/HoverBehaviour.h b/Source/RWTHVRToolkit/Public/Interaction/Interactables/HoverBehaviour.h
index 7557653fbf02dc8d1780bbb32804c583324281ad..efdd6eafd797bf60bc07847565bf216537309358 100644
--- a/Source/RWTHVRToolkit/Public/Interaction/Interactables/HoverBehaviour.h
+++ b/Source/RWTHVRToolkit/Public/Interaction/Interactables/HoverBehaviour.h
@@ -3,16 +3,16 @@
 #pragma once
 
 #include "CoreMinimal.h"
+#include "BaseBehaviour.h"
+#include "InteractionEventType.h"
 #include "Components/SceneComponent.h"
 #include "HoverBehaviour.generated.h"
 
-
-DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnHoverStart, const USceneComponent*, TriggeredComponent, FHitResult,
-											 Hit);
-DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHoverEnd, const USceneComponent*, TriggeredComponent);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnHoverEvent, const USceneComponent*, TriggeredComponent,
+											   const EInteractionEventType, EventType, const FHitResult&, Hit);
 
 UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
-class RWTHVRTOOLKIT_API UHoverBehaviour : public USceneComponent
+class RWTHVRTOOLKIT_API UHoverBehaviour : public UBaseBehaviour
 {
 	GENERATED_BODY()
 
@@ -22,16 +22,22 @@ public:
 	 * VRPawn) Hit: Hit Result of the trace to get access to e.g. contact point/normals etc.
 	 */
 	UPROPERTY(BlueprintAssignable)
-	FOnHoverStart OnHoverStartEvent;
+	FOnHoverEvent OnHoverEventEvent;
 
+	/**
+	 * Replication specific:
+	 * This is only executed on the local client which processed the interaction and requested the replication process
+	 * to be started. Can be used e.g. for local effects or things that should be done both on the server and local
+	 * client. Broadcast by UInteractableComponent when the originating client sends a server rpc to start the
+	 * interaction replication.
+	 */
 	UPROPERTY(BlueprintAssignable)
-	FOnHoverEnd OnHoverEndEvent;
+	FOnHoverEvent OnHoverReplicationStartedOriginatorEvent;
 
 protected:
 	UFUNCTION()
-	virtual void OnHoverStart(const USceneComponent* TriggeredComponent, FHitResult Hit);
-	UFUNCTION()
-	virtual void OnHoverEnd(const USceneComponent* TriggeredComponent);
+	virtual void OnHoverEvent(const USceneComponent* TriggerComponent, EInteractionEventType EventType,
+							  const FHitResult& Hit);
 
 	virtual void BeginPlay() override;
 };
diff --git a/Source/RWTHVRToolkit/Public/Interaction/Interactables/InteractableComponent.h b/Source/RWTHVRToolkit/Public/Interaction/Interactables/InteractableComponent.h
index da85ad8b003814098fc21e112de423a097a80535..7ef7078b931eebdc43d94932ebe6d615a8a7a442 100644
--- a/Source/RWTHVRToolkit/Public/Interaction/Interactables/InteractableComponent.h
+++ b/Source/RWTHVRToolkit/Public/Interaction/Interactables/InteractableComponent.h
@@ -9,6 +9,8 @@
 #include "InteractableComponent.generated.h"
 
 
+enum EInteractionEventType : uint8;
+class UBaseBehaviour;
 struct FInputActionValue;
 class UActionBehaviour;
 class UHoverBehaviour;
@@ -53,7 +55,7 @@ public:
 	bool bAllowInteractionFromChildGeometry = true;
 
 	UFUNCTION(BlueprintCallable)
-	FORCEINLINE bool HasInteractionTypeFlag(EInteractorType type) { return type & InteractorFilter; }
+	FORCEINLINE bool HasInteractionTypeFlag(EInteractorType Type) { return Type & InteractorFilter; }
 
 	/**
 	 * @brief Restrict interactability to given components (e.g. if an object is grabbed, block interactions from other
@@ -74,12 +76,10 @@ protected:
 	virtual void BeginPlay() override;
 
 public:
-	void HandleOnHoverStartEvents(USceneComponent* TriggerComponent, const EInteractorType Interactor);
-	void HandleOnHoverEndEvents(USceneComponent* TriggerComponent, const EInteractorType Interactor);
-	void HandleOnActionStartEvents(USceneComponent* TriggerComponent, const UInputAction* InputAction,
-								   const FInputActionValue& Value, const EInteractorType Interactor);
-	void HandleOnActionEndEvents(USceneComponent* TriggerComponent, const UInputAction* InputAction,
-								 const FInputActionValue& Value, const EInteractorType Interactor);
+	void HandleOnHoverEvents(USceneComponent* TriggerComponent, const EInteractorType Interactor,
+							 const EInteractionEventType EventType);
+	void HandleOnActionEvents(USceneComponent* TriggerComponent, const EInteractorType Interactor,
+							  const EInteractionEventType EventType, const FInputActionValue& Value);
 
 	/**
 	 * @brief If hover and action behaviors are not explicitly specified, load all existing ones
diff --git a/Source/RWTHVRToolkit/Public/Interaction/Interactables/InteractionEventType.h b/Source/RWTHVRToolkit/Public/Interaction/Interactables/InteractionEventType.h
new file mode 100644
index 0000000000000000000000000000000000000000..9e24c211276613c6ea0e7b11037d59e05caefba0
--- /dev/null
+++ b/Source/RWTHVRToolkit/Public/Interaction/Interactables/InteractionEventType.h
@@ -0,0 +1,8 @@
+#pragma once
+
+UENUM(BlueprintType)
+enum EInteractionEventType : uint8
+{
+	InteractionStart = 0,
+	InteractionEnd = 1,
+};
diff --git a/Source/RWTHVRToolkit/Public/Interaction/Interactors/DirectInteractionComponent.h b/Source/RWTHVRToolkit/Public/Interaction/Interactors/DirectInteractionComponent.h
index 69ef74ff844a727c11a333047a7f011a12d01241..46d2d160358d991a70e232bce73bc6c9a57c52ef 100644
--- a/Source/RWTHVRToolkit/Public/Interaction/Interactors/DirectInteractionComponent.h
+++ b/Source/RWTHVRToolkit/Public/Interaction/Interactors/DirectInteractionComponent.h
@@ -3,13 +3,13 @@
 #pragma once
 
 #include "CoreMinimal.h"
+#include "UBaseInteractionComponent.h"
 #include "Components/SceneComponent.h"
 #include "Interaction/Interactables/InteractableComponent.h"
-#include "Pawn/InputExtensionInterface.h"
 #include "DirectInteractionComponent.generated.h"
 
 UCLASS(Abstract, Blueprintable)
-class RWTHVRTOOLKIT_API UDirectInteractionComponent : public USceneComponent, public IInputExtensionInterface
+class RWTHVRTOOLKIT_API UDirectInteractionComponent : public UUBaseInteractionComponent
 {
 	GENERATED_BODY()
 
@@ -20,29 +20,18 @@ public:
 	virtual void TickComponent(float DeltaTime, ELevelTick TickType,
 							   FActorComponentTickFunction* ThisTickFunction) override;
 
-	UPROPERTY(EditAnywhere, Category = "Input")
-	class UInputAction* InteractionInputAction;
-
 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Direct Interaction")
 	float InteractionSphereRadius = 15.0;
 
-	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Direct Interaction")
-	bool bShowDebugTrace = false;
-
 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Direct Interaction")
 	bool bOnlyInteractWithClosestActor = false;
 
-	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Direct Interaction")
-	TArray<AActor*> ActorsToIgnore;
-
 	virtual void SetupPlayerInput(UInputComponent* PlayerInputComponent) override;
 
 private:
-	UFUNCTION()
-	void OnBeginInteraction(const FInputActionValue& Value);
+	virtual void OnBeginInteractionInputAction(const FInputActionValue& Value) override;
 
-	UFUNCTION()
-	void OnEndInteraction(const FInputActionValue& Value);
+	virtual void OnEndInteractionInputAction(const FInputActionValue& Value) override;
 
 	UPROPERTY()
 	TArray<UInteractableComponent*> PreviousInteractableComponentsInRange;
diff --git a/Source/RWTHVRToolkit/Public/Interaction/Interactors/RaycastInteractionComponent.h b/Source/RWTHVRToolkit/Public/Interaction/Interactors/RaycastInteractionComponent.h
index f5c93123e8ba136e747796825f93309b285109b0..31441d516c5a34923c2ca6d26665a50a9f6069ed 100644
--- a/Source/RWTHVRToolkit/Public/Interaction/Interactors/RaycastInteractionComponent.h
+++ b/Source/RWTHVRToolkit/Public/Interaction/Interactors/RaycastInteractionComponent.h
@@ -2,16 +2,15 @@
 
 #pragma once
 
-#include <Pawn/InputExtensionInterface.h>
-
 #include "CoreMinimal.h"
+#include "UBaseInteractionComponent.h"
 #include "Components/SceneComponent.h"
 #include "Interaction/Interactables/InteractableComponent.h"
 #include "RaycastInteractionComponent.generated.h"
 
 
 UCLASS(Abstract, Blueprintable)
-class RWTHVRTOOLKIT_API URaycastInteractionComponent : public USceneComponent, public IInputExtensionInterface
+class RWTHVRTOOLKIT_API URaycastInteractionComponent : public UUBaseInteractionComponent
 {
 	GENERATED_BODY()
 
@@ -23,20 +22,13 @@ public:
 	virtual void TickComponent(float DeltaTime, ELevelTick TickType,
 							   FActorComponentTickFunction* ThisTickFunction) override;
 
-	UPROPERTY(EditAnywhere, Category = "Input")
-	class UInputAction* InteractionInputAction;
-
 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Raycast")
 	float TraceLength = 3000.0;
-	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Raycast")
-	bool bShowDebugTrace = false;
 
 private:
-	UFUNCTION()
-	void OnBeginInteraction(const FInputActionValue& Value);
+	virtual void OnBeginInteractionInputAction(const FInputActionValue& Value) override;
 
-	UFUNCTION()
-	void OnEndInteraction(const FInputActionValue& Value);
+	virtual void OnEndInteractionInputAction(const FInputActionValue& Value) override;
 
 public:
 	virtual void SetupPlayerInput(UInputComponent* PlayerInputComponent) override;
diff --git a/Source/RWTHVRToolkit/Public/Interaction/Interactors/UBaseInteractionComponent.h b/Source/RWTHVRToolkit/Public/Interaction/Interactors/UBaseInteractionComponent.h
new file mode 100644
index 0000000000000000000000000000000000000000..ca97fd7e36f1359273734f7b88da77777fdfa7b0
--- /dev/null
+++ b/Source/RWTHVRToolkit/Public/Interaction/Interactors/UBaseInteractionComponent.h
@@ -0,0 +1,103 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Components/SceneComponent.h"
+#include "Pawn/InputExtensionInterface.h"
+#include "UBaseInteractionComponent.generated.h"
+
+
+struct FInputActionValue;
+enum EInteractionEventType : uint8;
+class UActionBehaviour;
+class UHoverBehaviour;
+
+UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
+class RWTHVRTOOLKIT_API UUBaseInteractionComponent : public USceneComponent, public IInputExtensionInterface
+{
+	GENERATED_BODY()
+
+public:
+	// Sets default values for this component's properties
+	UUBaseInteractionComponent();
+
+	virtual void SetupPlayerInput(UInputComponent* PlayerInputComponent) override;
+
+	UPROPERTY(EditAnywhere, Category = "Input")
+	class UInputAction* InteractionInputAction;
+
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Base Interaction")
+	TArray<AActor*> ActorsToIgnore;
+
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Base Interaction")
+	bool bShowDebugTrace = false;
+
+	/*
+	 * Replication part
+	 */
+
+
+	/**
+	 * Requests the start of the replication process for the given HoverBehaviour, EventType and Hit.
+	 * Depending on authority, this executes the replication of the behaviour directly or requests it via a server RPC.
+	 */
+	void RequestHoverBehaviourReplicationStart(UHoverBehaviour* Behaviour, const EInteractionEventType EventType,
+											   const FHitResult& Hit);
+
+	/**
+	 * This is executed on the server/authority. The behaviour is actually executed directly on the server, or
+	 * multicast to all connected clients. The multicast then executes the behaviour.
+	 */
+	void HoverBehaviourReplicationStart(UHoverBehaviour* Behaviour, const EInteractionEventType EventType,
+										const FHitResult& Hit);
+
+	/**
+	 * This is only executed on the local client which processed the interaction and requested the replication process
+	 * to be started. Can be used e.g. for local effects or things that should be done both on the server and local
+	 * client.
+	 */
+	void HoverBehaviourReplicationOriginatorCallback(UHoverBehaviour* Behaviour, const EInteractionEventType EventType,
+													 const FHitResult& Hit);
+
+	/**
+	 * Requests the start of the replication process for the given ActionBehaviour, EventType and the Value of the Input
+	 * Action. Depending on authority, this executes the replication of the behaviour directly or requests it via a
+	 * server RPC.
+	 */
+	void RequestActionBehaviourReplicationStart(UActionBehaviour* Behaviour, const EInteractionEventType EventType,
+												const FInputActionValue& Value);
+
+	/**
+	 * This is executed on the server/authority. The behaviour is actually executed directly on the server, or
+	 * multicast to all connected clients. The multicast then executes the behaviour.
+	 */
+	void ActionBehaviourReplicationStart(UActionBehaviour* Behaviour, const EInteractionEventType EventType,
+										 const FInputActionValue& Value);
+
+
+	// RPCs
+	UFUNCTION(Server, Reliable)
+	void ServerRequestHoverBehaviourReplicationStartRpc(UHoverBehaviour* Behaviour,
+														const EInteractionEventType EventType, const FHitResult& Hit);
+
+	UFUNCTION(NetMulticast, Reliable)
+	void MulticastHoverBehaviourReplicationStartRpc(UHoverBehaviour* Behaviour, const EInteractionEventType EventType,
+													const FHitResult& Hit);
+
+	UFUNCTION(Server, Reliable)
+	void ServerRequestActionBehaviourReplicationStartRpc(UActionBehaviour* Behaviour,
+														 const EInteractionEventType EventType,
+														 const FInputActionValue& Value);
+
+	UFUNCTION(NetMulticast, Reliable)
+	void MulticastActionBehaviourReplicationStartRpc(UActionBehaviour* Behaviour, const EInteractionEventType EventType,
+													 const FInputActionValue& Value);
+
+private:
+	UFUNCTION()
+	virtual void OnBeginInteractionInputAction(const FInputActionValue& Value);
+
+	UFUNCTION()
+	virtual void OnEndInteractionInputAction(const FInputActionValue& Value);
+};
diff --git a/Source/RWTHVRToolkit/Public/Pawn/Navigation/CollisionHandlingMovement.h b/Source/RWTHVRToolkit/Public/Pawn/Navigation/CollisionHandlingMovement.h
index 6166c84a1c6f2aee1adfdd2d7b5080ac027d23cd..b9cfad134d1b57e12d6be08a82b56935f3d4674b 100644
--- a/Source/RWTHVRToolkit/Public/Pawn/Navigation/CollisionHandlingMovement.h
+++ b/Source/RWTHVRToolkit/Public/Pawn/Navigation/CollisionHandlingMovement.h
@@ -42,6 +42,12 @@ public:
 
 	void SetHeadComponent(USceneComponent* NewHeadComponent);
 
+	UFUNCTION(BlueprintCallable)
+	bool AddActorToIgnore(AActor* ActorToIgnore);
+
+	UFUNCTION(BlueprintCallable)
+	bool RemoveActorFromIgnore(AActor* ActorToIgnore);
+
 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement")
 	EVRNavigationModes NavigationMode = EVRNavigationModes::NAV_WALK;