Can't override default playercontroller

Hi,

I was trying to follow this tutorial (Accurately syncing Unreal’s network clock | by Josh Sutphin | Medium), and they seem to be modifying the playercontroller directly in the following snippets, even though they say they are changing a custom player controller: (the names of the files are not shown in the website)

PlayerController.h

public:

/** Returns the network-synced time from the server.
  * Corresponds to GetWorld()->GetTimeSeconds()
  * on the server. This doesn't actually make a network
  * request; it just returns the cached, locally-simulated
  * and lag-corrected ServerTime value which was synced
  * with the server at the time of this PlayerController's
  * last restart. */
virtual float GetServerTime() { return ServerTime; }

virtual void ReceivedPlayer() override;

protected:

/** Reports the current server time to clients in response
  * to ServerRequestServerTime */
UFUNCTION(Client, Reliable)
void ClientReportServerTime(
    float requestWorldTime,
    float serverTime
);

/** Requests current server time so accurate lag
  * compensation can be performed in ClientReportServerTime
  * based on the round-trip duration */
UFUNCTION(Server, Reliable, WithValidation)
void ServerRequestServerTime(
    APlayerController* requester,
    float requestWorldTime
);

float ServerTime = 0.0f;

PlayerController.cpp

void APlayerController::ServerRequestServerTime_Implementation(
    APlayerController* requester,
    float requestWorldTime
)
{
    float serverTime = GetWorld()->GetGameState()->
        GetServerWorldTimeSeconds();
    ClientReportServerTime(requestWorldTime, serverTime);
}

bool APlayerController::ServerRequestServerTime_Validate(
    APlayerController* requester,
    float requestWorldTime
)
{
    return true;
}

void APlayerController::ClientReportServerTime_Implementation(
    float requestWorldTime,
    float serverTime
)
{
    // Apply the round-trip request time to the server's         
    // reported time to get the up-to-date server time
    float roundTripTime = GetWorld()->GetTimeSeconds() - 
        requestWorldTime;
    float adjustedTime = serverTime + (roundTripTime * 0.5f);
    ServerTime = adjustedTime;
}
void APlayerController::ReceivedPlayer()
{
    Super::ReceivedPlayer();

    if(IsLocalController())
    {
        ServerRequestServerTime(
            this,
            GetWorld()->GetTimeSeconds()
        );
    }
}

And their game state cpp file:

float ACustomGameState::GetServerWorldTimeSeconds() const
{
    if(APlayerController* pc = GetGameInstance()->
        GetFirstLocalPlayerController(GetWorld())
    )
    {
        return pc->GetServerTime();
    }
    else
    {
        return GetWorld()->GetTimeSeconds();
    }
}

I initially tried to override the base APlayerController directly in a file as they seemed to be doing, but I couldn’t work out how to go about it, and am not sure it would work as even if I define the functions under APlayerController:: then the properties required (ServerTime) are still under my custom player controller.

Then I tried making my own custom player controller class, derived from APlayerController:
FPSPlayerController.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/GameStateBase.h"
#include "FPSPlayerController.generated.h"

/**
 * 
 */
UCLASS()
class FPSGAME_API AFPSPlayerController : public APlayerController
{
    GENERATED_BODY()


public:
    AFPSPlayerController();

    //https://medium.com/@invicticide/accurately-syncing-unreals-network-clock-87a3f9262594
    /** Returns the network-synced time from the server.
      * Corresponds to GetWorld()->GetTimeSeconds()
      * on the server. This doesn't actually make a network
      * request; it just returns the cached, locally-simulated
      * and lag-corrected ServerTime value which was synced
      * with the server at the time of this PlayerController's
      * last restart. */
    virtual float GetServerTime() { return ServerTime; }

    virtual void ReceivedPlayer() override;

protected:

    /** Reports the current server time to clients in response
      * to ServerRequestServerTime */
    UFUNCTION(Client, Reliable)
        void ClientReportServerTime(
            float requestWorldTime,
            float serverTime
        );

    /** Requests current server time so accurate lag
      * compensation can be performed in ClientReportServerTime
      * based on the round-trip duration */
    UFUNCTION(Server, Reliable, WithValidation)
        void ServerRequestServerTime(
            APlayerController* requester,
            float requestWorldTime
        );

    float ServerTime = 0.0f;
};

FPSPlayerController.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "fpsgame.h"
#include "FPSPlayerController.h"


AFPSPlayerController::AFPSPlayerController()
{
    APlayerController();
}

void AFPSPlayerController::ServerRequestServerTime_Implementation(
    APlayerController* requester,
    float requestWorldTime
)
{
    float serverTime = GetWorld()->GetGameState()->
        GetServerWorldTimeSeconds();
    ClientReportServerTime(requestWorldTime, serverTime);
}

bool AFPSPlayerController::ServerRequestServerTime_Validate(
    APlayerController* requester,
    float requestWorldTime
)
{
    return true;
}

void AFPSPlayerController::ClientReportServerTime_Implementation(
    float requestWorldTime,
    float serverTime
)
{
    // Apply the round-trip request time to the server's         
    // reported time to get the up-to-date server time
    float roundTripTime = GetWorld()->GetTimeSeconds() -
        requestWorldTime;
    float adjustedTime = serverTime + (roundTripTime * 0.5f);
    ServerTime = adjustedTime;
}

void AFPSPlayerController::ReceivedPlayer()
{
    Super::ReceivedPlayer();

    if (IsLocalController())
    {
        ServerRequestServerTime(
            this,
            GetWorld()->GetTimeSeconds()
        );
    }
}

And implemented the custom game state as shown on the website, with the h file:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameState.h"
#include "FPSGameState.generated.h"

/**
 * 
 */
UCLASS()
class FPSGAME_API AFPSGameState : public AGameState
{
	GENERATED_BODY()

public:
	float GetServerWorldTimeSeconds() const;

};

This doesn’t work either, as AFPSPlayerController* pc = GetGameInstance()-> GetFirstLocalPlayerController(GetWorld()) comes up with an error: "A value of type "APlayerController * " Cannot be used to initialize an entity of type “AFPSPlayerController*”

I’m extremely confused as to how to get this to work, and need it to be able to progress with my project. Please could someone help me?

I will be happy to reply with any more needed information.

Hello there DanZeDev,

I believe if the line*: AFPSPlayerController* pc = GetGameInstance()-> GetFirstLocalPlayerController(GetWorld())

is changed to: AFPSPlayerController* pc = Cast<AFPSPlayerController>(GetGameInstance()-> GetFirstLocalPlayerController(GetWorld()));, then the function call should work (as AFPSPlayerController is derived from APlayerController).

1 Like

Thank you FloKron! This solved the problem. I tried to find something about casts as I remembered having a similar problem before, but I guess I didn’t search properly.

Thanks again!

1 Like