Issue with initial posession of pawn

Hey all,

I’ve hear this may be from a bug but at the same time ive had the issue not crop up when other parts of my code are broken.

So heres what im getting.

issue.png

As you can see, the last player isn’t possessing his pawn correctly. I already have a call to restart player in my code path, so im not sure what else could do it, other than on BeginPlay() manually making each player possess their pawns again. But that seems like it would cause other issues.

Here is the code I have for determining which player gets which pawn. Its all down to a few simple functions and overrides in my custom gamemode and player controller.

SideOpPlayerController.h


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/PlayerController.h"
#include "SideOpPlayerController.generated.h"

/**
 * 
 */
UCLASS()
class SIDEOP_API ASideOpPlayerController : public APlayerController
{
	GENERATED_BODY()



protected:

	virtual void SetupInputComponent() override;

	//UFUNCTION(Reliable, Client)
	//void DeterminePawnClass();
	//virtual void DeterminePawnClass_Implementation();

	virtual void BeginPlay() override;

	UFUNCTION(Reliable, Server, WithValidation)
	void ServerRPCSetPawn(TSubclassOf<APawn> InPawnClass);
	virtual void ServerRPCSetPawn_Implementation(TSubclassOf<APawn> InPawnClass);
	virtual bool ServerRPCSetPawn_Validate(TSubclassOf<APawn> InPawnClass);

	UPROPERTY(Replicated)
	TSubclassOf<APawn> PlayerPawn;

	// Pawn for the BluePlayer
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Players)
		TSubclassOf<APawn> BluePlayer;

	// Pawn for the BeigePlayer
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Players)
		TSubclassOf<APawn> BeigePlayer;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Players)
		TSubclassOf<APawn> GreenPlayer;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Players)
		TSubclassOf<APawn> PinkPlayer;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Players)
		TSubclassOf<APawn> YellowPlayer;



public:

	ASideOpPlayerController(const FObjectInitializer& ObjectInitializer);


	void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

	FORCEINLINE UClass* GetPlayerPawnClass(){ return PlayerPawn; }

	UFUNCTION(BlueprintNativeEvent)
	void SetPCPawn();
	void SetPCPawn_Implementation();

};

SideOpPlayerController.cpp




// Fill out your copyright notice in the Description page of Project Settings.

#include "SideOp.h"
#include "SideOpPlayerController.h"
#include "SideOpGameMode.h"


ASideOpPlayerController::ASideOpPlayerController(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	static ConstructorHelpers::FClassFinder<APawn> BluePlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Blue.BP_Character_Blue_C'"));
	static ConstructorHelpers::FClassFinder<APawn> BeigePlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Beige.BP_Character_Beige_C'"));
	static ConstructorHelpers::FClassFinder<APawn> GreenPlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Green.BP_Character_Green_C'"));
	static ConstructorHelpers::FClassFinder<APawn> YellowPlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Yellow.BP_Character_Yellow_C'"));
	static ConstructorHelpers::FClassFinder<APawn> PinkPlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Pink.BP_Character_Pink_C'"));

	// Set the rest of the characters
	if (BluePlayerObject.Class != NULL)
	{
		BluePlayer = BluePlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Blue Pawn Set from control"));
	}

	if (BeigePlayerObject.Class != NULL)
	{
		BeigePlayer = BeigePlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Beige Pawn Set from control"));
	}

	if (GreenPlayerObject.Class != NULL)
	{
		GreenPlayer = GreenPlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Green Pawn Set from control"));
	}

	if (YellowPlayerObject.Class != NULL)
	{
		YellowPlayer = YellowPlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Yellow Pawn Set from control"));
	}

	if (PinkPlayerObject.Class != NULL)
	{
		PinkPlayer = PinkPlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Pink Pawn Set from control"));
	}

	PlayerPawn = BluePlayer; // For whatever reason, this always sets the servers pawn. The rest of the set pawn code does nothing for it.
	// Set to replicate
	bReplicates = true;

}
void ASideOpPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();


}


void ASideOpPlayerController::BeginPlay()
{
	Super::BeginPlay();
	SetPCPawn();
}


bool ASideOpPlayerController::ServerRPCSetPawn_Validate(TSubclassOf<APawn> InPawnClass)
{
	return true;
}

void ASideOpPlayerController::ServerRPCSetPawn_Implementation(TSubclassOf<APawn> InPawnClass)
{

		PlayerPawn = InPawnClass;
		// Only restart the player once for the beginning of the map
		GetWorld()->GetAuthGameMode()->RestartPlayer(this);

}

void ASideOpPlayerController::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(ASideOpPlayerController, PlayerPawn);
}

void ASideOpPlayerController::SetPCPawn_Implementation()
{
	int32 PlayerColor = FMath::RandRange(1 , 4);
	switch (PlayerColor)
	{
	case 1:
		PlayerPawn = BluePlayer;
		ServerRPCSetPawn(PlayerPawn);
		return;
	case 2:
		PlayerPawn = BeigePlayer;
		ServerRPCSetPawn(PlayerPawn);
		return;
	case 3:
		PlayerPawn = GreenPlayer;
		ServerRPCSetPawn(PlayerPawn);
		return;
	case 4:
		PlayerPawn = PinkPlayer;
		ServerRPCSetPawn(PlayerPawn);
		return;
	default:
		PlayerPawn = YellowPlayer;
		ServerRPCSetPawn(PlayerPawn);
		return;
	}
	return;
}


SideOpGameMode.h



// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#pragma once
#include "GameFramework/GameMode.h"
#include "SideOpGameMode.generated.h"

// The GameMode defines the game being played. It governs the game rules, scoring, what actors
// are allowed to exist in this game type, and who may enter the game.
//
// This game mode just sets the default pawn to be the MyCharacter asset, which is a subclass of SideOpCharacter

UCLASS(minimalapi)
class ASideOpGameMode : public AGameMode
{
	GENERATED_BODY()


public:
	ASideOpGameMode(const FObjectInitializer& ObjectInitializer);

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Players)
	TSubclassOf<APawn> BluePlayer;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Players)
	TSubclassOf<APawn> BeigePlayer;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Players)
	TSubclassOf<APawn> GreenPlayer;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Players)
	TSubclassOf<APawn> PinkPlayer;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Players)
	TSubclassOf<APawn> YellowPlayer;

	virtual void BeginPlay() override;
	virtual UClass* GetDefaultPawnClassForController(AController* InController) override;

};

SideOpGameMode.cpp



// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "SideOp.h"
#include "SideOpGameMode.h"
#include "SideOpCharacter.h"
#include "SideOpPlayerController.h"

ASideOpGameMode::ASideOpGameMode(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	static ConstructorHelpers::FClassFinder<APawn> PlayerDefaultPawnObject(TEXT("Pawn'/Game/Blueprints/BP_Character.BP_Character_C'"));
	static ConstructorHelpers::FClassFinder<APawn> BluePlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Blue.BP_Character_Blue_C'"));
	static ConstructorHelpers::FClassFinder<APawn> BeigePlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Beige.BP_Character_Beige_C'"));
	static ConstructorHelpers::FClassFinder<APawn> GreenPlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Green.BP_Character_Green_C'"));
	static ConstructorHelpers::FClassFinder<APawn> YellowPlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Yellow.BP_Character_Yellow_C'"));
	static ConstructorHelpers::FClassFinder<APawn> PinkPlayerObject(TEXT("Pawn'/Game/Players/BP_Character_Pink.BP_Character_Pink_C'"));

	// set default pawn class to our default character
	if (PlayerDefaultPawnObject.Class != NULL /*&& HUDObject.Class != NULL*/)
	{
		DefaultPawnClass = PlayerDefaultPawnObject.Class; 

	}
	
	// Set the rest of the characters
	if (BluePlayerObject.Class != NULL)
	{
		BluePlayer = BluePlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Blue Pawn Set"));
	}

	if (BeigePlayerObject.Class != NULL)
	{
		BeigePlayer = BeigePlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Beige Pawn Set"));
	}

	if (GreenPlayerObject.Class != NULL)
	{
		GreenPlayer = GreenPlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Green Pawn Set"));
	}

	if (YellowPlayerObject.Class != NULL)
	{
		YellowPlayer = YellowPlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Yellow Pawn Set"));
	}

	if (PinkPlayerObject.Class != NULL)
	{
		PinkPlayer = PinkPlayerObject.Class;
		UE_LOG(LogTemp, Warning, TEXT("Pink Pawn Set"));
	}

	
}

void ASideOpGameMode::BeginPlay()
{
	Super::BeginPlay();
}



UClass* ASideOpGameMode::GetDefaultPawnClassForController(AController* InController)
{
	Super::GetDefaultPawnClassForController(InController);
	ASideOpPlayerController* PC = Cast<ASideOpPlayerController>(InController);
	TSubclassOf<APawn> ClassToUse = nullptr;
	if (PC != nullptr)
	{
		ClassToUse = PC->GetPlayerPawnClass();
		return ClassToUse;
	}
	else
	{
		return DefaultPawnClass;
	}
} 

I know I need to setup better code security with auth calls and whatnot, but this is just a prototype at the moment.

Id prefer not to have to pull the 4.8 source and compile right now. If it was closer to release, I’d feel more comfortable, but I feel it would bring other issues around.

For now, I figured I would ask if anyone has found any workarounds, or if there is a bug in my code you see, or can suggest where to go, that would be amazing.

Cheers.

EDIT: For whatever reason, my code also now sometimes spawns two pawns for a player. But not always.