Multiplayer: Getting the pawn from the player (C++)

Hey,
I have a server that sends a PlayerId to a client and I need the client to get the player, like this:
(Now this below works, I was wondering if there already was something like this built in or a faster way to do it.)

int32 PlayerID = AMyCharacter->PlayerState->PlayerId

AMyCharacter *GetPlayer(int32 PlayerID)
{
    for (auto Actor : GetWorld()->NetworkActors)
	{
		AMyCharacter *Player = Cast<AMyCharacter>(Actor);
		if (Player && Player->PlayerState->PlayerId == PlayerID)
		   return Player;
	}
}

I figured I could make a TArray PlayerList in gamestate but I don’t want to duplicate things that already exist in UE4

Please suggest something.
Thank you for your time :slight_smile:

The world object has a PlayerControllerIterator that should be consistent across all clients. You can iterate over that if you want.

I’m sorry for the delayed reply (computer problems ;P)

I assume that you mean this:

for (auto ControllerIt = GetWorld()->GetPlayerControllerIterator();
		ControllerIt; ++ControllerIt)
	{
		APlayerController *Controller = *ControllerIt;

		if (Controller->PlayerState->PlayerId == PlayerID)
		{
			return Cast<AMyCharacter>(Controller->GetPawn());
		}
	}

It never finds the player (doesn’t work)

So far only iterating NetworkActors has worked for me.
Here are some other ways I tried:

This also “works” but it gets a copy (server & client has the same player pointer)

TArray<APlayerController *> PlayerList;
GEngine->GetAllLocalPlayerControllers(PlayerList);

for (auto Controller : PlayerList)
{
	if (Controller->PlayerState->PlayerId == PlayerID)
	{
		APawn *Pawn = Controller->GetPawn();
		return Cast<AMyCharacter>(Pawn);
	}
}

This finds the player but Controller is always NULL

(In game state)
    for (auto PlayerState : PlayerArray)
    {
    	if (PlayerState->PlayerId == PlayerID)
    	{
    		AController *Controller = PlayerState->GetInstigatorController();
    
    		return Cast<AMyCharacter>(Controller->GetPawn());
    	}
    }

I ended up with this solution:

 AMyCharacter *GetPlayer(int32 PlayerID)
    {
         for (auto PlayerState : PlayerArray)
    	{
    		if (PlayerState->PlayerId == PlayerID)
    		{
    			AMyPlayerState*PS = Cast<AMyPlayerState>(PlayerState);
    			if (!PS)
    				return NULL;
    
    #ifdef WITH_SERVER_CODE
    			if (Role == ROLE_Authority)
    			{
    				APlayerController *PC = PS->ParentController.Get(false);
    				if (!PC)
    					return NULL;
    				return Cast<AMyCharacter>(PC->GetPawn());
    			}
    #endif
    
    #if !UE_SERVER
    			return PS->ParentPlayer.Get(false);
    #endif
    			return NULL;
    		}
    	}
    }

(MyPlayerState.h)
public:
#ifdef WITH_SERVER_CODE
	TWeakObjectPtr<APlayerController> ParentController;
#endif

#if !UE_SERVER
	TWeakObjectPtr<class AMyCharacter> ParentPlayer;
#endif
};

(MyGameMode.cpp)
void AMyGameMode::StartNewPlayer(APlayerController* NewPlayer)
{
	Super::StartNewPlayer(NewPlayer); // Remember this, if forgotten HUD is disabled ;P

	AMyPlayerState *PS = Cast<AMyPlayerState>(NewPlayer->PlayerState);
	if (PS)
		PS->ParentController = NewPlayer;
}

(MyCharacter.cpp)
    void AMyCharacter::OnRep_PlayerState()
    {
    	Super::OnRep_PlayerState();
    
    #if !UE_SERVER
    	AMyPlayerState *PS = Cast<AMyPlayerState>(PlayerState);
    	if (PS)
    		PS->ParentPlayer = this;
    #endif
    }

Interesting solution! Really a shame that something like this isn’t built in… it seems strange there there is no easy way to go from a PlayerState to the possessed pawn. I wonder if this is because we are using the PlayerState in a way it wasn’t intended? I have a similar question here.