PlayerController not changing with level during Seamless Travel

In my game, players severtravel (with seamless travel) back and forth between a lobby level and a game level. In the lobby level blueprint’s BeginPlay, I have some stuff going on that ties some items in the world to the local player controller. This is activated when I first enter the lobby level, (of note–this first travel, from the menu to the lobby, is not Seamless Travel) but when we go through to the game and then back to the lobby afterward (both trips using Seamless Travel) the same BeginPlay logic doesn’t execute. In a previous build of this game that didn’t use Seamless Travel at all, this exact setup worked fine. (I’ve been moving the build to Steam, and for some reason hard travel kicks clients there, but Seamless Travel works, so I’m switching).

So my question: Does Seamless Travel never hit the BeginPlay event? Or does it hit it once, then keep the level around somewhere in memory, in “Play”, while I’m in the game, so when we return we don’t hit Begin Play again? If I want an event that fires every time players re-enter the level, what should I look to?

EDIT:

With some additional testing and research, I’ve determined BeginPlay is in fact being hit every time I seamless travel back to the lobby. However, the logic I need to execute there is on my player controller class for the lobby level, LobbyController. I have a separate player controller class for the game level, GameController. Since I’m doing seamless travel, my players are still GameControllers when they get back to the lobby, so a cast to LobbyController is failing. Is it possible to insist on throwing out and getting a new player controller even in seamless travel?

An interesting wrinkle: players hard clienttravel from their menus to the lobby, then seamless travel together to the game. So they ARE LobbyControllers the first time through. Why then do their player controllers “refresh”
to become GameControllers when we enter the game level? It’s identical seamless travel to the call taking everyone back to the lobby–when the player controller won’t change from GameController back to LobbyController.

2ND EDIT:

With further testing, I find that I’m not hitting GameMode::PostSeamlessTravel and HandleSeamlessTravelPlayer for each player, so they’re never hitting SwapPlayerController, which would otherwise set up the new Player Controller class. If I play single player, and seamless travel from lobby to game to lobby, I hit these functions as expected, and get the desired BeginPlay results I’m looking for because I have a LobbyController when I need one. But if I play with two players, I’m not even hitting PostSeamlessTravel. Why wouldn’t I hit that with multiple players?

I noticed the call to PostSeamlessTravel is wrapped in a couple if statements:

      if (bSwitchedToDefaultMap)
		{
			// we've now switched to the final destination, so we're done
			
			// remember the last used URL
			CurrentContext.LastURL = PendingTravelURL;

			// Flag our transition as completed before we call PostSeamlessTravel.  This 
			// allows for chaining of maps.

			bTransitionInProgress = false;
			UE_LOG(LogWorld, Log, TEXT("----SeamlessTravel finished------") );

			AGameMode* const GameMode = LoadedWorld->GetAuthGameMode();
			if (GameMode)
			{
				UNavigationSystem::InitializeForWorld(LoadedWorld, NavigationSystem::GameMode);

				// inform the new GameMode so it can handle players that persisted
				GameMode->PostSeamlessTravel();					
			}

			// Called after post seamless travel to make sure players are setup correctly first
			LoadedWorld->BeginPlay();

			FCoreDelegates::PostLoadMap.Broadcast();
		}

I’m currently building a Debug_Game exe to find out which if statement isn’t hitting, and will update with more when I do.

We’re having the same issue over here. Any luck?

Sorry for not updating at the time. I was very busy. :slight_smile: But if memory serves, it was the GameMode check on line 15 of that snippet that was not succeeding, presumably because the game mode was not ready yet. If I remember correctly, this is a problem because my desired effect was taking place in the level blueprint’s BeginPlay, and the timing on that is tight–the level’s BeginPlay isn’t guaranteed to wait until all player controllers or even the server player controller is converted. At least I assume, because it wasn’t. :slight_smile:

Let’s say the behavior I wanted in the level blueprint’s BeginPlay was to call a function Func(). Initially I had the BeginPlay event node feed right into a Func node. That’s when I was having the problem.

My solution was to wrap the function call in a delay loop.

If the cast to my controller class fails, I delay and try again. Probably you want some default case to take over after a few failed tries. For us, it always succeeded the second time around.

Hope that helps!