Players don't see the updated PlayerState->PlayerName of the other players

Hi!

I’ve just started to develop my first Multiplayer game. There’ll be two players with their names above of their heads. I’m using a Blueprint Widget to show it.

When the game begins, the player has to set his/her name. I have a Blueprint widget with an Editable Text. This is how I show this blueprint widget:

When the player hits the Enter key in the Editable Text:

I have created my custom PlayerState in C++.

The header:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "OpenDoorPlayerState.generated.h"

/**
 * 
 */
UCLASS()
class MULTIPLAYERTEST_API AOpenDoorPlayerState : public APlayerState
{
	GENERATED_BODY()

protected:
	virtual void OnRep_PlayerName() override;

	UFUNCTION(BlueprintCallable)
	virtual void SetPlayerName(const FString& S) override;
	
};

The code:

#include "OpenDoorPlayerState.h"
#include "Kismet/GameplayStatics.h"
#include "MultiplayerCharacter.h"


void AOpenDoorPlayerState::OnRep_PlayerName()
{
	Super::OnRep_PlayerName();

	AMultiplayerCharacter* PlayerCharacter =
		Cast<AMultiplayerCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0));

	if (PlayerCharacter)
	{
		UE_LOG(LogTemp, Warning, TEXT("[ AOpenDoorPlayerState::OnRep_PlayerName] Player Character Name: %s - PlayerName: %s"), *PlayerCharacter->GetName(), *GetPlayerName());
		PlayerCharacter->ShowPlayersName(GetPlayerName());
	}

}

void AOpenDoorPlayerState::SetPlayerName(const FString& S)
{
	Super::SetPlayerName(S);

	ENetMode NetMode = GetNetMode();

	UE_LOG(LogTemp, Warning, TEXT("[ AOpenDoorPlayerState::SetPlayerName ] %s"), *S);
}

The ShowPlayersName is:

UFUNCTION(BlueprintImplementableEvent)
void ShowPlayersName(const FString& PlayerName);

In blueprints:

The problem is: if updates the blueprint widget in the client, but the player running as a listen server it doesn’t. If I change it in the listen server, the client doesn’t see the updated name.

When the game starts:

Output log:

LogNet: Join request: /Game/MultiplayerTest/Maps/T_BlockoutMap?Name=Melnibone-20B6B133447A6510DA00D09D26FD93A7?SplitscreenCount=1

LogOnlineSession: Warning: OSS: No game present to join for session (GameSession)

LogTemp: Warning: [ AOpenDoorPlayerState::OnRep_PlayerName] Player Character Name: BP_MultiplayerCharacter_C_0 - PlayerName: Melnibone-20B6B13344

LogTemp: Warning: [ AOpenDoorPlayerState::SetPlayerName ] Melnibone-20B6B13344

LogNet: Join succeeded: Melnibone-20B6B13344

LogOnlineSession: Warning: OSS: No game present to join for session (GameSession)

LogTemp: Warning: [ AOpenDoorPlayerState::OnRep_PlayerName] Player Character Name: BP_MultiplayerCharacter_C_0 - PlayerName: Melnibone-7894D3E242

LogOnlineSession: Warning: OSS: No game present to join for session (GameSession)

LogTemp: Warning: [ AOpenDoorPlayerState::OnRep_PlayerName] Player Character Name: BP_MultiplayerCharacter_C_0 - PlayerName: Melnibone-20B6B13344

After setting the name on the listen server:

Output log:>

LogTemp: Warning: [ AOpenDoorPlayerState::OnRep_PlayerName] Player Character Name: BP_MultiplayerCharacter_C_0 - PlayerName: NameListenServer

LogTemp: Warning: [ AOpenDoorPlayerState::SetPlayerName ] NameListenServer

LogTemp: Warning: [ AOpenDoorPlayerState::OnRep_PlayerName] Player Character Name: BP_MultiplayerCharacter_C_0 - PlayerName: NameListenServer

After setting the name on the client:

Output log:

LogTemp: Warning: [ AOpenDoorPlayerState::OnRep_PlayerName] Player Character Name: BP_MultiplayerCharacter_C_0 - PlayerName: NameClient

LogTemp: Warning: [ AOpenDoorPlayerState::SetPlayerName ] NameClient

LogTemp: Warning: [ AOpenDoorPlayerState::OnRep_PlayerName] Player Character Name: BP_MultiplayerCharacter_C_0 - PlayerName: NameClient

Do you know what it is happening?

Thanks!

Coming from Unreal Slackers after I already linked you the answer you were looking for.

Sorry, but I don’t understand the answer you have linked. I’m going to clarify me:

I don’t need to present some initial data on the UI.

I’m testing because this is the first time I do this. So, if you see the name Elric or the computer name it is because it is testing code.

The problem is that I have change PlayerState->PlayerName on one player and the other player doesn’t see the new name.

I have tried with APawn::OnRep_PlayerState() and it only fires before the input name blueprint widget appears. I don’t see this event fires again. Never.

Thanks!

Right, APawn::OnRep_PlayerState() handles the first two cases out of following three:

  1. Showing correct name to all players on receiving the initial packet (setting name while spawning player Pawn and PlayerState).
  2. Showing correct name when a player Pawn enters the relevancy range of another player Pawn.
  3. Showing correct name on runtime from a user input text.

Case 3 is handled if you set PlayerState.PlayerName on server and listen for APlayerState::OnRep_PlayerName() get called.

My notes:

  1. Don’t use GetPlayerXXX(0) nodes in multiplayer. There are always other ways to get what you want. For example, in widget instead of GetPlayerController(0) use GetOwningPlayer. In C++ they go by this version: UGameplayStatics::GetPlayerXXX(this, 0), which you used in AOpenDoorPlayerState::OnRep_PlayerName(), and instead you should have used APlayerState::GetPawn().
  2. You are firing a server RPC in BeginPlay of PlayerController. There are two issues with this one. The first issue is that server RPCs should be called from client for them to be meaningful. BeginPlay fires on both server and client versions of PlayerController. How do we split the two execution paths then? The usual way is to use SwitchHasAuthority node (HasAuthority() in C++) with Authority pin being server and Remote pin being client (that is usually correct for replicated actors and nothing else). So, is the solution to use it with Remote pin then? The answer is obviously not. You don’t want to deprive the listen-server player which is always Authority from calling that precious function. The other issue is that it could be that the PlayerController isn’t yet ready to start calling RPCs. As the docs suggest, the first safe point to start calling RPCs on PlayerController is AGameModeBase::PostLogin(). From there you should make sure to call a client RPC that adds a widget to the player screen making them input their name.
  3. You are calling a server RPC to then fire a client RPC, which is nonsensical due to the fact that if you wanted access to the client version from BeginPlay it’s as easy as using SwitchHasAuthority with Remote pin like noted in 2. However, as also noted if you do that then the server player (host) wouldn’t be able to see that widget as it’s always Authority (has no RemoteRole). Then what should you do? Answer is to use a client RPC from PostLogin and any consecutive events like noted in 2 too. Reason being, widgets are local, i.e. exist at the local controller level. In order to access the local controller level from server (GameMode actor) we have to fire a client RPC.

I highly suggest you read and understand the multiplayer compendium, along with anything that seems starter friendly in pinned messages in Unreal Slackers’ #multiplayer channel.

1 Like