[UE4]Networking in Basic - Travelling in Multiplayer
Keywords: UE4, Networking, Dedicated Server,ClientTravel, ServerTravel, UEngine::Browse
Notices
Difference between ServerTravel and ClientTravel
- ServerTravel also informs the clients to move along with the server.
- ClientTravel will be called locally for the client to load a new map. or connect to ip.
- Browse can load local map via LoadMap ( not for multiplayer ) but there are more things happening in this method. I also want to know more about what it does and what it should be used for.
- LoadMap should also load a map for a local client
Browse, LoadMap, ServerTravel and ClientTravel?
https://answers.unrealengine.com/questions/122565/browse-servertravel-and-clienttravel.html
What’s the different between absolute travel and relative travel?
https://answers.unrealengine.com/questions/101284/what-the-different-between-absolute-travel-and-rel.html
What is the difference between ServerTravel and OpenLevel?
https://answers.unrealengine.com/questions/55477/the-difference-of-the-two-functions.html
PlayerController->ClientTravel() (Connect to remote server)
https://answers.unrealengine.com/questions/29017/gamemode-multiplayer-c.html
Seamless and non-seamless travel
https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Travelling
Cases
How to pass arguments from Client to Server
When execute PlayerController->ClientTravel
, default URL is like this:
FString Address = TEXT("127.0.0.1:7777");
If want to pass arguments to Server when logining, format URL like this:
FString Address = FString::Printf(TEXT("127.0.0.1:7777?Param1=%s?Param2=%s"), *Param1, *Param2);
How to parse arguments sent by Client on Server
Overrice function InitNewPlayer()
in GameMode
, then parse arguments like this:
FString AMyGameMode::InitNewPlayer(APlayerController* NewPlayerController, const FUniqueNetIdRepl& UniqueId, const FString& Options, const FString& Portal = TEXT(""))
{
FString Param1 = UGameplayStatics::ParseOption(Options, TEXT("Param1"));
FString Param2 = UGameplayStatics::ParseOption(Options, TEXT("Param2"));
}
Official Doc: Passing Arguments To Server During Connection
https://wiki.unrealengine.com/Passing_Arguments_To_Server_During_Connection
How does Server informs Clients which are connected to Server to travel map
Execute ClientTravel
on Server:
void AMyGameModeBase::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
FString URL = TEXT("/Game/Maps/TestMap");
NewPlayer->ClientTravel(URL, TRAVEL_Absolute);
}
This way works right for travelling to new map, but Client would also disconnect to Server. It means that the NetMode
of all Actors in Client would be set as NM_Standalone
.
How to use ServerTravel
A common use case is starting a server in a lobby level. Clients connect to this lobby level and choose loadout and character, for example. When ready, the server triggers a ServerTravel, which transitions all clients into the main game level.
When ServerTravel is triggered, the server tells all clients to begin to ClientTravel to the map specified. If the ServerTravel is seamless then the client maintains its connection to the server. If it’s not seamless then all the clients disconnect from the server and reconnect once they have loaded the map. Internally, the server does a similar process: it loads in the new level, usually a game world for all the clients to play on, and begins accepting player spawn requests once ready.
Reference: Map travel
https://docs.improbable.io/unreal/alpha/content/map-travel#in-native-unreal-1
Enabling Seamless Travel
To enable seamless travel, you need to setup a transition map. This is configured through the UGameMapsSettings::TransitionMap property. By default this property is empty, and if your game leaves this property empty, an empty map will be created for the transition map.
The reason the transition map exists, is that there must always be a world loaded (which holds the map), so we can’t free the old map before loading the new one. Since maps can be very large, it would be a bad idea to have the old and new map in memory at the same time, so this is where the transition map comes in.
So now we can travel from the current map to the transition map, and then from there we can travel to the final map. Since the transition map is very small, it doesn’t add much extra overhead while it overlaps the current and final map.
Once you have the transition map setup, you set AGameModeBase::bUseSeamlessTravel
to true, and from there seamless travel should work!
Origin:
https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/Networking/Travelling/
API Related
ClientTravel
See the usage of APlayerController->ClientTravel()
in offical example Shooter Game.
Seamless travel
After seamless travel AGameModeBase::GenericPlayerInitialization(AController* Controller)
and AGameModeBase::HandleStartingNewPlayer()
, Gets called for the client.
Travelling API in GameMode
ClientTravel:
APlayerController* AGameModeBase::ProcessClientTravel(FString& FURL, FGuid NextMapGuid, bool bSeamless, bool bAbsolute)
{
// We call PreClientTravel directly on any local PlayerPawns (ie listen server)
APlayerController* LocalPlayerController = nullptr;
for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
{
if (APlayerController* PlayerController = Iterator->Get())
{
if (Cast<UNetConnection>(PlayerController->Player) != nullptr)
{
// Remote player
PlayerController->ClientTravel(FURL, TRAVEL_Relative, bSeamless, NextMapGuid);
}
else
{
// Local player
LocalPlayerController = PlayerController;
PlayerController->PreClientTravel(FURL, bAbsolute ? TRAVEL_Absolute : TRAVEL_Relative, bSeamless);
}
}
}
return LocalPlayerController;
}
ServerTravel:
void AGameModeBase::ProcessServerTravel(const FString& URL, bool bAbsolute)
{
#if WITH_SERVER_CODE
StartToLeaveMap();
// Force an old style load screen if the server has been up for a long time so that TimeSeconds doesn't overflow and break everything
bool bSeamless = (bUseSeamlessTravel && GetWorld()->TimeSeconds < 172800.0f); // 172800 seconds == 48 hours
FString NextMap;
if (URL.ToUpper().Contains(TEXT("?RESTART")))
{
NextMap = UWorld::RemovePIEPrefix(GetOutermost()->GetName());
}
else
{
int32 OptionStart = URL.Find(TEXT("?"));
if (OptionStart == INDEX_NONE)
{
NextMap = URL;
}
else
{
NextMap = URL.Left(OptionStart);
}
}
FGuid NextMapGuid = UEngine::GetPackageGuid(FName(*NextMap), GetWorld()->IsPlayInEditor());
// Notify clients we're switching level and give them time to receive.
FString URLMod = URL;
APlayerController* LocalPlayer = ProcessClientTravel(URLMod, NextMapGuid, bSeamless, bAbsolute);
UE_LOG(LogGameMode, Log, TEXT("ProcessServerTravel: %s"), *URL);
UWorld* World = GetWorld();
check(World);
World->NextURL = URL;
ENetMode NetMode = GetNetMode();
if (bSeamless)
{
World->SeamlessTravel(World->NextURL, bAbsolute);
World->NextURL = TEXT("");
}
// Switch immediately if not networking.
else if (NetMode != NM_DedicatedServer && NetMode != NM_ListenServer)
{
World->NextSwitchCountdown = 0.0f;
}
#endif // WITH_SERVER_CODE
}
Reference
Official Docs
Travelling in Multiplayer
https://docs.unrealengine.com/en-US/Gameplay/Networking/Travelling/index.html
山舞银蛇,原驰蜡象,欲与天公试比高。须晴日,看红装素裹,分外妖娆。----毛泽东《沁园春·雪》