Movement not replicated

Hello,

I will try to explain my problem as best as I can, given that it’s quite a strange one.
My multiplayer project is very simple at the moment. I have a character blueprint with C++ to control it’s movement logic and a custom player controller to send input to the character.
All the game mode does is spawn the default pawn.

It seems that only the server is able to control the character. All the clients, although they accept input (I can see that with a debug message on screen), their character does not move.

Replication is on for character blueprint and the “replicate movement” checkbox is ticked. I don’t see what different I am doing to the third person sample project which works correctly, even in dedicated server mode.

I am posting some of the player controller and character code, if it’s worth anything to you guys.

The controller:

void APlayerMechController::SetupInputComponent()
{
    Super::SetupInputComponent();

    // axis
    InputComponent->BindAxis("Horizontal", this, &APlayerMechController::OnHorizontal);
    InputComponent->BindAxis("Vertical", this, &APlayerMechController::OnVertical);
    
    // buttons
    InputComponent->BindAction("FreeRun", IE_Pressed, this, &APlayerMechController::OnFreeRunPress);
    InputComponent->BindAction("FreeRun", IE_Released, this, &APlayerMechController::OnFreeRunRelease);
}

void APlayerMechController::OnHorizontal(float axis)
{
    MechPawn = Cast<AMech>(GetPawn());
    if (MechPawn != nullptr && axis != 0.0f)
    {
        MechPawn->OnHorizontal(axis);
    }
}

void APlayerMechController::OnVertical(float axis)
{
    MechPawn = Cast<AMech>(GetPawn());
    if (MechPawn != nullptr && axis != 0.0f)
    {
        MechPawn->OnVertical(axis);
    }
}

And part of the character movement code:

void AMech::BeginPlay()
{
	Super::BeginPlay();
	
    UCharacterMovementComponent* MovementCmp = Cast<UCharacterMovementComponent>(GetMovementComponent());
    OriginalWalkSpeed = MovementCmp->MaxWalkSpeed;
    
    CurrentHorizontal = CurrentVertical = 0.0f;
    CurrentRotation = GetActorRotation();
}

AMech* AMech::FindEnemy()
{
    for (TActorIterator<AMech> It(GetWorld()); It; ++It)
    {
        if (*It != this)
        {
            return *It;
        }
    }
    return nullptr;
}

void AMech::OnHorizontal(float axis)
{
    CurrentHorizontal = axis;
    OnAxisUpdate(CurrentHorizontal, CurrentVertical);
}

void AMech::OnVertical(float axis)
{
    CurrentVertical = axis;
    OnAxisUpdate(CurrentHorizontal, CurrentVertical);
}

float AMech::GetForwardSign() const
{
    FVector Fwd = GetActorForwardVector();
    FVector CamFwd = Controller->GetViewTarget()->GetActorForwardVector();
    
    FVector Perp = FVector::CrossProduct(Fwd, CamFwd);
    return Perp.Z > 0 ? -1.0f : 1.0f;
}

void AMech::OnAxisUpdate(float horizontal, float vertical)
{
    if (Controller != nullptr) {
        
        float dt = GetWorld()->GetDeltaSeconds();
        UCharacterMovementComponent* MovementCmp = Cast<UCharacterMovementComponent>(GetMovementComponent());

        FRotator CamRotation = Controller->GetViewTarget()->GetActorRotation();
        
        FVector MoveDir(0,0,0);
        // free running around the world
        if (IsFreeRunning)
        {
            MovementCmp->MaxWalkSpeed = OriginalWalkSpeed;
            
            float Angle = PI / 2.0f - FMath::Atan2(vertical, horizontal) + FMath::DegreesToRadians(CamRotation.Yaw);
            
            FVector V(horizontal, vertical, 0.0f);
            
            float Forward = V.Size();
            float Strafe = 0.0f;

            if (!FMath::IsNearlyZero(Forward)) {
                LastTargetRotation = FRotator(0.0f, FMath::RadiansToDegrees(Angle), 0.0f);
            }
            
            MoveDir = GetActorForwardVector() * Forward;
        }
        // movement while facing the opponent + strafe
        else
        {
            AMech* Enemy = FindEnemy();
            if (Enemy)
            {
                MovementCmp->MaxWalkSpeed = OriginalWalkSpeed / 4.0f;
                
                FVector LookDir = (Enemy->GetActorLocation() - GetActorLocation());
                LookDir.Normalize();
                
                LastTargetRotation = LookDir.Rotation();
                
                MoveDir = GetActorForwardVector() * horizontal * GetForwardSign() + GetActorRightVector() * vertical;
            }
        }
        
        CurrentRotation = FMath::RInterpTo(CurrentRotation, LastTargetRotation, dt, 10.0f);
        SetActorRotation(CurrentRotation);
        MovementCmp->AddInputVector(MoveDir);
    }
    
}

I also have a camera blueprint which is living in the scene. This camera is just setting the view perpendicular to the 2 game characters. How I set the view target is using this (in the camera blueprint):

camera blueprintalt text

I have noticed that when I do a “showdebug input” I only see input being collected by the Camera actor in the scene. Unless I am understanding this debug command incorrectly, how come the characters do get input?

I am sorry for the long post, I hope someone sheds some light.
Thanks!

How does the player possess your character? In UE4 networking only the player controller that is in chain of ownership of the pawn can affect its movement.

Hi Mikom,

I don’t do anything special to possess the character. Should I be calling Possess on the pawn? Perhaps I wrongly assumed that it would do that automatically.

I have noticed that the third person sample does not override the player controller class.

Hmm, this way GameMode should spawn the subsequent players correctly.

If you are ok with sharing the project, maybe you could post it on some dropbox, and I’ll take a look (or mail me the link to mikom3@gmail.com).

Only folders, Config, Content, Source and .uproject file.

Yes I will send it to you in a bit, many thanks for looking into this.

I downloaded the project and looked at it briefly. It seems that everything is configured properly.

Interesting thing is that client and server don’t have symmetry in Role and RemoteRole properties. For client’s player character on client side it should be Role=AutonomousProxy RemoteRole=Authority, however Role is SimulatedProxy.

I’ll look into it deeper if I find time this evening but I can’t promise anything.

Right after writing the last comment, I found this post:

You are missing Super:: call in AMech::GetLifetimeReplicatedProps.

After adding it, everything works flawlessly

I should add this to my “Things to check when UE4 replication misbehaves” list.

Ahh how did I miss this!
Many thanks man, I will fix this when I go home. you for taking time to look into it :slight_smile:

My Lifesaver =)

This mistake will also break movement replication…
if( Role = ROLE_Authority) instead of if(Role == ROLE_Authority)

gosh, that was it! you so much <3 I was starting to lose my mind ^^