Why does the "New Player" player controller from GameMode "OnPostLogin" seem to reference the server only?

It seems simple enough. All I want to do is store a player name in my player controller or player state. But those are reset when joining a server (which changes the level). So I store the player name in my game instance, which should be persistent and local to the client. Here are the steps:

Menu widget has a, editable text box:

  • On change, update the MPGameInstance → PlayerName string variable.

MPPlayer controller Event BeginPlay:

  • grab the PlayerName variable from my game instance
  • grab my player controller (self, since I am running the player controller “BeginPlay”)
  • call a “Run on Server” RPC SetPlayerSettings and pass the name and the player controller in as parameters.

MPPlayerController “SetPlayerSettings” custom event (RunOnServer, reliable):

  • this just gets the MPGameState and calls the “ServerSetPlayerStateInfo” RPC below, passing along the above parameters.

MPGameState “ServerSetPlayerStateInfo” custom event (Multicast, reliable):

  • Pulls the player state from the passed-in player controller
  • casts to MPPlayerState
  • sets MPPlayerState->DisplayName string variable to the passed-in string.

Now, the player state SHOULD have whatever name is stored in that client’s local game instance. BUT:

MPGameMode Event OnPostLogin:

  • I grab the “New Player” output and pull out the Player State target
  • Cast the player state to MPPlayerState
  • Send a notification that MPPlayerState->DisplayName has joined the session.

BUT the notification always shows the name of the server player.

I have tried many different methods of this:

  • Setting the player name as a player controller string variable: Gets reset to default on joining / level load.
  • Setting the string variable in game instance, and setting player controller name variable on BeginPlay: Shows server player name.

I’ve tried several other ways that I forget by now, but the results are the same: The player name of the client either gets reset to default or set to the value stored in the server’s game instance.

I understand that the player controller exists on both the server and the owning client. But it seems that the BeginPlay event only cares about the server when you’re joining a session or changing levels in a joined session.

Can anyone correct me or set me in the right direction? My end goal is just to store a widget-set player name as a string variable somewhere it won’t get reset on join / level change, and have my player controller re-inherit that name when joining a session or changing levels.

I wish there was more / better documentation on order of execution of all the beginplay / onlogin stuff, and although plenty of resources tell me that the player controller exists on the client and server, I’ve yet to find a more detailed explanation of how they interact when you’re a client, since you have technically a local and remote copy of your controller.

OK, I think I solved my own problem by stripping everything down to JUST a player controller, player state, game mode, and game instance. The trick seems to be what order you call everything in, and when you try to access it. I added a “ready” boolean and experimented with a delay / branch loop at different locations. This is what I think is working (at least it now has the correct player names populated in the player state and reports the correct player joining a match).

A few years old post, but I too had this problem and started putting down some breakpoints and realized the order of operation. OnPostLogin executes before the associated Playerstate of the returned PlayerController after loading a new stage.

So from what I’ve experienced the flow was:

  1. OnPostLogin
  2. PlayerController
  3. PlayerState

There are probably more actors/blueprints that may insert in there but I wanted to jot this down for anyone else experiencing this. Have fun!