APlayerController::IsLocalController is changing state

The issue happens if a game loads a map as a listen server and then transitions to a map without multiplayer net mode, e.g. Standalone. During the transition all connected player’s controller of remote players on the server might set flag bIsLocalPlayerController to true from APlayerController::IsLocalController, which change the state to be “local controllers” on the server.

Steps to Reproduce

  1. Load a map as a listen server.
  2. Have another player connect to you.
  3. On the server transition to a map with net mode Standalone.
  4. Somewhere in code call IsLocalController on a client player controller on the server during transition (e.g. EndPlay of a player controller component, etc.).
  5. See that APlayerController::IsLocalController will set the flag bIsLocalPlayerController to true, which changes the state of a controller to be local, even if it belongs to a client.

Hi,

I haven’t been able to reproduce this in my own test project, so in order to get a better idea of the problem, I have a couple of questions.

First, how are you travelling to the next level? Is this being done with something like the “open <Level Name>” command or some other function?

Next, are you doing anything to affect the Net Mode? In my own test project, I’m still seeing the Net Mode as NM_ListenServer when EndPlay is called for the the player controllers.

Any other relevant information you could provide would be appreciated.

Thanks,

Alex

Hi Alex,

Thank you for a very quick response and sorry to not following up with you for such a long time.

Let me described in details what are we doing to encounter the issue:

  1. We are start our game in main menu map, which we load on all peers with net mode NM_Standalone.
  2. Then we have a multiplayer mode option in the menu, where there is an option to create a new session or join an existing one.
  3. First a player would create a new online session and transition to a new lobby map by World->ServerTravel(TravelDestination, true); We are making peer-to-peer game, so we will be loading into a lobby as NM_ListenServer, by having “?listen” in the TravelDestination string argument.
  4. Then another player would join the lobby by calling PlayerController->ClientTravel(TravelURL, TRAVEL_Absolute);
  5. After everyone is ready to go in the lobby, the host we call World->ServerTravel(TravelDestination, true); again to transit to a game map, which would cause to all connected clients to travel with the server.
  6. Then during the match a server would call UGameplayStatics::OpenLevel(this, FName(UGameMapsSettings::GetGameDefaultMap()), true); to go back to the main menu, which will force all connected clients to follow back as well. And during this transition we encounter the issue, where on a server instance, the call to APlyaerController::IsLocalController will change the bIsLocalPlayerController to true for a remote player controller. Then if there is a logic on one of the actors, created during the gameplay, in which we check all player controllers on the server for IsLocalController, it will return false results.

I am attaching a callstack of a state change inside APlyaerController::IsLocalController, after a server calls OpenLevel to transit back to the main menu

APlayerController::IsLocalController() PlayerController.cpp:338 AController::PawnPendingDestroy(APawn *) Controller.cpp:430 APawn::DetachFromControllerPendingDestroy() Pawn.cpp:1086 APawn::Destroyed() Pawn.cpp:525 UWorld::DestroyActor(AActor *, bool, bool) LevelActor.cpp:884 AActor::Destroy(bool, bool) Actor.cpp:4847 UNetDriver::Shutdown() NetDriver.cpp:2169 UE::Private::DestroyNamedNetDriver_Local(FWorldContext &, FName) UnrealEngine.cpp:14465 [Inlined] UEngine::DestroyNamedNetDriver(UWorld *, FName) UnrealEngine.cpp:14520 UEngine::ShutdownWorldNetDriver(UWorld *) UnrealEngine.cpp:14052 UEngine::LoadMap(FWorldContext &, FURL, UPendingNetGame *, FString &) UnrealEngine.cpp:15594 UEngine::Browse(FWorldContext &, FURL, FString &) UnrealEngine.cpp:15154 UEngine::TickWorldTravel(FWorldContext &, float) UnrealEngine.cpp:15352 UEditorEngine::Tick(float, bool) EditorEngine.cpp:2111 UUnrealEdEngine::Tick(float, bool) UnrealEdEngine.cpp:547 FEngineLoop::Tick() LaunchEngineLoop.cpp:5871 [Inlined] EngineTick() Launch.cpp:69 GuardedMain(const wchar_t *) Launch.cpp:190 LaunchWindowsStartup(HINSTANCE__ *, HINSTANCE__ *, char *, int, const wchar_t *) LaunchWindows.cpp:266 WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int) LaunchWindows.cpp:317I hope it helps to reproduce the issue.

Thank you.

Hi,

Thank you for the additional information! I am able to reproduce the problem now, and I’ve opened a new issue, UE-295837, which should be visible in the public tracker in a day or so.

In the meanwhile, you may be able to use GetRemoteRole to determine which controller is local when the pawn is being destroyed, as this should still return ROLE_AutonomousProxy for the controller owned by the client.

Thanks,

Alex

Hi Alex,

I am glad you were able to reproduce the issue. Thanks for the help.