In a multiplayer game the PlayerController exists on the server and client (but not other clients). I am a little confused on the interaction between these 2 instances of the PlayerController.
Let’s say the player is in playing state and dies and we call DetachFromControllerPendingDestroy(). This will cause the player to go to inactive state and BeginInactiveState() will be called which sets a timer to call UnFreeze().
So both the client and server instances of PlayerController will call BeginInactiveState() and both have their own copy of TimerHandle_UnFreeze. So both instances (server and client) will set their respective timers and both will call UnFreeze(). Both instances represent the same PlayerController but one exists on the server and one exists on the client. As a side note, should DetachFromControllerPendingDestroy() only be called on the server or should it be called on both the client and the server?
What if we want to call ChangeState() to change the player’s state? Does this need to be called on the server instance of the PlayerController only? What effect will calling ChangeState() on the client PlayerController have?
Let’s say we created a bool bRespawnOnDeath which we set to determine if we want the player to respawn automatically after dying. So in our UnFreeze() we might have:
void AMyPlayerController::UnFreeze()
{
if(bRespawnOnDeath)
{
ServerRestartPlayer();
}
}
But UnFreeze() gets called on both the client and the server. So ServerRestartPlayer() would get called on both the client and server instances of the PlayerController? What if we wanted to change the bool bRespawnOnDeath during gameplay? Would we have to make this bool replicated and then have the server set this value?
For example:
// In AMyPlayerController class
UFUNCTION(Server, Reliable, WithValidation)
void SetRespawnOnDeath();
If so, would it be better to put this bool in the PlayerState instead of the PlayerController (just like the bool bIsSpectating)?
What about what happens to a PlayerController during a seamless travel? I did some testing and it looks like a new PlayerController is created for each player. But the documentation says that all PlayerControllers (server only) and all local PlayerControllers (server and client) will persist automatically during a seamless travel. So they persist but then are replaced by new PlayerController instances once the travel is complete?
Sometimes my UnFreeze() gets called on a stale PlayerContoller after the seamless travel. The traveling playing switches to inactive state which calls BeginInactiveState() which sets the unfreeze timer and then a seamless travel happens. After the seamless travel, the player has a new instance of the PlayerController but the timer has already been set on the old one. So UnFreeze() on the now stale PlayerController is called which leads to undesirable behavior.
AFAIK, there is no way to stop BeginInactiveState() from setting the freeze timer unless you override BeginInactiveState() without calling Super::BeginInactiveState()? I don’t want my UnFreeze() code to be called on the stale PlayerController after a seamless travel. Do I have to manually clear the timer on both the server and the client before the seamless travel occurs to prevent this from happening?
This has been one of the most confusing parts of learning Unreal for me anyway. If anyone has time to answer all my questions and give a lot of examples in action I would appreciate it… I feel that there is not enough documentation on this part of the system and the interaction between the server/client instances of the PlayerController.