Vehicle Movement Replication is not synced

Hello comunity,
i wanted to make a game with vehicles, but the problem is that the vehicles get out of sync after a time or when the vehicle jump over something or so, then this will happen.

i also made a video to describe the problem better: - YouTube

i dont know what this is, but it has something to do with replication. I´m using the Standard PhysX Vehicles.

hope you can help me ^^

zFunked ;D

Same problem. On version 4.19 everything was fine, after the update I have the same problem.

Same thing! Is the reason really in the engine? Just today I switched to 4.20 for my vehicle game project, and I have my cars on a high building with elimination floor on outside (kinda like derby, you fall you lose). Now I started noticing completely insane replication on 4.20, when I run 2 players + dedicated server and do a small collision between cars, server sometimes think that one car flew off so it ends the game. Imagine my confusion where I see 2 cars standing still on the roof, on both clients, it’s like server didn’t even try to replicate the falling position, just continued consuming input.

Now I’m not saying that the issue is the same, and it could be that I made a mistake somewhere, but I am definitely interested in this thread. I don’t remember it happening on 4.19, but to be honest I didn’t play around with collisions that much.

Same problem here. Brand new project with the adv vehicle blueprint. Spawning 2+ vehicles in multiplayer results in off-sets of their replaced representations over time. Easily reproduced by driving in circles with one vehicle and observing from the other.

The player’s position on the server is different from what the client shows (which made me doubt my scripting skills for about 5 hours before noticing all this).

Hope they are aware!

I also have this problem in my project after upgrading to 4.20.
I can also add that i’m having trouble moving the vehicles using SetActorLocation, When running a simple SetActorLocation on the server to move a vehicle to a new location, instead of the client vehicle instantly syncing to the location it now floats towards the location.
Does anyone else have this happen?

The problem is already registered, but you can vote for the fix:
UE-62345

Same here too.

Vehicle replication worked in 4.19 & no longer works in 4.20.
From what I can tell the vehicle movement for all other clients is synced correctly to the server’s version ( testing 4 clients against a standalone server ).

The physics simulation appears to be running on both the server and the local client, however no client side correction is happening, and so the local simulation diverges very quickly from the server’s version.
The server’s version of the movement does get replicated to the client as the values in ReplicatedMovement on the vehicle pawn are all correct wrt to the server, but the local client does nothing with them as far as I can tell.
It does however apply the values for “non local clients” correctly.

Digging further into the code it looks as if this is down to how the new FPhysicsReplication has been implemented, FPhysicsReplication::OnTick skips updating the actor if its role anything other than ROLE_SimulatedProxy.

The solution seems to be to roll your own client side correction for the vehicle pawn by overriding PostNetReceivePhysicState or OnRep_ReplicatedMovement. Which would be odd as it was all working in 4.19.

Would be good to get some info from Epic in this respect as I’m sure I’ve just missed something.

I have a workaround.

Turns out that you can replace FPhysicsReplication with your own version.

You need to create a subclass of FPhysicsReplication, and a factory class derived from IPhysicsReplicationFactory to instantiate it.

Override the Tick function of FPhysicsReplication with your own code - all I’ve done here is take the basic implementation from FPhysicsReplication and changed the role check to be for ROLE_AutonomousProxy.

Then during initialisation register your factory class with the physics engine.

This fixes the issue ( YMMV ).

MyPhysicsReplication.h

#pragma once

#include "CoreMinimal.h"
#include "PhysicsPublic.h"
#include "PhysicsReplication.h"

class MY_API FMyPhysicsReplication : public FPhysicsReplication
{
public:
	FMyPhysicsReplication(FPhysScene* PhysScene);

protected:
	virtual void OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrimitiveComponent>, FReplicatedPhysicsTarget>& ComponentsToTargets) override;
};

class MY_API FMyPhysicsReplicationFactory : public IPhysicsReplicationFactory
{
public:
	FMyPhysicsReplicationFactory();

	virtual FPhysicsReplication* Create(FPhysScene* OwningPhysScene) override;
	virtual void Destroy(FPhysicsReplication* PhysicsReplication) override;
};

MyPhysicsReplication.cpp

#include "MyPhysicsReplication.h"
#include "PhysicsEngine/PhysicsSettings.h"


FMyPhysicsReplication::FMyPhysicsReplication(FPhysScene* PhysScene)
: FPhysicsReplication(PhysScene)
{
}

void FMyPhysicsReplication::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrimitiveComponent>, FReplicatedPhysicsTarget>& ComponentsToTargets)
{
	FPhysicsReplication::OnTick( DeltaSeconds, ComponentsToTargets );

	const FRigidBodyErrorCorrection& PhysicErrorCorrection = UPhysicsSettings::Get()->PhysicErrorCorrection;
	for (auto Itr = ComponentsToTargets.CreateIterator(); Itr; ++Itr)
	{
		bool bRemoveItr = false;
		if (UPrimitiveComponent* PrimComp = Itr.Key().Get())
		{
			if(FBodyInstance* BI = PrimComp->GetBodyInstance(Itr.Value().BoneName))
			{
				FReplicatedPhysicsTarget& PhysicsTarget = Itr.Value();
				FRigidBodyState& UpdatedState = PhysicsTarget.TargetState;
				AActor* OwningActor = PrimComp->GetOwner();
				if( OwningActor && OwningActor->Role == ROLE_AutonomousProxy )
				{
					if (UpdatedState.Flags & ERigidBodyFlags::NeedsUpdate)
					{
						const float PingSecondsOneWay = 0;
						const bool bRestoredState = ApplyRigidBodyState( DeltaSeconds, BI, PhysicsTarget, PhysicErrorCorrection, PingSecondsOneWay );
						if (bRestoredState)
							bRemoveItr = true;
						PrimComp->SyncComponentToRBPhysics();
					}
				}
			}
		}

		if (bRemoveItr)
			Itr.RemoveCurrent();
	}
}

FMyPhysicsReplicationFactory::FMyPhysicsReplicationFactory()
{
}

FPhysicsReplication* FMyPhysicsReplicationFactory::Create(FPhysScene* OwningPhysScene)
{
	return new FMyPhysicsReplication( OwningPhysScene );
}

void FMyPhysicsReplicationFactory::Destroy(FPhysicsReplication* PhysicsReplication)
{
	delete PhysicsReplication;
}

To register your factory with the engine you need to do this sometime during game startup ( during GameInstance Init() seems to be fine ).

	FPhysScene::PhysicsReplicationFactory = MakeShareable( new FMyPhysicsReplicationFactory() );

Do it according to your steps, the position of the vehicle is synchronized, but the movement is not smooth, very obvious.

You may need to change some of the physics replication parameters. They’ve changed quite a bit from 4.19.

[/Script/Engine.PhysicsSettings]
PhysicErrorCorrection=(PingExtrapolation=0.100000,
ErrorPerLinearDifference=1.000000,
ErrorPerAngularDifference=1.000000,
MaxRestoredStateError=1.000000,
PositionLerp=0.000000,
AngleLerp=0.400000,
LinearVelocityCoefficient=100.000000,
AngularVelocityCoefficient=10.000000,
ErrorAccumulationSeconds=0.500000,
ErrorAccumulationDistanceSq=15.000000,
ErrorAccumulationSimilarity=100.000000)

I’ve left these at their defaults, but tweaking these may help.

I have also switched over to using the new ReplicationGraph system, it’s much more efficient.

Hey Slick, thanks for this, could you please provide some more info on how to “register your factory with the engine you need to do this sometime during game startup ( during GameInstance Init() seems to be fine ).”
Sorry I am a C++tard (will be looking to change that soonish).

Thanks :wink:

Sure,

MyGameInstance.h

#pragma once

#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"

UCLASS()
class MY_API UMyGameInstance : public UGameInstance
{
	GENERATED_BODY()

public:
	virtual void Init() override;
};

MyGameInstance.cpp

#include "MyGameInstance.h"
#include "MyPhysicsReplication.h"

void UMyGameInstance::Init()
{
	Super::Init();
	FPhysScene::PhysicsReplicationFactory = MakeShareable(new FMyPhysicsReplicationFactory());
}

Then you’ll need to point your project at your game instance class.
In Project Settings, Maps & Modes

Awesome, thank you :slight_smile:

I ran into this problem too. Thanks Slick. Lifesaver.

Looking around the new code I see many hints that it’s setup to support client authoritative vehicles… which I have working now by just using Slick’s solution and,

  1. Doing nothing on the owner client’s PostNetReceivePhysicState (ignore it and dont try to sync)

    void MyWheeledVehicle::PostNetReceivePhysicState()
    {
    if (!IsLocallyControlled())
    {
    Super::PostNetReceivePhysicState();
    }
    }

  2. Adding the RB state to the server update calls from the clients. The server is just snapping to the client’s version right now.

     void UMyWheeledVehicleMovementComp4W::MyServerUpdateState_Implementation(float InSteeringInput, float InThrottleInput, float InBrakeInput, float InHandbrakeInput, int32 InCurrentGear, FRigidBodyState NewBodyState)
     {
    


    if (UpdatedPrimitive)
    {
    FName BoneName;
    FBodyInstance* BI = UpdatedPrimitive->GetBodyInstance(BoneName);

     		const FVector NewLinVel = FVector(NewBodyState.LinVel);
     		const FVector NewAngVel = FVector(NewBodyState.AngVel);
     		const FVector NewPos = FVector(NewBodyState.Position);
     		const FQuat NewAng = FQuat(NewBodyState.Quaternion);
     
     		BI->SetBodyTransform(FTransform(NewAng, NewPos), ETeleportType::TeleportPhysics);
     
     		// Set the new velocities
     		BI->SetLinearVelocity(NewLinVel, false);
     		BI->SetAngularVelocityInRadians(FMath::DegreesToRadians(NewAngVel), false);					
     	}
     }
    

I haven’t had a chance to test this with other people, in true mp, but I suspect there’s many problems with the fact there’s no way for the server to hard correct the owner client right now. I’m sure there’s many ways, but I’ll probably try doing a check in PostNetReceivePhysicState and if the error is large then snap the owner client.

Ideas?

Hey tehKpayne,
sorry for the late response, we don´t want to use C++. We made an C++ Project lastly for learning purposes and added some content packs after that the whole script won´t recompile because VS2017 said that there were over 200 errors. So i´m letting my fingers out of c++ for now.
There must be a solution for this …
I´m really confused now

I also have this problem as of version 4.20

Epic is claiming its fixed in 4.21! Unreal Engine Issues and Bug Tracker (UE-62345)

Yes, this problem is solved, thanks to everyone who voted =)

Is anyone having this issue in 4.21? It seems to be better, but I cant find any way to keep them 100% synced other than an ugly long polling setup. I’ve tried replicating dif components, with or without the new Replicate Physics to Autonomy. I cant find any combo to get vehicle replication correct