Map Changes

Why Map Changes Are Different in Fusion

Unreal developers reach for ServerTravel and ClientTravel for map changes. Under Fusion there is no canonical server, so ServerTravel has nothing to anchor on — map authority anchors to the Master Client instead. The Master Client decides the next world for the room; every other peer follows that decision or branches off locally. See Concepts for the Master Client role.

The Master Client picks the next world via UFusionOnlineSubsystem::ChangeWorld. Non-master peers either follow that decision (room-wide travel, replicated through Fusion) or branch off locally via ClientTravel (single-peer travel, not replicated). Never call ServerTravel under Fusion — it has no meaningful target.

Use the Fusion-aware entry points on UFusionOnlineSubsystem and UFusionHelpers instead of UGameplayStatics::OpenLevel or APlayerController::ClientTravel. The Fusion entry points keep the internal FMapInstance and EMapState machinery consistent across peers; the stock entry points bypass it and produce inconsistent room state.

ChangeWorld and ChangeWorldByName

UFusionOnlineSubsystem::ChangeWorld(TSoftObjectPtr<UWorld>, WorldContextObject) is the Master-Client-only call that schedules the next world for the whole room. It takes a soft-object pointer to the target UWorld and replicates the request to every other peer, so they all load the same map.

ChangeWorldByName(FString WorldName, WorldContextObject) is the string-path overload that ChangeWorld funnels into. Both forms transition the local client to EMapState::MasterClientChangeWorld and replicate the request to remote peers.

C++

if (OnlineSubsystem->IsMasterClient())
{
    OnlineSubsystem->ChangeWorld(NextLevel, this);
}

ClientTravel and FusionClientTravel

UFusionOnlineSubsystem::ClientTravel(FString LevelName, WorldContextObject) is the non-master local-travel call. It travels the local client only, without replicating to the room. The method errors and refuses to run when invoked on the Master Client — the Master is expected to use ChangeWorld to move the whole room.

UFusionHelpers::FusionClientTravel(WorldContextObject, FName LevelName, bool bAbsolute, FString Options) is the Blueprint-friendly wrapper. It parses an Unreal URL (with ?Options) and forwards the resolved map name to UFusionOnlineSubsystem::ClientTravel.

C++

UFusionHelpers::FusionClientTravel(
    this, FName("MainMenu"), /*bAbsolute=*/true, FString());

FusionClientTravel is the recommended replacement for APlayerController::ClientTravel in Fusion projects. That includes the path that AGameMode::RestartGame would otherwise take on non-master peers (see Game Classes).

The Map-Load Callback Sequence

Three multicast delegates fire on UFusionOnlineSubsystem in order during a map change:

  • OnMapLoadRequested — a load has been scheduled; the client knows where it is going next.
  • OnMapLoadPerform — fires only when the project opted out of auto-load; the handler is expected to issue the actual map open.
  • OnMapLoadDone — the new world is live and ready for actors to attach.

OnMapLoadPerform is only broadcast when UFusionOnlineSubsystemSettings::LoadMapAutomatically is false, or when Fusion.LoadMapBehaviourOverride forces manual mode (see Level Streaming). In manual mode the client stays in EMapState::IsLoading until the project's handler calls the actual map open — which is the moment for a loading screen, an async asset stream, or any other custom transition.

C++

void UMyGameInstance::Init()
{
    Super::Init();
    UFusionOnlineSubsystem* Subsystem = GetSubsystem<UFusionOnlineSubsystem>();
    Subsystem->OnMapLoadRequested.AddDynamic(this, &UMyGameInstance::HandleMapLoadRequested);
    Subsystem->OnMapLoadPerform.AddDynamic(this, &UMyGameInstance::HandleMapLoadPerform);
    Subsystem->OnMapLoadDone.AddDynamic(this, &UMyGameInstance::HandleMapLoadDone);
}
Order Callback Fires when EMapState while waiting
1OnMapLoadRequestedAlways — a map load has been scheduled.MasterClientChangeWorld
2OnMapLoadPerformOnly in manual mode (LoadMapAutomatically=false or Fusion.LoadMapBehaviourOverride=2). Handler must drive the actual open.IsLoading
3OnMapLoadDoneAlways — the new world is live and ready for actors to attach.Active (new world)

StopFusionSession

UFusionOnlineSubsystem::StopFusionSession has two overloads. The parameterless StopFusionSession() tears down the underlying UFusionClient via Shutdown(). The BP-callable StopFusionSession(WorldContextObject, TSoftObjectPtr<UWorld> ReturnWorld) does the same teardown and then OpenLevels back to a non-networked map.

StopFusionSession() is also the path the async actions take internally. UFusionLeaveRoomAsync, UFusionDisconnectFromPhotonAsync, and UFusionConnectToPhotonAsync all call it when they need to drop the Fusion runtime — there is no separate teardown to call.

Prefer the ReturnWorld overload when leaving multiplayer back to a main menu. Without it, the player stays on the now-detached networked map, which keeps networked actors and components around in a degraded state. With ReturnWorld, the player lands on a clean offline level immediately.

RestartGame Caveats

AGameMode::RestartGame() works under Fusion but only behaves correctly when invoked on the Master Client. Calling it on a non-master peer triggers a local ClientTravel that does not replicate to the rest of the room — the calling peer travels alone and the others see them disappear. See Game Classes for the broader AGameMode story.

The recommended split: the Master Client calls UFusionOnlineSubsystem::ChangeWorld to rotate the whole room. Non-master clients call UFusionHelpers::FusionClientTravel only when they want to leave the room locally — for example, returning to the main menu after a defeat.

Avoid AGameMode::RestartGame() for round-restart flows. Replicate the intent through gameplay state — typically the MatchState on the GameState (see Game Classes) — and let the Master Client drive the actual ChangeWorld from its handler.

Seamless vs Non-Seamless Travel

Fusion detects seamless travel inside UFusionClient::PreMapLoad by checking WorldContext.SeamlessTravelHandler.IsInTransition() and flips an internal bDoingSeamlessTravel flag. Transition maps are filtered out via IsTargetWorld so they do not accidentally become the networked world — the brief transition map between the old and new world is not networked.

Seamless travel on the Master Client must originate from UFusionOnlineSubsystem::ChangeWorld. Calling UWorld::SeamlessTravel directly from any other state path logs an error and refuses to network the result — the Master Client cannot bypass the Fusion-aware entry point and still get room-wide travel.

Enable AGameMode::bUseSeamlessTravel only after confirming the transition map is non-networked (its actors do not carry UFusionActorComponent). Non-seamless is the safer default and matches what the ChangeWorld and ClientTravel code paths exercise most heavily — choose seamless only when the project needs to preserve actor state across travel and is willing to validate the transition map.

Preserving PlayerState Across Travel

Fusion follows Unreal's InactivePlayerArray convention on AGameMode. Disconnected players' APlayerState survives travel the same way it does under stock Unreal networking, preserving carryover state across rejoins within the room's PlayerTTL window. See Game Classes for the APlayerState story.

The rejoin correlation mechanism is governed by Fusion's player-object lifecycle, not by the stock-UE address lookup. The PlayerAttached ownership story in Game Classes covers how a returning player's PlayerState is matched back to its original slot.

Store any travel-critical per-player data on APlayerState, not on the controller or pawn. Controllers and pawns are re-spawned by the new map's GameMode and lose their state across travel. The PlayerState survives in InactivePlayerArray, so anything that must outlive the travel — score, team assignment, persistent flags — lives there.

Back to top