Pawn PlayerState reference on client is sometimes NULL

I have run into some problems with PlayerState reference inside Pawn class. PlayerState is sometimes NULL on clients. This happens right after loading up a level with a server and multiple clients. It is still NULL if you keep waiting for many minutes, so it should not be a replication issue. It is also completely random which clients it happens with and on how many.

I have provided an image where I show that PlayerState reference is NULL on two of clients.

I have managed to isolate this issue down to Player Controller function BeginSpectatingState that is called immediately at startup on all clients (I dont know why this function is called). way I see it, following is what normally happens:

  • When level has loaded up and gameplay has started, server calls Possess and PossessedBy for clients. At this point, PlayerState reference for Pawns are set to point to same as Controller PlayerState. PlayerState is eventually replicated down to client. (I have tested that this replication successfully happens).
  • Sometimes however, BeginSpectatingState function is called after this replication happen. problem is that this function calls UnPossessed for Pawn which again sets PlayerState to NULL on client.
  • Since this sometimes happens after PlayerState was replicated from server, I believe this may be what causes it to be NULL.

This issue only happens sometimes, and only on clients. Most of time, PlayerState is ok. I have managed to reproduce this issue in a project based on Third Person template. Please see my explanation below for modifications I made:

Reproducable steps:

Using Engine version 4.8.2, I created a project based on C++ Third Person template, and did following modifications:

  • Create a C++ function in custom character class with below code. Also make sure to add input to project that calls this function when clicking left mouse button (or any other button). We will just use this function to check if PlayerState is NULL.

    void ABugTestProjectCharacter::checkPlayerState()
    {
    if (Controller->PlayerState != NULL)
    {
    GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, TEXT(“Controller->PlayerState is ok”));
    }
    else
    {
    GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, TEXT(“Controller->PlayerState is NULL”));
    }

     if (PlayerState != NULL)
     {
     	GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, TEXT("PlayerState is ok"));
     }
     else
     {
     	GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, TEXT("PlayerState is NULL"));
     }
    

    }

To test, click Play button as Standalone Game from inside editor and test with at least four players (1 listen server and 3 clients). (Testing with more than two players increases chances for it to happen). I also used following settings: Uncheck “Use Single Process” and use Editor Multiplayer Mode as “Play As Listen Server”.

When clicking left mouse button on some of clients, you will hopefully see that on some of them, PlayerState reference is reported as being NULL. Most of time however, PlayerState reference is ok, therefore it is a good idea to test multiple times. Hopefully you will see it as NULL some of times.

Hi ,

I’ve assigned a member of our team to look into this issue for you, and they’ll post here if they require any additional information. Thanks for your report!

Hey -

I tried testing this by calling function on LMB click. With 4 players I only saw PlayerState as NULL once however Controller->PlayerState was NULL as well. After next frame when I clicked again they were both “ok”. This only occurred once out of about 15 tried with four players each time. all other attempts showed all players as ok when I clicked. How often are you seeing PlayerState as NULL with Controller->PlayerState being ok and is there anything else that might increase odds of this occurring?

Hello

For me, at least every third or fourth time that I test this, PlayerState is NULL on at least one of clients. Controller->PlayerState is always ok.

It is also important to note that for me, when PlayerState is reported as NULL, it stays permanently NULL. Even if I have game instance running for several minutes, PlayerState is still NULL when I click LMB.

At moment I dont know about anything else that might increase odds for this occurring.

As I said in my original post I also believe this problem occurs because BeginSpectatingState function is called for Controller on clients at startup. This function will sometimes call UnPossess which effectively sets PlayerState to NULL on local clients. I have also tested that, for any of these clients, servers authorative version of this PlayerState is ok.

I am going to sleep now since I live in norway, but when I wake up tomorrow, I will try to create a project where this happens for me and post it here so that maybe you can take a look at same project as me. Maybe this will help. I will also see if I can find out anything more about this problem.

Thank you very much for taking a look at this.

I have created a new project now in engine version 4.8.2, and overriden several functions and added debug messages to screen inside them to see when and if they are called. I have included project as an attachment. You will have to rebuild project after unzipping file. following modifications are done (in C++):

  • Inside BugTestProjectCharacter class, I have done following:
  • Added function checkPlayerState. This function is called when clicking LMB.
  • I have overriden UnPossessed function declared in Pawn.
  • I then added a new class called BugTestProjectPlayerController that extends from PlayerController. Inside this class, I have done following:
  • I have overriden BeginSpectatingState function declared in PlayerController.
  • I have overriden UnPossess function declared in Controller.
  • Finally, I modified BugTestProjectGameMode to use BugTestProjectPlayerController class as its Player Controller.

To test project, make sure to do following:
Click Play in editor as “Standalone Game” with following settings under Advanced Settings → Multiplayer Options:

  • Number of players: 4
  • Uncheck “Use Single Process”
  • Editor Multiplayer Mode: “Play as Listen Server”

link text

I have tested project multiple times now with four players and can report following findings:

  • Sometimes, PlayerState is ok on all clients.
  • Most of time, PlayerState is NULL on either one client or two of clients.
  • PlayerState can also be NULL on all three clients (Occurs very rarely).

I also believe I know why PlayerState is sometimes NULL. At start of game, server will initialize everything, among PlayerState for all clients. PlayerState will eventually be replicated down to client. Also at startup, BeginSpectatingState function is called on all clients. If GetPawn() is not NULL, this function calls UnPossess which again calls GetPawn()->UnPossessed. This function sets PlayerState to NULL on client. If this call happens after PlayerState was replicated down to client, I believe client now sets its own PlayerState reference to NULL, which is why PlayerState is NULL on some of clients.

This is screen output I get:

When PlayerState is NULL, I get following screen output:

  • ABugTestProjectCharacter::UnPossessed
  • ABugTestProjectPlayerController::UnPossess
  • GetPawn() != NULL
  • ABugTestProjectPlayerController::BeginSpectatingState
  • GetPawn() == NULL
  • ABugTestProjectPlayerController::BeginSpectatingState

When PlayerState is ok, I get either this screen output:

  • GetPawn() == NULL
  • ABugTestProjectPlayerController::BeginSpectatingState

or this screen output (Same as first one):

  • ABugTestProjectCharacter::UnPossessed
  • ABugTestProjectPlayerController::UnPossess
  • GetPawn() != NULL
  • ABugTestProjectPlayerController::BeginSpectatingState
  • GetPawn() == NULL
  • ABugTestProjectPlayerController::BeginSpectatingState

Hey Gardennnnnnn-

We were able to find repro for this issue and have submitted a bug report (UE-19058) for investigation.

Cheers

I still have this issue, is it fixed?

Hey -

I checked status of ticket I had entered, and it is still open. Unfortunately we do not have any estimate for when it will be resolved. However, I have added a comment to ticket to indicate this is still affecting people.

Cheers

Thanks , just FYI we’re seeing this exact same issue on our project Space Dust Racers as well, both in 4.8 and 4.10 (after upgrading).

And thanks for filing detailed bug report !

Hi , has there been any progress on UE-19058? We’re still seeing it in UE4.10. It’d be great if we could fix it before release!

Hey sds–

UE-19058 has not been resolved yet. If there is any information specific to your case that has not been mentioned here I can add it to report.

Cheers

Thanks . We’re seeing it in Development and Shipping builds somewhat infrequently (perhaps 1/100?) when a client joins a server (this happens in separate processes, not a single process through editor). I’ve tried testing with different lag simulation settings but it doesn’t seem to make a huge difference. Cheers!

(this is unfortunately not an answer but system would not let me post my bug confirmation with more than 2000 characters otherwise)

Hi everyone

We experienced some random editor crashes when testing our multiplayer game with more than 1 player in PIE where PlayerState on a clients locally controlled pawn suddenly was NULL even though it was successfully received in OnRep_PlayerState before.

Turns out that in some cases PlayerState on the APawn can get replicated before SpectatorClass in AGameState gets replicated. In that case, AGameState then calls ReceivedSpectatorClass which then goes on to put all local player controllers into spectator mode. Controllers entering spectator mode will UnPossess pawn and that in turn sets PlayerState back to NULL.

I have made a tiny test project which implements a custom AGameMode, AGameState and APawn which just logs out when relevant steps are called during initialization. Then during bad late call to AMyPawn::UnPossessed and during AMyPawn::Tick it prints a message that things are going badly.

It is seems very much a race condition in that it happens very randomly. Sometimes it doesn’t happen at all between editor starts and sometimes it happens every second time. Sometimes it happens more often when testing with 4 or more PIE players instead of just 2.

Here is me running project in PIE 4 times with 2 players. First and fourth time ended up with client pawn having no PlayerState. I set log output to just print info from client (HasAuthority() == false) and only included lines regarding controlled pawn here.


[0.00] AMyGameMode::OnConstruction
[0.00] AMyGameMode::RestartPlayer
[0.28] AMyGameMode::RestartPlayer
[0.34] AMyPawn         OnConstruction    - PlayerState: 0x0 - State: Has No Controller
[0.37] AMyPawn         OnRep_PlayerState - PlayerState: 0x4674e400 - State: Spectating
[0.37] AMyGameState    OnConstruction
[0.37] AMyGameState    ReceivedSpectatorClass
[0.37] AMyPawn         UnPossessed       - PlayerState: 0x4674e400 - State: Spectating
[0.37] AMyPawn             UnPossessed due to ReceivedSpectatorClass during init after receiving PlayerState will remove PlayerState again
[0.37] AMyPawn         BeginPlay         - PlayerState: 0x0 - State: Has No Controller
[0.37] AMyGameState    BeginPlay
[0.59] AMyPawn         Restart           - PlayerState: 0x0 - State: Spectating
[0.60] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[0.72] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[0.84] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[0.96] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.08] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.20] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.33] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.45] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.57] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.69] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.82] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.94] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.06] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.19] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.31] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.43] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.56] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.68] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.81] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.94] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[3.07] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[3.07] AMyPawn         EndPlay           - PlayerState: 0x0 - State: Playing
[3.07] AMyPawn         UnPossessed       - PlayerState: 0x0 - State: Playing
[3.07] AMyGameState    EndPlay
[3.07] AMyGameMode::EndPlay

[0.00] AMyGameMode::OnConstruction
[0.00] AMyGameMode::RestartPlayer
[0.24] AMyGameMode::RestartPlayer
[0.27] AMyPawn         OnConstruction    - PlayerState: 0x0 - State: Has No Controller
[0.29] AMyGameState    OnConstruction
[0.29] AMyGameState    ReceivedSpectatorClass
[0.29] AMyPawn         UnPossessed       - PlayerState: 0x0 - State: Spectating
[0.29] AMyPawn         BeginPlay         - PlayerState: 0x0 - State: Has No Controller
[0.29] AMyGameState    BeginPlay
[0.33] AMyPawn         OnRep_PlayerState - PlayerState: 0x427c4800 - State: Has No Controller
[0.53] AMyPawn         Restart           - PlayerState: 0x427c4800 - State: Spectating
[1.12] AMyPawn         EndPlay           - PlayerState: 0x427c4800 - State: Playing
[1.12] AMyPawn         UnPossessed       - PlayerState: 0x427c4800 - State: Playing
[1.12] AMyGameState    EndPlay
[1.13] AMyGameMode::EndPlay

[0.00] AMyGameMode::OnConstruction
[0.00] AMyGameMode::RestartPlayer
[0.23] AMyGameMode::RestartPlayer
[0.28] AMyPawn         OnConstruction    - PlayerState: 0x0 - State: Has No Controller
[0.30] AMyGameState    OnConstruction
[0.30] AMyGameState    ReceivedSpectatorClass
[0.30] AMyPawn         UnPossessed       - PlayerState: 0x0 - State: Spectating
[0.30] AMyPawn         BeginPlay         - PlayerState: 0x0 - State: Has No Controller
[0.30] AMyGameState    BeginPlay
[0.33] AMyPawn         OnRep_PlayerState - PlayerState: 0x59365600 - State: Has No Controller
[0.53] AMyPawn         Restart           - PlayerState: 0x59365600 - State: Spectating
[0.96] AMyPawn         EndPlay           - PlayerState: 0x59365600 - State: Playing
[0.96] AMyPawn         UnPossessed       - PlayerState: 0x59365600 - State: Playing
[0.96] AMyGameState    EndPlay
[0.96] AMyGameMode::EndPlay

[0.00] AMyGameMode::OnConstruction
[0.00] AMyGameMode::RestartPlayer
[0.27] AMyGameMode::RestartPlayer
[0.32] AMyPawn         OnConstruction    - PlayerState: 0x0 - State: Has No Controller
[0.34] AMyPawn         OnRep_PlayerState - PlayerState: 0x33dc9600 - State: Spectating
[0.34] AMyGameState    OnConstruction
[0.34] AMyGameState    ReceivedSpectatorClass
[0.34] AMyPawn         UnPossessed       - PlayerState: 0x33dc9600 - State: Spectating
[0.34] AMyPawn             UnPossessed due to ReceivedSpectatorClass during init after receiving PlayerState will remove PlayerState again
[0.34] AMyPawn         BeginPlay         - PlayerState: 0x0 - State: Has No Controller
[0.34] AMyGameState    BeginPlay
[0.57] AMyPawn         Restart           - PlayerState: 0x0 - State: Spectating
[0.58] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[0.70] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[0.82] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[0.94] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.06] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.18] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.31] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.43] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.55] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.68] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.80] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[1.92] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.04] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.17] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.29] AMyPawn         Tick on bad pawn! - PlayerState: 0x0 - State: Playing
[2.29] AMyPawn         EndPlay           - PlayerState: 0x0 - State: Playing
[2.29] AMyPawn         UnPossessed       - PlayerState: 0x0 - State: Playing
[2.29] AMyGameState    EndPlay
[2.30] AMyGameMode::EndPlay

As one can see on bad first and fourth attempt, AMyPawn gets constructed and then gets OnRep_PlayerState right away even before AMyGameState gets constructed (all happening seemingly on same tick, same time stamp). On second and third correct run ReceivedSpectatorClass happens what seems a tick before OnRep_PlayerState.

We confirmed this on two machines with 4.10.2, 4.10.3 and 4.11.0 preview 5.

test project is attached.

Thanks for looking into this,

Hey -

Thank you for detailed information about pawn/playerstate/game state. I have updated bug report to include this information as well as sample project you provided.

Cheers

This might also help, I was able to reproduce this issue much more frequently after a server travel using these network settings:
-pktloss=2 -pktlag=75 -pktlagvariance=10 -pktdup=2 -pktorder=1

We were seeing this relatively consistently in a test project, and I was able to track down what I believe is reason for some of calls not working as expected.

short version of the “fix” is to test for GetPawn() == nullptr in ReceivedSpectatorClass() before calling BeginSpectatingState(). That way only on initial connect does it cause a problem. In our case, this fixed problem. We also had a second potential fix that involved rebuilding PlayerState pointer inside of client reset when it acknowledged pawn. This however still caused Pawn value to thrash by switching to a spectator pawn and back.

long version of what was happening, based on my testing, was as follows. Note that our Pawn class was loaded via LoadClass and was not loaded by GameMode’s default values.

  1. Net Player joins
  2. Player pawn class is loaded via GetDefaultPawnClassForController
  3. Player pawn is spawned via SpawnDefaultPawnFor
  4. ClientRestart() is called inside of Server’s Possess
  5. ClientRestart() sends a null for NewPawn since PackageMap has not ack’d pawn class (blueprint class)
  6. ClientRestart() happens on client, sets to NULL pawn, does not enter Playing state since Pawn is null
  7. Controller’s Pawn replicates, calls OnRep_Pawn, sets Pawn
  8. Pawn’s PlayerState replicates, calls OnRep_PlayerState, sets PlayerState
  9. GameState’s SpectatingClass replicates, calls OnRep_SpectatorClass, which calls ReceivedSpectatorClass, which is forwarded to all local player controllers
  10. Since state is Spectating because ClientRestart() failed, and SpectatingPawn is null, it calls BeginSpectatingState()
  11. BeginSpectatingState() sees that Pawn variable is set, calls Unpossess()
  12. Unpossess clears Pawn’s PlayerState variable on client
  13. ClientRestart() is called again on a retry with correct Pawn value.
  14. ClientRestart() uses SetPawn rather than possess, which does NOT set PlayerState locally
  15. ClientRestart() AcknowledgesPawn
  16. ClientRestart() changes state to Playing

Hey bdavey-

This issue was tested and has not been seen to occur as of 4.13. Please try making a copy of your project and let me know if you still have this occurring on your end in latest engine version.

This issue still seems to be occurring in 4.12.5 and is a real issue for my current project. I’m working on a workaround involving re-launching clients with invalid network setups automatically but this is just trying to work around issue. This issue was originally posted over a year ago, has no progress been made on it in all that time?

I am still experiencing this issue in 4.19, building off of 1st Person template.