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

docs(replication): adds more comments to replication of transforms and pawn rpcs

parent 283b158b
Branches
Tags
No related merge requests found
...@@ -19,24 +19,34 @@ UClientTransformReplication::UClientTransformReplication() ...@@ -19,24 +19,34 @@ UClientTransformReplication::UClientTransformReplication()
// Naive direct transform replication (replace with input rep?) // Naive direct transform replication (replace with input rep?)
void UClientTransformReplication::UpdateState(float DeltaTime) void UClientTransformReplication::UpdateState(float DeltaTime)
{ {
// We only perform the state update on the local owner of the actor. This is our (client-)authority
const auto* OwningActor = GetOwner(); const auto* OwningActor = GetOwner();
if (OwningActor && OwningActor->HasLocalNetOwner()) if (OwningActor && OwningActor->HasLocalNetOwner())
{ {
// Only do this if we actually replicate the actor
if (GetIsReplicated()) if (GetIsReplicated())
{ {
const FVector Loc = OwningActor->GetActorLocation(); const FVector Loc = OwningActor->GetActorLocation();
const FRotator Rot = OwningActor->GetActorRotation(); const FRotator Rot = OwningActor->GetActorRotation();
// Only update state if the local state changed
if (!Loc.Equals(ReplicatedTransform.Position) || !Rot.Equals(ReplicatedTransform.Rotation)) if (!Loc.Equals(ReplicatedTransform.Position) || !Rot.Equals(ReplicatedTransform.Rotation))
{ {
// Factor in NetUpdateRate
ControllerNetUpdateCount += DeltaTime; ControllerNetUpdateCount += DeltaTime;
if (ControllerNetUpdateCount >= (1.0f / ControllerNetUpdateRate)) // todo save inverse? if (ControllerNetUpdateCount >= (1.0f / ControllerNetUpdateRate))
{ {
ControllerNetUpdateCount = 0.0f; ControllerNetUpdateCount = 0.0f;
// The local state has changed and we're within the update rate. Apply the new local state to the
// replicated variable - this just saves the new local state on the local net owner.
ReplicatedTransform.Position = Loc; ReplicatedTransform.Position = Loc;
ReplicatedTransform.Rotation = Rot; ReplicatedTransform.Rotation = Rot;
if (GetNetMode() == NM_Client) // why do we differentiate here between netmode and authority?
// If we are running as a client, push the state to the server by calling the respective RPC.
// This is required in case we are both the server and the local net owner, in which case simply
// updating the local state is enough.
if (GetNetMode() == NM_Client)
{ {
ServerSendControllerTransformRpc(ReplicatedTransform); ServerSendControllerTransformRpc(ReplicatedTransform);
} }
...@@ -50,23 +60,32 @@ void UClientTransformReplication::TickComponent(float DeltaTime, ELevelTick Tick ...@@ -50,23 +60,32 @@ void UClientTransformReplication::TickComponent(float DeltaTime, ELevelTick Tick
FActorComponentTickFunction* ThisTickFunction) FActorComponentTickFunction* ThisTickFunction)
{ {
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// Perform a state update (if required) each tick. A state update means that we check whether
// our transform changed. If it did, send the new one to the server if we're within our NetUpdateRate.
UpdateState(DeltaTime); UpdateState(DeltaTime);
} }
// this function just defines how our properties are replicated
void UClientTransformReplication::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const void UClientTransformReplication::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
{ {
Super::GetLifetimeReplicatedProps(OutLifetimeProps); Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Skipping the owner with this as the owner will use the controllers location directly // Skipping the owner with this as the owner will use the local state directly
DOREPLIFETIME_CONDITION(UClientTransformReplication, ReplicatedTransform, COND_SkipOwner); DOREPLIFETIME_CONDITION(UClientTransformReplication, ReplicatedTransform, COND_SkipOwner);
DOREPLIFETIME(UClientTransformReplication, ControllerNetUpdateRate); DOREPLIFETIME(UClientTransformReplication, ControllerNetUpdateRate);
} }
// Apply the state update on the server
void UClientTransformReplication::ServerSendControllerTransformRpc_Implementation(FVRTransformRep NewTransform) void UClientTransformReplication::ServerSendControllerTransformRpc_Implementation(FVRTransformRep NewTransform)
{ {
// Store new transform and trigger OnRep_Function // Store new transform and trigger OnRep_Function
ReplicatedTransform = NewTransform; ReplicatedTransform = NewTransform;
// We are on the server here. If we are additionally the local net owner, i.e. the source of the stateupdate,
// it means we already directly apply our transform, as we are either a listen server or running in standalone.
// Therefore only apply the On_Rep function if we are not the local net owner.
// The OnRep function is doing the actual state update.
if (!GetOwner()->HasLocalNetOwner()) if (!GetOwner()->HasLocalNetOwner())
OnRep_ReplicatedTransform(); OnRep_ReplicatedTransform();
} }
......
...@@ -16,8 +16,7 @@ UReplicatedMotionControllerComponent::UReplicatedMotionControllerComponent() ...@@ -16,8 +16,7 @@ UReplicatedMotionControllerComponent::UReplicatedMotionControllerComponent()
ControllerNetUpdateCount = 0.0f; ControllerNetUpdateCount = 0.0f;
} }
// Naive direct transform replication (replace with input rep?) // See UClientTransformReplication::UpdateState
void UReplicatedMotionControllerComponent::UpdateState(float DeltaTime) void UReplicatedMotionControllerComponent::UpdateState(float DeltaTime)
{ {
if (GetOwner()->HasLocalNetOwner()) if (GetOwner()->HasLocalNetOwner())
...@@ -36,7 +35,7 @@ void UReplicatedMotionControllerComponent::UpdateState(float DeltaTime) ...@@ -36,7 +35,7 @@ void UReplicatedMotionControllerComponent::UpdateState(float DeltaTime)
ReplicatedTransform.Position = Loc; ReplicatedTransform.Position = Loc;
ReplicatedTransform.Rotation = Rot; ReplicatedTransform.Rotation = Rot;
if (GetNetMode() == NM_Client) // why do we differentiate here between netmode and authority? if (GetNetMode() == NM_Client)
{ {
ServerSendControllerTransformRpc(ReplicatedTransform); ServerSendControllerTransformRpc(ReplicatedTransform);
} }
...@@ -58,6 +57,7 @@ void UReplicatedMotionControllerComponent::GetLifetimeReplicatedProps( ...@@ -58,6 +57,7 @@ void UReplicatedMotionControllerComponent::GetLifetimeReplicatedProps(
{ {
Super::GetLifetimeReplicatedProps(OutLifetimeProps); Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Disable the basic built in replication of relative transform data, as we are using our own transform sync/state update
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeLocation); DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeLocation);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeRotation); DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeRotation);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeScale3D); DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeScale3D);
...@@ -67,6 +67,7 @@ void UReplicatedMotionControllerComponent::GetLifetimeReplicatedProps( ...@@ -67,6 +67,7 @@ void UReplicatedMotionControllerComponent::GetLifetimeReplicatedProps(
DOREPLIFETIME(UReplicatedMotionControllerComponent, ControllerNetUpdateRate); DOREPLIFETIME(UReplicatedMotionControllerComponent, ControllerNetUpdateRate);
} }
// See UClientTransformReplication::ServerSendControllerTransformRpc_Implementation
void UReplicatedMotionControllerComponent::ServerSendControllerTransformRpc_Implementation( void UReplicatedMotionControllerComponent::ServerSendControllerTransformRpc_Implementation(
FVRTransformRep NewTransform) FVRTransformRep NewTransform)
{ {
......
...@@ -60,6 +60,8 @@ void AVirtualRealityPawn::Tick(float DeltaSeconds) ...@@ -60,6 +60,8 @@ void AVirtualRealityPawn::Tick(float DeltaSeconds)
* The alternative would be to do this only on the server on possess and check for player state/type, * The alternative would be to do this only on the server on possess and check for player state/type,
* as connections now send their playertype over. * as connections now send their playertype over.
*/ */
// This pawn's controller has changed! This is called on both server and owning client. If we are the owning client
// and the master, request that the DCRA is attached to us.
void AVirtualRealityPawn::NotifyControllerChanged() void AVirtualRealityPawn::NotifyControllerChanged()
{ {
Super::NotifyControllerChanged(); Super::NotifyControllerChanged();
...@@ -71,6 +73,8 @@ void AVirtualRealityPawn::NotifyControllerChanged() ...@@ -71,6 +73,8 @@ void AVirtualRealityPawn::NotifyControllerChanged()
if (UVirtualRealityUtilities::IsRoomMountedMode() && (UVirtualRealityUtilities::IsMaster() || GetNetMode()) == if (UVirtualRealityUtilities::IsRoomMountedMode() && (UVirtualRealityUtilities::IsMaster() || GetNetMode()) ==
NM_Standalone) NM_Standalone)
{ {
// If we are also the authority (standalone or listen server), directly attach it to us.
// If we are not (client), ask the server to do it.
if (HasAuthority()) if (HasAuthority())
AttachDCRAtoPawn(); AttachDCRAtoPawn();
else else
...@@ -205,6 +209,8 @@ void AVirtualRealityPawn::UpdateRightHandForDesktopInteraction() const ...@@ -205,6 +209,8 @@ void AVirtualRealityPawn::UpdateRightHandForDesktopInteraction() const
} }
// Todo rewrite this in some other way or attach it differently, this is horrible // Todo rewrite this in some other way or attach it differently, this is horrible
// Executed on the server only: Finds and attaches the CaveSetup Actor, which contains the DCRA to the Pawn.
// It is only executed on the server because attachments are synced to all clients, but not from client to server.
void AVirtualRealityPawn::AttachDCRAtoPawn() void AVirtualRealityPawn::AttachDCRAtoPawn()
{ {
if (!CaveSetupActorClass || !CaveSetupActorClass->IsValidLowLevelFast()) if (!CaveSetupActorClass || !CaveSetupActorClass->IsValidLowLevelFast())
...@@ -212,6 +218,8 @@ void AVirtualRealityPawn::AttachDCRAtoPawn() ...@@ -212,6 +218,8 @@ void AVirtualRealityPawn::AttachDCRAtoPawn()
UE_LOGFMT(Toolkit, Warning, "No CaveSetup Actor class set in pawn!"); UE_LOGFMT(Toolkit, Warning, "No CaveSetup Actor class set in pawn!");
return; return;
} }
// Find our CaveSetup actor
TArray<AActor*> FoundActors; TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), CaveSetupActorClass, FoundActors); UGameplayStatics::GetAllActorsOfClass(GetWorld(), CaveSetupActorClass, FoundActors);
...@@ -249,8 +257,10 @@ void AVirtualRealityPawn::SetupMotionControllerSources() ...@@ -249,8 +257,10 @@ void AVirtualRealityPawn::SetupMotionControllerSources()
RightHand->SetTrackingMotionSource(MotionControllerSourceRight); RightHand->SetTrackingMotionSource(MotionControllerSourceRight);
} }
// Requests the server to perform the attachment, as only the server can sync this to all the other clients.
void AVirtualRealityPawn::ServerAttachDCRAtoPawnRpc_Implementation() void AVirtualRealityPawn::ServerAttachDCRAtoPawnRpc_Implementation()
{ {
// We're on the server here - attach the actor to the pawn.
AttachDCRAtoPawn(); AttachDCRAtoPawn();
} }
......
...@@ -46,7 +46,7 @@ protected: ...@@ -46,7 +46,7 @@ protected:
UPROPERTY(EditDefaultsOnly, ReplicatedUsing = OnRep_ReplicatedTransform, Category = "Networking") UPROPERTY(EditDefaultsOnly, ReplicatedUsing = OnRep_ReplicatedTransform, Category = "Networking")
FVRTransformRep ReplicatedTransform; FVRTransformRep ReplicatedTransform;
// Called whenever ReplicatedTransform is replicated to clients. Not called on Server/Owning client // Called whenever ReplicatedTransform is replicated to clients. Not called on Owning client, manually called on server
UFUNCTION() UFUNCTION()
virtual void OnRep_ReplicatedTransform() virtual void OnRep_ReplicatedTransform()
{ {
...@@ -61,6 +61,7 @@ protected: ...@@ -61,6 +61,7 @@ protected:
UFUNCTION(Unreliable, Server, WithValidation) UFUNCTION(Unreliable, Server, WithValidation)
void ServerSendControllerTransformRpc(FVRTransformRep NewTransform); void ServerSendControllerTransformRpc(FVRTransformRep NewTransform);
// Function called each tick that checks if we need to send a new state from our local net owner to the server.
void UpdateState(float DeltaTime); void UpdateState(float DeltaTime);
public: public:
......
...@@ -24,6 +24,10 @@ protected: ...@@ -24,6 +24,10 @@ protected:
// Full transform update replication // Full transform update replication
///////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* See UClientTransformReplication for a description of the replication functions, they work exactly the same way.
*/
// Rate to update the position to the server, 100htz is default (same as replication rate, should also hit every tick). // Rate to update the position to the server, 100htz is default (same as replication rate, should also hit every tick).
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Networking", UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "Networking",
meta = (ClampMin = "0", UIMin = "0")) meta = (ClampMin = "0", UIMin = "0"))
......
...@@ -35,6 +35,9 @@ protected: ...@@ -35,6 +35,9 @@ protected:
UPROPERTY(EditDefaultsOnly, ReplicatedUsing = OnRep_ReplicatedTransform, Category = "Networking") UPROPERTY(EditDefaultsOnly, ReplicatedUsing = OnRep_ReplicatedTransform, Category = "Networking")
FVRTransformRep ReplicatedTransform; FVRTransformRep ReplicatedTransform;
/*
* See UClientTransformReplication for a description of the replication functions, they work exactly the same way.
*/
void UpdateState(float DeltaTime); void UpdateState(float DeltaTime);
UFUNCTION() UFUNCTION()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment