I have a simple solution with a dedicated server and client. If i react to the PostLogin event in gamemode on the server and then directly call the client via a RPC the pawn is not yet controlled by the controller… Why is this not working? Any other way to do this? So far i only found solutions in which you just wait until the pawn is valid, which sounds just bad…
there is a flag you can setup in the game mode to enable spawn or disable spawn of default pawn after post login.
i prefer opening the Game Mode class and looking for the virtual functions you can override
The RestartPlayer in example is needed to override player spawning…
void ASGameMode::RestartPlayer(class AController* NewPlayer)
{
// ...
/* Look for a live player to spawn next to */
FVector SpawnOrigin = FVector::ZeroVector;
FRotator StartRotation = FRotator::ZeroRotator;
for (FConstPawnIterator It = GetWorld()->GetPawnIterator(); It; It++)
{
ASCharacter* MyCharacter = Cast<ASCharacter>(*It);
if (MyCharacter && MyCharacter->IsAlive())
{
/* Get the origin of the first player we can find */
SpawnOrigin = MyCharacter->GetActorLocation();
StartRotation = MyCharacter->GetActorRotation();
break;
}
}
/* No player is alive (yet) - spawn using one of the PlayerStarts */
if (SpawnOrigin == FVector::ZeroVector)
{
Super::RestartPlayer(NewPlayer);
return;
}
/* Get a point on the nav mesh near the other player */
FVector StartLocation = UNavigationSystem::GetRandomPointInRadius(NewPlayer, SpawnOrigin, 250.0f);
// Try to create a pawn to use of the default class for this player
if (NewPlayer->GetPawn() == nullptr && GetDefaultPawnClassForController(NewPlayer) != nullptr)
{
FActorSpawnParameters SpawnInfo;
SpawnInfo.Instigator = Instigator;
APawn* ResultPawn = GetWorld()->SpawnActor<APawn>(GetDefaultPawnClassForController(NewPlayer), StartLocation, StartRotation, SpawnInfo);
if (ResultPawn == nullptr)
{
UE_LOG(LogGameMode, Warning, TEXT("Couldn't spawn Pawn of type %s at %s"), *GetNameSafe(DefaultPawnClass), &StartLocation);
}
NewPlayer->SetPawn(ResultPawn);
}
if (NewPlayer->GetPawn() == nullptr)
{
NewPlayer->FailedToSpawnPawn();
}
else
{
NewPlayer->Possess(NewPlayer->GetPawn());
// If the Pawn is destroyed as part of possession we have to abort
if (NewPlayer->GetPawn() == nullptr)
{
NewPlayer->FailedToSpawnPawn();
}
else
{
// Set initial control rotation to player start's rotation
NewPlayer->ClientSetRotation(NewPlayer->GetPawn()->GetActorRotation(), true);
FRotator NewControllerRot = StartRotation;
NewControllerRot.Roll = 0.f;
NewPlayer->SetControlRotation(NewControllerRot);
SetPlayerDefaults(NewPlayer->GetPawn());
}
}
}
hope that helps…
Checkout this great tutorial… helped me a lot!
After digging through the UE4 source code it seems that pawn is replicated in the controller and is set during post login, while i am using RPC’s… its undefined which is first. What i do now is simply keep a bool in the controller IsClientInitialized… and override
OnRep_Pawn() from the controller, if the postlogin call or onrep_pawn results in a valid pawn i initialize the client and set the boolean to true, so its really only called once. This way i ensure that the pawn on the client is already initialized, but i am also certain it happens with other data valid as well.