Movement Replication

Hi Everyone, I’ve started a basic FPS Game, and started out from a blank proyect, added some animations and setup my animation blueprint to move with some basic locomotion. Everything works just fine.

But when coming to Multiplayer, my server moves perfect, and the client see it correctly.
The client on the other hand, when i press the movement keys (WASD) it just plays the animations of movin (I have two floats moveForward, and MoveStrafe as replicated)
But it plays the animations in place, and doesnt move arround the level.

My code is the following

FPSCharacter.h:

 // This two floats are replicated so i cant use them on my animation blueprint to update animations.
	UPROPERTY(BlueprintReadOnly, Replicated, Category = "Player Movement")
	float MoveForwardValue;

UPROPERTY(BlueprintReadOnly, Replicated, Category = "Player Movement")
float MoveStrafeValue;

UFUNCTION(Server, Reliable, WithValidation)
void ServerMoveForward(float value);

UFUNCTION(Server, Reliable, WithValidation)
void ServerMoveStrafe(float value);

UFUNCTION()
void MoveForward(float value);

UFUNCTION()
void MoveStrafe(float value);

FPSCharacter.cpp:

AFPSCharacter::AFPSCharacter()
{
	PrimaryActorTick.bCanEverTick = true;


// Try adding this, dont seem to do much...	
	bReplicates = true;
	GetMovementComponent()->SetIsReplicated(true);
	GetCharacterMovement()->SetIsReplicated(true);
}


void AFPSCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(AFPSCharacter, MoveForwardValue);
	DOREPLIFETIME(AFPSCharacter, MoveStrafeValue);
}

void AFPSCharacter::ServerMoveForward_Implementation(float value)
{
	MoveForward(value);
}

void AFPSCharacter::ServerMoveStrafe_Implementation(float value)
{
	MoveStrafe(value);
}

bool AFPSCharacter::ServerMoveForward_Validate(float value)
{
	return true;
}

bool AFPSCharacter::ServerMoveStrafe_Validate(float value)
{
	return true;
}

void AFPSCharacter::MoveForward(float value)
{
	if (Role < ROLE_Authority)
	{
		ServerMoveForward(value);
		return;
	}
	
	MoveForwardValue = value;
	AddMovementInput(GetActorForwardVector() * value);
}

void AFPSCharacter::MoveStrafe(float value)
{
	if (Role < ROLE_Authority)
	{
		ServerMoveStrafe(value);
		return;
	}

	MoveStrafeValue = value;
	AddMovementInput(GetActorRightVector() * value);
}

void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
	PlayerInputComponent->BindAxis("MoveStrafe", this, &AFPSCharacter::MoveStrafe);
	PlayerInputComponent->BindAxis("LookVertical", this, &ACharacter::AddControllerPitchInput);
	PlayerInputComponent->BindAxis("LookHorizontal", this, &ACharacter::AddControllerYawInput);

}

It’s worth checking out the samples that Epic have provided, as they have the best practices for replication and movement.

That said, I think you’re hitting on a network authorisation issue. In that, iirc, only a few classes are directly allowed to transmit replication or RPC (remote procedure call) requests - ACharacter/APawn is not one of them, but APlayerController, is.

I think what you’ll want to do is move your input up to a Player Controller, if you haven’t already done so. Do a check if is host or is local, so that the host/local keeps processing in the same way… but if a client, then pass the move request over an RPC.
That will inform the Host/Server that you want to move your Pawn in some direction (or perform some action), so that the host can check if you’re allowed to do so, and attempt to perform the action - it will then also replicate this out to all connected clients.

Whilst you’re looking at network games, do also check out the docs on AGameState and APlayerState, as these will help you with game time, scores, health etc.

I’ve resolved this, i’m really new into ue4 and networking so i was kind of on a wrong mindset.
what i really needed to do, was just replicate my two controll floats to be used from the animation blueprint. my code ended up like this:

void AFPSCharacter::MoveStrafe(float value)
{	
AddMovementInput(GetActorRightVector() * value);

if (HasAuthority())
UpdateMoveStrafe(value);
else
ServerUpdateMoveStrafe(value);
}

-------------------------------------------------------------------

void AFPSCharacter::ServerUpdateMoveStrafe_Implementation(float value)
{
UpdateMoveStrafe(value);
}

------------------------------------------------------------

void AFPSCharacter::UpdateMoveStrafe(float value)
{
MoveStrafeValue = value;
}

A Pawn/Character has the same privileges (RunOnServer,RunOnOwningClient etc.) as a PlayerController when they are possessed by said PlayerController. It is called Connection Ownership and is determined by the Actor most outer owner. E.g a Pistol Owned by A Character Owned by a PlayerController. You could Implement Pistol player Input directly on the Pistol.

This is awesome! Thank you for adding more to this conversation :smiley:
Some quick searches (now I know what the terms are) brings up:

(In case anyone comes across this answer)

What’s odd, is I’ve had situations where the above should’ve worked… but hasn’t… yet, moving things into the PlayerController did resolve the issue. Hmm, perhaps there was something awry in the connection chain!?

There could be a number of problems that prevents that like if the client call the RPC before the server has updated the ownership. OnRep_Owner() is one way to ensure that the RunOnServer event is not sent too early.