"Persistant" actors after seamless travel?

Hi everyone,

I’m currently working on a client/server based project (using blueprint), and I’m trying to restart the match after it’s over. In my game, I need to transfer information from the last match to the new one.
I read in the documentation that after a seamless travel on the server, some actors “persist”. Here’s the list:

My question is: what does exactly “persist” mean? Because if I check on the server the variables in my GameMode (which is supposed to be “persistant”) after the seamless travel, everything is reset to the default value. Same goes for the PlayerControllers. Is that normal, or should it not be erased?

If that’s normal, then how can I transfer information from the last match through seamless travel (server side)?

Thanks for answering and please forgive my possibly not so perfect english! :slight_smile:

Hey Syalen, I have the same question as yours. Did you find your answer anywhere?

Hey
Hum, actually I just avoided the problem by asking the clients after the restart for the information I needed (it was about cosmetic things). So I don’t have a real solution for this, but maybe you can simply pass arguments through the OpenLevel node, instead of using the RestartLevel one. I didn’t try, but it should work. Sadly, it can only be useful if you want to pass integers, floats or other simple variables, but not a whole actor.
And by the way, I switched to non-seamless travel, so the clients are disconnected and reconnected to the server each time you restart it, this way I can catch when a player connects, just the same way as when the server starts for the first time.

Hope I helped you!

Me too. And only find some miss leading or not good explained information. Would be nice if someone can explain why UE4 documentations is speaking of persistent GM and possible persistent actors on seamless travel, but on server travel by using seamless travel GM get’s reset. Thanks a lot!

https://forums.unrealengine.com/development-discussion/c-gameplay-programming/1775847-preserving-data-on-seamless-travel
https://forums.unrealengine.com/development-discussion/blueprint-visual-scripting/102335-gamemode-variable-empty-after-seamless-travel

Basically the actors/objects aren’t destroyed/recreated. Their actor/object persists.

For persistent data look at Game Instance. UGameInstance | Unreal Engine Documentation

It’s good for storing some data. If you need a more indepth long term approach look at GameSave. Save Game | Unreal Engine Documentation

Just as an FYI Game States are unique between levels. Each level has it’s own GS, so when you switch levels a new GS is created.

Thanks for the answer, but that’s not what official documentation says. (At least as I understand it)
https://docs.unrealengine.com/en-US/…ing/index.html

I know GameInstance and have a good understanding of most concept and class types of UE4.
ButI still struggle to understand the documentation text for seamless travel.

There is nothing written, that all data in actros gets wiped or resetted. And when they talk from “persistent” and someone read this, of course he will think that data in the object persists also. I find nothing on the page telling the opposite.
https://docs.unrealengine.com/en-US/…ing/index.html
Or perhaps I simply do not find it.

And when documentation says that “All Controllers that have a valid PlayerState” on server side “are persistent”, then it’s sounds like also data data will be. Also here … at least as my understanding of the text is telling me.
Especially because documentation also cleary says this “When using seamless travel, it’s possible to carry over (persist) actors from the current level to the new one. This is useful for certain actors, like inventory items, players, etc.
So, it seems to be the “easy and preferred” way to take “playerstate” and other things with you when servertravel into new level, **instead **of GameInstance.

So, I am still curios how its working and why every data get nuked on seamless travel and how to prevent this. E.g. how to take full PlayerState with you on seamless Servertravel, with all data in it of course.

I’m not a good blueprint user so may be wrong, but I think you can’t do that you want with blueprints.

By default only PlayerController, PlayerState, PlayerCameraManager and HUD actors will be seamless traveled to new level (that’s not entirely true, but more on that below), other actors from that documentation is persist only for the transitions map and will be destroyed with it.


That’s what really happened.

GameMode and PlayerContoller has function GetSeamlessTravelActorList.

void AGameModeBase::GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList)
{
	// Get allocations for the elements we're going to add handled in one go
	const int32 ActorsToAddCount = GameState->PlayerArray.Num() + (bToTransition ? 3 : 0);
	ActorList.Reserve(ActorsToAddCount);

	// Always keep PlayerStates, so that after we restart we can keep players on the same team, etc
	ActorList.Append(GameState->PlayerArray);

	if (bToTransition)
	{
		// Keep ourselves until we transition to the final destination
		ActorList.Add(this);
		// Keep general game state until we transition to the final destination
		ActorList.Add(GameState);
		// Keep the game session state until we transition to the final destination
		ActorList.Add(GameSession);

		// If adding in this section best to increase the literal above for the ActorsToAddCount
	}
}

and

void APlayerController::GetSeamlessTravelActorList(bool bToEntry, TArray<AActor*>& ActorList)
{
	if (MyHUD != NULL)
	{
		ActorList.Add(MyHUD);
	}

	// Should player camera persist or just be recreated?  (clients have to recreate on host)
	ActorList.Add(PlayerCameraManager);
}

These functions calls for GameMode and each PlayerController and outs array of actors that has to be persist. It’s called twice for OldMap->TransitionMap and for TransitionMap->NewMap travels. bToTransition parameter means is it OldMap->TransitionMap(true) or TransitionMap->NewMap(false) travel. So if you want to make an actor persistent you have to override one of these functions and add that actor to out ActorList.


Now about some issues.

Above I said that PlayerController and PlayerState will be traveled to the new map.

For PlayerController it’s true only if the new map has the same player controller class in it’s GameMode settings. In case when it’s not will be created a new PlayerController instance and data from old controller will be transfer with SeamlessTravelFrom and SeamlessTravelTo functions inside PlayerController:

void APlayerController::SeamlessTravelTo(APlayerController* NewPC)
{
	CleanUpAudioComponents();
}

and

void APlayerController::SeamlessTravelFrom(APlayerController* OldPC)
{
	// copy PlayerState data
	if (OldPC->PlayerState)
	{
		OldPC->PlayerState->Reset();
		OldPC->PlayerState->SeamlessTravelTo(PlayerState);
		OldPC->PlayerState->Destroy();
		OldPC->PlayerState = NULL;
	}

	// Copy seamless travel state
	SeamlessTravelCount = OldPC->SeamlessTravelCount;
	LastCompletedSeamlessTravelCount = OldPC->LastCompletedSeamlessTravelCount;
}

For PlayerState will always be created a new instance and data from old one will be transfer with SeamlessTravelTo function inside PlayerState:

void APlayerState::SeamlessTravelTo(APlayerState* NewPlayerState)
{
	DispatchCopyProperties(NewPlayerState);
	NewPlayerState->SetIsOnlyASpectator(IsOnlyASpectator());
}

and this function will eventually call CopyProperties:

void APlayerState::CopyProperties(APlayerState* PlayerState)
{
	PlayerState->SetScore(GetScore());
	PlayerState->SetCompressedPing(GetCompressedPing());
	PlayerState->ExactPing = ExactPing;
	PlayerState->SetPlayerId(GetPlayerId());
	PlayerState->SetUniqueId(GetUniqueId());
	PlayerState->SetPlayerNameInternal(GetPlayerName());
	PlayerState->SetStartTime(GetStartTime());
	PlayerState->SavedNetworkAddress = SavedNetworkAddress;
}

So to seamless travel some data you can override this functions.


And do not try to make persist a GameMode. It doesn’t work, and even if will that’s anyway bad because each map has own GameMode class, and doing that way may provide some unexpected bugs and inconveniences. If I need to have some global data I’d like to use GameInstanceSubsystem.

2 Likes