Setting Character Velocity in Networking

I’m currently trying to get the movement system me and a friend designed to work with networking and unfortunately we didn’t plan for it to translate well when we first made it. I’ll explain how it works:

The system is closely modeled after how Source and Quake movement works.
The first thing that could be a pitfall is that it is calculating the players velocity every frame.
First an acceleration vector is created based off of the player’s inputs. So if the player presses W, it makes an acceleration vector forward, if they press A, backwards, W and A, forward and left, etc.
This acceleration vector is then fed into a function that calculates the players next increase in velocity based on their frame time. Then the final step is to take the player’s current velocity and add the resulting velocity from the previous steps.
This is done by directly setting the player’s velocity using SetVelocity();

The functions look like this:

void ASMCharacter::MovementStuff(float DeltaTime) {

	//Don't apply friction while jumping to prevent losing speed while bhopping
	if (spaceHold) {
		SMCharacterMovementComponent->GroundFriction = 0;
		SMCharacterMovementComponent->BrakingDecelerationWalking = 0;
		SMCharacterMovementComponent->BrakingDecelerationFlying = 0;
		GroundAcceleration = 10000;
	} else {
		SMCharacterMovementComponent->GroundFriction = 8;
		SMCharacterMovementComponent->BrakingDecelerationWalking = 2048.0;
		GroundAcceleration = 10000;
	}

	//Actually calculate next velocity.
	GetMovementComponent()->Velocity += GetNextFrameVelocity(CreateAccelerationVector(), DeltaTime);
	//ReplicateMovementPlease_Implementation(DeltaTime);
	DebugUtil::Message(FString::Printf(TEXT("%.2f u/s"), 
		MathUtil::ToHammerUnits(MathUtil::Hypotenuse(GetMovementComponent()->Velocity.X, GetMovementComponent()->Velocity.Y))), DeltaTime);
}

//creates the acceleration vector for the next frame
FVector ASMCharacter::CreateAccelerationVector() {
	FVector accel;
	accel = AActor::GetActorForwardVector() * AActor::GetInputAxisValue(FName("MoveForward"));
	accel += AActor::GetActorRightVector() * AActor::GetInputAxisValue(FName("MoveRight"));
	accel.Normalize(0.0001f);
	accel *= (GetMovementComponent()->IsFalling() || bPressedJump) ? AirAcceleration : GroundAcceleration;
	//GEngine->AddOnScreenDebugMessage(-1, 0.1f, FColor::Blue, FString::Printf(TEXT("%f, %f, %f"), accel.X, accel.Y, accel.Z));
	return accel;
}

//use the acceleration if the the magnitude of the next frame's velocity is less than the limit
FVector ASMCharacter::GetNextFrameVelocity(FVector AccelVector, float DeltaTime) {
	float magVprojA = GetMovementComponent()->Velocity.CosineAngle2D(AccelVector)*GetMovementComponent()->Velocity.Size();
	float magAxT = (AccelVector * DeltaTime).Size();
	if (GetMovementComponent()->IsFalling() || bPressedJump) {
		if (magVprojA < (MaxAirSpeedIncrease - magAxT)) {
			return AccelVector * DeltaTime;
		} else if (magVprojA < MaxAirSpeedIncrease) {
			return (MaxAirSpeedIncrease - magVprojA) * (AccelVector / AccelVector.Size());
		} else {
			return FVector(0, 0, 0);
		}
	} else {
		return AccelVector * DeltaTime;
	}
}

MovementStuff() calls on GetNextFrameVelocity() which takes in the frame time and CreateAccelerationVector()

Now of course this doesn’t translate directly to networking and I’m wondering how I could replicate these functions to work with it. I’ll go over somethings I did, but I don’t quite understand why it doesn’t work.

The first thing I did was:

  1. Every frame the client calls a function called ReplicateMovementPlease() which is a Server RPC function.
  2. ReplicateMovementPlease() calls on MovementStuff() with the frame time reported by the client.
  3. MovementStuff() does it’s thing and should set the Velocity of the MovementComponent of the client to the next velocity.

However it doesn’t work. The reason I think this works is because when I was reading I saw that the client is basically a dummy game that only takes in what the server tells it, and if the server changes the clients MovementComponent then it should be replicated back to the client (as I set it to always be replicated).

The second thing I did was:

  1. Create a new function called something like SetClientVelocity() that was a Client RPC function and change the MovementStuff function to return an FVector. All SetClientVelocity() did was take in the FVector returned by MovementStuff and try and set the clients velocity.
  2. Set ReplicateMovementPlease() to execute SetClientVelocity(MovementStuff(DeltaTime))

This didn’t work either. I kind of understand that maybe it didn’t work because it was still trying to set the clients velocity locally, but I thought maybe since it was going through the server this time, it would work.

All advice is appreciated and if it isn’t obvious enough, I’m very new to networking. I would very much like to keep the movement system like this as it has been the focus this entire time.

Actually the issue might be that I was never sending the server the current input values of the client, I can’t test it right now, but I will and close the issue if that was the problem

I did fix the initial issue by making a function for both the server and the client to update velocity, but there’s now there’s a new issue here the client moves slower than the server does and is very jittery.