How to replace AI with a real player?

I’m currently making a card game that has 4 players. If there is only 1 client, then there will be 3 AI players. I have that much working already, but what if another player joins the game? How can I tell when a new client joins and how can I correctly place the new client into the game?

This can be done in the GameMode with the PreLogin and PostLogin functions. I check if the AI is controlled via IsControlled(). If it isn’t then a human player is free to take it. AIControllers are spawned automatically, so it seems like IsControlled() should always return true, but it appears to work regardless. I suppose if you want to be double sure, you can make your own AIController and PlayerController and test which one is controlling the pawn. Here is my current setup:

 void AMyGameMode::PreLogin(const FString & Options, const FString & Address, const TSharedPtr<class FUniqueNetId>& UniqueId, FString& ErrorMessage)
 {
      GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Client connected"));

      if (IsPawnAvailable())
      {
           Super::PreLogin(Options, Address, UniqueId, ErrorMessage);
      }
      else
      {
           //setting the error message will disconnect the player
           ErrorMessage = TEXT("Game is full.  Please try another.");
      }
 }

 void AMyGameMode::PostLogin(APlayerController* NewPlayer)
 {
      Super::PostLogin(NewPlayer);
      ASPawn* FreePawn = GetFreePawn();
      NewPlayer->Possess(FreePawn);

      // probably redundant
      FreePawn->SetOwner(NewPlayer);

      ConnectedClients++;

      // at least 1 connected client before the game starts
      if (ConnectedClients == 1)
      {
           PreGameSetup();
      }
 }

 ASPawn* AMyGameMode::GetFreePawn()
 {
      for (TActorIterator<AAIPlayer> ActorItr(GetWorld()); ActorItr; ++ActorItr)
      {
           if (!ActorItr->IsControlled())
           {
                return *ActorItr;
           }
      }
      return NULL;
 }

 bool AMyGameMode::IsPawnAvailable()
 {
      for (TActorIterator<AAIPlayer> ActorItr(GetWorld()); ActorItr; ++ActorItr)
      {
           if (!ActorItr->IsControlled())
           {
                return true;
           }
      }
      return false;
 }

Possessing pawns:

More ways to connect players: