SetupPlayerInputComponent is not fired on listen server host

Hello. I’m new to unreal engine.
I’m trying to run simple project as listen server. This project has custom game mode which spawns pawns for all player controllers:

FString AMyGameMode::InitNewPlayer(APlayerController* NewPlayerController, const TSharedPtr<const FUniqueNetId>& UniqueId, const FString& Options, const FString& Portal)
{
	FString Result = Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);

	UWorld* world = GetWorld();
	AMyGameMode* gameMode = Cast<AMyGameMode>(world->GetAuthGameMode());
	UClass* pawnClass = gameMode->PlayerPawnClass;

	FActorSpawnParameters params;
	params.Owner = NewPlayerController;
	APawn* pawn = Cast<APawn>(world->SpawnActor(pawnClass, 0, 0, params));

	NewPlayerController->Possess(pawn);
	return Result;
}

This pawns is dummy pawns which are used as RTS camera controllers, so they have implementation of SetupPlayerInputComponent with input bindings.

When I’m trying to play in editor - all works fine, all players can control their pawns, but when I package the project and run it in listen mode, listen server host can’t control his pawn, though other players can.
I found that SetupPlayerInputComponent does not fired for host, though InitNewPlayer has fired.

So, why it happend? What I’m doing wrong?

Sorry for my english.


Update: I don’t know where InitNewPlayer is being called.

AMyGameMode.h

#pragma once

#include "MyTypes.h"
#include "GameFramework/GameMode.h"
#include "MyGameMode.generated.h"

/**
 * 
 */
UCLASS(config = Game)
class PROTECTTHE_API AMyGameMode : public AGameMode
{
	GENERATED_BODY()
public:
	AMyGameMode(const FObjectInitializer& ObjectInitializer);

	UPROPERTY(EditAnywhere, Category = "Game Mode")
	TSubclassOf<APawn> PlayerPawnClass;
	
	/** Time before game returns to menu after finish. */
	UPROPERTY(config)
	int32 TimeBeforeReturnToMenu;
			
	// Begin GameMode interface

	/** Initialize the GameState actor. */
	virtual void InitGameState() override;

	virtual FString InitNewPlayer(APlayerController* NewPlayerController, 
									const TSharedPtr<const FUniqueNetId>& UniqueId,
									const FString& Options,
									const FString& Portal) override;
	virtual void PostLogin(APlayerController* NewPlayerController) override;
	/** 
	 * Handle new player, skips pawn spawning. 
	 * @param NewPlayer	
	 */
	virtual void RestartPlayer(AController* NewPlayer) override;
	
	/** 
	 * Modify the damage we want to apply to an actor.
	 * 
	  * @param Damage			The damage
	  * @param DamagedActor		The actor we wish to damage
	  * @param DamageEvent		The event that caused the damage
	  * @param EventInstigator	
	  * @param DamageCauser
	  *
	  * @returns The adjusted damage amount
	  */
	virtual float ModifyDamage(float Damage, AActor* DamagedActor, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) const;

	// End GameMode interface
	
	/** 
	 * Finish the game with selected team as winner.
	 *
	 * @param	InWinningTeam		The team that has won.
	 */
	UFUNCTION(BlueprintCallable, Category = Game)
	void FinishGame(ETeam InWinningTeam);

	void ReturnToMenu();
	
protected:
	/* Helper to return the current gameplay state. */
	EGameplayState				GetGameplayState() const;

	/** Handle for efficient management of UpdateHealth timer */
	FTimerHandle				TimerHandle_ReturnToMenu;
};

AMyGameMode.cpp

#include "MyProject.h"
#include "MyGameMode.h"
#include "MyGameState.h"
#include "Player/MyPlayerController.h"

AMyGameMode::AMyGameMode(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	GameStateClass = AMyGameState::StaticClass();
	PlayerControllerClass = AMyPlayerController::StaticClass();
	
	if ((GEngine != nullptr) && (GEngine->GameViewport != nullptr)) {
		GEngine->GameViewport->SetSuppressTransitionMessage(true);
	}
}

// internal
EGameplayState AMyGameMode::GetGameplayState() const
{
	return GetGameState<AMyGameState>()->GameplayState;
}

void AMyGameMode::InitGameState()
{
	Super::InitGameState();

	AMyGameState* const GameState = GetGameState<AMyGameState>();
	if (GameState)
		GameState->StartGameplayStateMachine();
}

FString AMyGameMode::InitNewPlayer(APlayerController* NewPlayerController, const TSharedPtr<const FUniqueNetId>& UniqueId, const FString& Options, const FString& Portal)
{
	FString Result = Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);

	UWorld* world = GetWorld();
	AMyGameMode* gameMode = Cast<AMyGameMode>(world->GetAuthGameMode());
	UClass* pawnClass = gameMode->PlayerPawnClass;

	FActorSpawnParameters params;
	params.Owner = NewPlayerController;
	APawn* pawn = Cast<APawn>(world->SpawnActor(pawnClass, 0, 0, params));

	NewPlayerController->Possess(pawn);
	return Result;
}

void AMyGameMode::PostLogin(APlayerController* NewPlayerController)
{
	Super::PostLogin(NewPlayerController);
}

void AMyGameMode::RestartPlayer(AController* NewPlayer)
{
	AActor* const StartSpot = FindPlayerStart(NewPlayer);
	if (StartSpot != nullptr) {
		// initialize and start it up
		InitStartSpot(StartSpot, NewPlayer);

		//AMyPlayerController* const NewPC = Cast<AMyPlayerController>(NewPlayer);
		//if (NewPC != nullptr)
		//	NewPC->SetInitialLocationAndRotation(StartSpot->GetActorLocation(), StartSpot->GetActorRotation());
	} else {
		UE_LOG(LogGame, Warning, TEXT("Player start not found, failed to restart player"));
	}
}

float AMyGameMode::ModifyDamage(float Damage, AActor* DamagedActor, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) const
{
	// no health changes after game is finished
	if (GetGameplayState() == EGameplayState::Finished)
		return 0.0f;
	
	return Damage;
}

void AMyGameMode::FinishGame(ETeam InWinningTeam)
{
	AMyGameState* CurrentGameState = GetGameState<AMyGameState>();
	// If the game state is valid and we are still playing
	if ((CurrentGameState != nullptr) && (CurrentGameState->GameplayState != EGameplayState::Finished) ) {
		// tell the gamestate to wrap it up
		CurrentGameState->FinishGame(InWinningTeam);
	}
	
	// Add a timer to return to main if one does not already exist.
	if (GetWorldTimerManager().GetTimerRate(TimerHandle_ReturnToMenu) == -1.0f ) {
		// set timer to return to the main menu
		GetWorldTimerManager().SetTimer(TimerHandle_ReturnToMenu, this, &AMyGameMode::ReturnToMenu, TimeBeforeReturnToMenu, false);
	}
}

void AMyGameMode::ReturnToMenu()
{
	//GetWorld()->ServerTravel(FString("/Game/Maps/StrategyMenu"));
}

Update:

When I move spawning code from InitNewPlayer to PostLogin, the problem has gone. It looks like, that spawn pawns in InitNewPlayer is not a good idea.

FString AMyGameMode::InitNewPlayer(APlayerController* NewPlayerController, const TSharedPtr<const FUniqueNetId>& UniqueId, const FString& Options, const FString& Portal)
{
	FString Result = Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);
	return Result;
}

void AMyGameMode::PostLogin(APlayerController* NewPlayerController)
{
	Super::PostLogin(NewPlayerController);


	UWorld* world = GetWorld();
	AMyGameMode* gameMode = Cast<AMyGameMode>(world->GetAuthGameMode());
	UClass* pawnClass = gameMode->PlayerPawnClass;

	FActorSpawnParameters params;
	params.Owner = NewPlayerController;
	APawn* pawn = Cast<APawn>(world->SpawnActor(pawnClass, 0, 0, params));

	NewPlayerController->Possess(pawn);
}

Hey anaksimandr-

Where is InitNewPlayer being called from? Can you post the full class (header and source) for the custom game mode? Do you notice the same behavior if you use the CreatePlayer/Possess nodes in blueprints to spawn multiple players?

Cheers

Thanks for quick reply. I’m updated the post. I don’t know where InitNewPlayer is being called. And I don’t know how to manually call player creation from blueprints.

The simplest process for creating multiple players in blueprints is to open the level blueprint and add a for loop node set from 0 to total number of players. Then plug the Loop Body pin into a Create Player node. Plug the Return Value pin into a Possess node (which should connect the execution pin automatically). I am still looking over the code you posted however let me know if this helps at all in the mean time.

I had added the graph above to the my level blueprint, but nothing changes.

I’ev updated the post. The problem solved by moving spawning code to the PostLogin function. But I don’t understand why it works this way.