Guest User

Scipt

a guest
Nov 11th, 2017
1,409
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 35.47 KB | None | 0 0
  1. // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
  2.  
  3. #include "Engine/GameInstance.h"
  4. #include "Misc/MessageDialog.h"
  5. #include "Misc/CommandLine.h"
  6. #include "GameMapsSettings.h"
  7. #include "EngineGlobals.h"
  8. #include "Engine/EngineTypes.h"
  9. #include "TimerManager.h"
  10. #include "Engine/LatentActionManager.h"
  11. #include "Engine/World.h"
  12. #include "AI/Navigation/NavigationSystem.h"
  13. #include "Misc/Paths.h"
  14. #include "UObject/CoreOnline.h"
  15. #include "GameFramework/PlayerController.h"
  16. #include "Engine/Engine.h"
  17. #include "Engine/Console.h"
  18. #include "Engine/GameEngine.h"
  19. #include "GameFramework/GameModeBase.h"
  20. #include "Engine/DemoNetDriver.h"
  21. #include "Engine/NetworkObjectList.h"
  22. #include "Engine/LocalPlayer.h"
  23. #include "GameFramework/OnlineSession.h"
  24. #include "GameFramework/PlayerState.h"
  25. #include "GameFramework/GameSession.h"
  26. #include "Net/OnlineEngineInterface.h"
  27. #include "Kismet/GameplayStatics.h"
  28. #include "Framework/Application/SlateApplication.h"
  29. #include "GenericPlatform/GenericApplication.h"
  30.  
  31. #if WITH_EDITOR
  32. #include "Settings/LevelEditorPlaySettings.h"
  33. #include "Editor/EditorEngine.h"
  34. #endif
  35.  
  36. UGameInstance::UGameInstance(const FObjectInitializer& ObjectInitializer)
  37. : Super(ObjectInitializer)
  38. , TimerManager(new FTimerManager())
  39. , LatentActionManager(new FLatentActionManager())
  40. {
  41.     TimerManager->SetGameInstance(this);
  42. }
  43.  
  44. void UGameInstance::FinishDestroy()
  45. {
  46.     if (TimerManager)
  47.     {
  48.         delete TimerManager;
  49.         TimerManager = nullptr;
  50.     }
  51.  
  52.     // delete operator should handle null, but maintaining pattern of TimerManager:
  53.     if (LatentActionManager)
  54.     {
  55.         delete LatentActionManager;
  56.         LatentActionManager = nullptr;
  57.     }
  58.  
  59.     Super::FinishDestroy();
  60. }
  61.  
  62. UWorld* UGameInstance::GetWorld() const
  63. {
  64.     return WorldContext ? WorldContext->World() : NULL;
  65. }
  66.  
  67. UEngine* UGameInstance::GetEngine() const
  68. {
  69.     return CastChecked<UEngine>(GetOuter());
  70. }
  71.  
  72. void UGameInstance::Init()
  73. {
  74.     ReceiveInit();
  75.  
  76.     if (!IsRunningCommandlet())
  77.     {
  78.         UClass* SpawnClass = GetOnlineSessionClass();
  79.         OnlineSession = NewObject<UOnlineSession>(this, SpawnClass);
  80.         if (OnlineSession)
  81.         {
  82.             OnlineSession->RegisterOnlineDelegates();
  83.         }
  84.  
  85.         if (!IsDedicatedServerInstance())
  86.         {
  87.             TSharedPtr<GenericApplication> App = FSlateApplication::Get().GetPlatformApplication();
  88.             if (App.IsValid())
  89.             {
  90.                 App->RegisterConsoleCommandListener(GenericApplication::FOnConsoleCommandListener::CreateUObject(this, &ThisClass::OnConsoleInput));
  91.             }
  92.         }
  93.     }
  94. }
  95.  
  96. void UGameInstance::OnConsoleInput(const FString& Command)
  97. {
  98. #if !UE_BUILD_SHIPPING && !UE_BUILD_TEST
  99.     UConsole* ViewportConsole = (GEngine->GameViewport != nullptr) ? GEngine->GameViewport->ViewportConsole : nullptr;
  100.     if (ViewportConsole)
  101.     {
  102.         ViewportConsole->ConsoleCommand(Command);
  103.     }
  104.     else
  105.     {
  106.         GEngine->Exec(GetWorld(), *Command);
  107.     }
  108. #endif
  109. }
  110.  
  111. void UGameInstance::Shutdown()
  112. {
  113.     ReceiveShutdown();
  114.  
  115.     if (OnlineSession)
  116.     {
  117.         OnlineSession->ClearOnlineDelegates();
  118.         OnlineSession = nullptr;
  119.     }
  120.  
  121.     for (int32 PlayerIdx = LocalPlayers.Num() - 1; PlayerIdx >= 0; --PlayerIdx)
  122.     {
  123.         ULocalPlayer* Player = LocalPlayers[PlayerIdx];
  124.  
  125.         if (Player)
  126.         {
  127.             RemoveLocalPlayer(Player);
  128.         }
  129.     }
  130.  
  131.     // Clear the world context pointer to prevent further access.
  132.     WorldContext = nullptr;
  133. }
  134.  
  135. void UGameInstance::InitializeStandalone()
  136. {
  137.     // Creates the world context. This should be the only WorldContext that ever gets created for this GameInstance.
  138.     WorldContext = &GetEngine()->CreateNewWorldContext(EWorldType::Game);
  139.     WorldContext->OwningGameInstance = this;
  140.  
  141.     // In standalone create a dummy world from the beginning to avoid issues of not having a world until LoadMap gets us our real world
  142.     UWorld* DummyWorld = UWorld::CreateWorld(EWorldType::Game, false);
  143.     DummyWorld->SetGameInstance(this);
  144.     WorldContext->SetCurrentWorld(DummyWorld);
  145.  
  146.     Init();
  147. }
  148.  
  149. #if WITH_EDITOR
  150.  
  151. FGameInstancePIEResult UGameInstance::InitializeForPlayInEditor(int32 PIEInstanceIndex, const FGameInstancePIEParameters& Params)
  152. {
  153.     UEditorEngine* const EditorEngine = CastChecked<UEditorEngine>(GetEngine());
  154.  
  155.     // Look for an existing pie world context, may have been created before
  156.     WorldContext = EditorEngine->GetWorldContextFromPIEInstance(PIEInstanceIndex);
  157.  
  158.     if (!WorldContext)
  159.     {
  160.         // If not, create a new one
  161.         WorldContext = &EditorEngine->CreateNewWorldContext(EWorldType::PIE);
  162.         WorldContext->PIEInstance = PIEInstanceIndex;
  163.     }
  164.  
  165.     WorldContext->RunAsDedicated = Params.bRunAsDedicated;
  166.  
  167.     WorldContext->OwningGameInstance = this;
  168.    
  169.     const FString WorldPackageName = EditorEngine->EditorWorld->GetOutermost()->GetName();
  170.  
  171.     // Establish World Context for PIE World
  172.     WorldContext->LastURL.Map = WorldPackageName;
  173.     WorldContext->PIEPrefix = WorldContext->PIEInstance != INDEX_NONE ? UWorld::BuildPIEPackagePrefix(WorldContext->PIEInstance) : FString();
  174.  
  175.     const ULevelEditorPlaySettings* PlayInSettings = GetDefault<ULevelEditorPlaySettings>();
  176.  
  177.     // We always need to create a new PIE world unless we're using the editor world for SIE
  178.     UWorld* NewWorld = nullptr;
  179.  
  180.     bool bNeedsGarbageCollection = false;
  181.     const EPlayNetMode PlayNetMode = [&PlayInSettings]{ EPlayNetMode NetMode(PIE_Standalone); return (PlayInSettings->GetPlayNetMode(NetMode) ? NetMode : PIE_Standalone); }();
  182.     const bool CanRunUnderOneProcess = [&PlayInSettings]{ bool RunUnderOneProcess(false); return (PlayInSettings->GetRunUnderOneProcess(RunUnderOneProcess) && RunUnderOneProcess); }();
  183.     if (PlayNetMode == PIE_Client)
  184.     {
  185.         // We are going to connect, so just load an empty world
  186.         NewWorld = EditorEngine->CreatePIEWorldFromEntry(*WorldContext, EditorEngine->EditorWorld, PIEMapName);
  187.     }
  188.     else if (PlayNetMode == PIE_ListenServer && !CanRunUnderOneProcess)
  189.     {
  190.         // We *have* to save the world to disk in order to be a listen server that allows other processes to connect.
  191.         // Otherwise, clients would not be able to load the world we are using
  192.         NewWorld = EditorEngine->CreatePIEWorldBySavingToTemp(*WorldContext, EditorEngine->EditorWorld, PIEMapName);
  193.     }
  194.     else
  195.     {
  196.         // Standard PIE path: just duplicate the EditorWorld
  197.         NewWorld = EditorEngine->CreatePIEWorldByDuplication(*WorldContext, EditorEngine->EditorWorld, PIEMapName);
  198.  
  199.         // Duplication can result in unreferenced objects, so indicate that we should do a GC pass after initializing the world context
  200.         bNeedsGarbageCollection = true;
  201.     }
  202.  
  203.     // failed to create the world!
  204.     if (NewWorld == nullptr)
  205.     {
  206.         return FGameInstancePIEResult::Failure(NSLOCTEXT("UnrealEd", "Error_FailedCreateEditorPreviewWorld", "Failed to create editor preview world."));
  207.     }
  208.  
  209.     NewWorld->SetGameInstance(this);
  210.     WorldContext->SetCurrentWorld(NewWorld);
  211.     WorldContext->AddRef(EditorEngine->PlayWorld);  // Tie this context to this UEngine::PlayWorld*     // @fixme, needed still?
  212.  
  213.     // make sure we can clean up this world!
  214.     NewWorld->ClearFlags(RF_Standalone);
  215.     NewWorld->bKismetScriptError = Params.bAnyBlueprintErrors;
  216.  
  217.     // Do a GC pass if necessary to remove any potentially unreferenced objects
  218.     if(bNeedsGarbageCollection)
  219.     {
  220.         CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
  221.     }
  222.  
  223.     Init();
  224.  
  225.     // Give the deprecated method a chance to fail as well
  226.     FGameInstancePIEResult InitResult = FGameInstancePIEResult::Success();
  227.  
  228.     if (InitResult.IsSuccess())
  229.     {
  230.         PRAGMA_DISABLE_DEPRECATION_WARNINGS
  231.         InitResult = InitializePIE(Params.bAnyBlueprintErrors, PIEInstanceIndex, Params.bRunAsDedicated) ?
  232.             FGameInstancePIEResult::Success() :
  233.             FGameInstancePIEResult::Failure(NSLOCTEXT("UnrealEd", "Error_CouldntInitInstance", "The game instance failed to Play/Simulate In Editor"));
  234.         PRAGMA_ENABLE_DEPRECATION_WARNINGS
  235.     }
  236.  
  237.     return InitResult;
  238. }
  239.  
  240.  
  241. bool UGameInstance::InitializePIE(bool bAnyBlueprintErrors, int32 PIEInstance, bool bRunAsDedicated)
  242. {
  243.     // DEPRECATED VERSION
  244.     return true;
  245. }
  246.  
  247. FGameInstancePIEResult UGameInstance::StartPlayInEditorGameInstance(ULocalPlayer* LocalPlayer, const FGameInstancePIEParameters& Params)
  248. {
  249.     OnStart();
  250.  
  251.     UEditorEngine* const EditorEngine = CastChecked<UEditorEngine>(GetEngine());
  252.     ULevelEditorPlaySettings const* PlayInSettings = GetDefault<ULevelEditorPlaySettings>();
  253.  
  254.     const EPlayNetMode PlayNetMode = [&PlayInSettings]{ EPlayNetMode NetMode(PIE_Standalone); return (PlayInSettings->GetPlayNetMode(NetMode) ? NetMode : PIE_Standalone); }();
  255.  
  256.     // for clients, just connect to the server
  257.     if (PlayNetMode == PIE_Client)
  258.     {
  259.         FString Error;
  260.         FURL BaseURL = WorldContext->LastURL;
  261.         if (EditorEngine->Browse(*WorldContext, FURL(&BaseURL, TEXT("127.0.0.1"), (ETravelType)TRAVEL_Absolute), Error) == EBrowseReturnVal::Pending)
  262.         {
  263.             EditorEngine->TransitionType = TT_WaitingToConnect;
  264.         }
  265.         else
  266.         {
  267.             return FGameInstancePIEResult::Failure(FText::Format(NSLOCTEXT("UnrealEd", "Error_CouldntLaunchPIEClient", "Couldn't Launch PIE Client: {0}"), FText::FromString(Error)));
  268.         }
  269.     }
  270.     else
  271.     {
  272.         // we're going to be playing in the current world, get it ready for play
  273.         UWorld* const PlayWorld = GetWorld();
  274.  
  275.         // make a URL
  276.         FURL URL;
  277.         // If the user wants to start in spectator mode, do not use the custom play world for now
  278.         if (EditorEngine->UserEditedPlayWorldURL.Len() > 0 && !Params.bStartInSpectatorMode)
  279.         {
  280.             // If the user edited the play world url. Verify that the map name is the same as the currently loaded map.
  281.             URL = FURL(NULL, *EditorEngine->UserEditedPlayWorldURL, TRAVEL_Absolute);
  282.             if (URL.Map != PIEMapName)
  283.             {
  284.                 // Ensure the URL map name is the same as the generated play world map name.
  285.                 URL.Map = PIEMapName;
  286.             }
  287.         }
  288.         else
  289.         {
  290.             // The user did not edit the url, just build one from scratch.
  291.             URL = FURL(NULL, *EditorEngine->BuildPlayWorldURL(*PIEMapName, Params.bStartInSpectatorMode), TRAVEL_Absolute);
  292.         }
  293.  
  294.         // If a start location is specified, spawn a temporary PlayerStartPIE actor at the start location and use it as the portal.
  295.         AActor* PlayerStart = NULL;
  296.         if (EditorEngine->SpawnPlayFromHereStart(PlayWorld, PlayerStart, EditorEngine->PlayWorldLocation, EditorEngine->PlayWorldRotation) == false)
  297.         {
  298.             // failed to create "play from here" playerstart
  299.             return FGameInstancePIEResult::Failure(NSLOCTEXT("UnrealEd", "Error_FailedCreatePlayFromHerePlayerStart", "Failed to create PlayerStart at desired starting location."));
  300.         }
  301.  
  302.         if (!PlayWorld->SetGameMode(URL))
  303.         {
  304.             // Setting the game mode failed so bail
  305.             return FGameInstancePIEResult::Failure(NSLOCTEXT("UnrealEd", "Error_FailedCreateEditorPreviewWorld", "Failed to create editor preview world."));
  306.         }
  307.        
  308.         // Make sure "always loaded" sub-levels are fully loaded
  309.         PlayWorld->FlushLevelStreaming(EFlushLevelStreamingType::Visibility);
  310.  
  311.         PlayWorld->CreateAISystem();
  312.  
  313.         PlayWorld->InitializeActorsForPlay(URL);
  314.         // calling it after InitializeActorsForPlay has been called to have all potential bounding boxed initialized
  315.         UNavigationSystem::InitializeForWorld(PlayWorld, LocalPlayers.Num() > 0 ? FNavigationSystemRunMode::PIEMode : FNavigationSystemRunMode::SimulationMode);
  316.  
  317.         // @todo, just use WorldContext.GamePlayer[0]?
  318.         if (LocalPlayer)
  319.         {
  320.             FString Error;
  321.             if (!LocalPlayer->SpawnPlayActor(URL.ToString(1), Error, PlayWorld))
  322.             {
  323.                 return FGameInstancePIEResult::Failure(FText::Format(NSLOCTEXT("UnrealEd", "Error_CouldntSpawnPlayer", "Couldn't spawn player: {0}"), FText::FromString(Error)));
  324.             }
  325.         }
  326.  
  327.         UGameViewportClient* const GameViewport = GetGameViewportClient();
  328.         if (GameViewport != NULL && GameViewport->Viewport != NULL)
  329.         {
  330.             // Stream any levels now that need to be loaded before the game starts
  331.             GEngine->BlockTillLevelStreamingCompleted(PlayWorld);
  332.         }
  333.        
  334.         if (PlayNetMode == PIE_ListenServer)
  335.         {
  336.             // start listen server with the built URL
  337.             PlayWorld->Listen(URL);
  338.         }
  339.  
  340.         PlayWorld->BeginPlay();
  341.     }
  342.  
  343.     // Give the deprecated method a chance to fail as well
  344.     FGameInstancePIEResult StartResult = FGameInstancePIEResult::Success();
  345.  
  346.     if (StartResult.IsSuccess())
  347.     {
  348.         PRAGMA_DISABLE_DEPRECATION_WARNINGS
  349.         StartResult = StartPIEGameInstance(LocalPlayer, Params.bSimulateInEditor, Params.bAnyBlueprintErrors, Params.bStartInSpectatorMode) ?
  350.             FGameInstancePIEResult::Success() :
  351.             FGameInstancePIEResult::Failure(NSLOCTEXT("UnrealEd", "Error_CouldntInitInstance", "The game instance failed to Play/Simulate In Editor"));
  352.         PRAGMA_ENABLE_DEPRECATION_WARNINGS
  353.     }
  354.  
  355.     return StartResult;
  356. }
  357.  
  358. bool UGameInstance::StartPIEGameInstance(ULocalPlayer* LocalPlayer, bool bInSimulateInEditor, bool bAnyBlueprintErrors, bool bStartInSpectatorMode)
  359. {
  360.     // DEPRECATED VERSION
  361.     return true;
  362. }
  363. #endif
  364.  
  365.  
  366. UGameViewportClient* UGameInstance::GetGameViewportClient() const
  367. {
  368.     FWorldContext* const WC = GetWorldContext();
  369.     return WC ? WC->GameViewport : nullptr;
  370. }
  371.  
  372. // This can be defined in the target.cs file to allow map overrides in shipping builds
  373. #ifndef UE_ALLOW_MAP_OVERRIDE_IN_SHIPPING
  374. #define UE_ALLOW_MAP_OVERRIDE_IN_SHIPPING 0
  375. #endif
  376.  
  377. void UGameInstance::StartGameInstance()
  378. {
  379.     UEngine* const Engine = GetEngine();
  380.  
  381.     // Create default URL.
  382.     // @note: if we change how we determine the valid start up map update LaunchEngineLoop's GetStartupMap()
  383.     FURL DefaultURL;
  384.     DefaultURL.LoadURLConfig(TEXT("DefaultPlayer"), GGameIni);
  385.  
  386.     // Enter initial world.
  387.     EBrowseReturnVal::Type BrowseRet = EBrowseReturnVal::Failure;
  388.     FString Error;
  389.    
  390.     const TCHAR* Tmp = FCommandLine::Get();
  391.  
  392. #if UE_BUILD_SHIPPING && !UE_SERVER && !UE_ALLOW_MAP_OVERRIDE_IN_SHIPPING
  393.     // In shipping don't allow a map override unless on server
  394.     Tmp = TEXT("");
  395. #endif // UE_BUILD_SHIPPING && !UE_SERVER
  396.  
  397. #if !UE_SERVER
  398.     // Parse replay name if specified on cmdline
  399.     FString ReplayCommand;
  400.     if ( FParse::Value( Tmp, TEXT( "-REPLAY=" ), ReplayCommand ) )
  401.     {
  402.         PlayReplay( ReplayCommand );
  403.         return;
  404.     }
  405. #endif // !UE_SERVER
  406.  
  407.     const UGameMapsSettings* GameMapsSettings = GetDefault<UGameMapsSettings>();
  408.     const FString& DefaultMap = GameMapsSettings->GetGameDefaultMap();
  409.  
  410.     FString PackageName;
  411.     if (!FParse::Token(Tmp, PackageName, 0) || **PackageName == '-')
  412.     {
  413.         PackageName = DefaultMap + GameMapsSettings->LocalMapOptions;
  414.     }
  415.  
  416.     FURL URL(&DefaultURL, *PackageName, TRAVEL_Partial);
  417.     if (URL.Valid)
  418.     {
  419.         BrowseRet = Engine->Browse(*WorldContext, URL, Error);
  420.     }
  421.  
  422.     // If waiting for a network connection, go into the starting level.
  423.     if (BrowseRet != EBrowseReturnVal::Success)
  424.     {
  425.         UE_LOG(LogLoad, Error, TEXT("%s"), *FString::Printf(TEXT("Failed to enter %s: %s. Please check the log for errors."), *URL.Map, *Error));
  426.  
  427.         // the map specified on the command-line couldn't be loaded.  ask the user if we should load the default map instead
  428.         if (FCString::Stricmp(*PackageName, *DefaultMap) != 0)
  429.         {
  430.             const FText Message = FText::Format(NSLOCTEXT("Engine", "MapNotFound", "The map specified on the commandline '{0}' could not be found. Would you like to load the default map instead?"), FText::FromString(URL.Map));
  431.             if (   FCString::Stricmp(*URL.Map, *DefaultMap) != 0  
  432.                 && FMessageDialog::Open(EAppMsgType::OkCancel, Message) != EAppReturnType::Ok)
  433.             {
  434.                 // user canceled (maybe a typo while attempting to run a commandlet)
  435.                 FPlatformMisc::RequestExit(false);
  436.                 return;
  437.             }
  438.             else
  439.             {
  440.                 BrowseRet = Engine->Browse(*WorldContext, FURL(&DefaultURL, *(DefaultMap + GameMapsSettings->LocalMapOptions), TRAVEL_Partial), Error);
  441.             }
  442.         }
  443.         else
  444.         {
  445.             const FText Message = FText::Format(NSLOCTEXT("Engine", "MapNotFoundNoFallback", "The map specified on the commandline '{0}' could not be found. Exiting."), FText::FromString(URL.Map));
  446.             FMessageDialog::Open(EAppMsgType::Ok, Message);
  447.             FPlatformMisc::RequestExit(false);
  448.             return;
  449.         }
  450.     }
  451.  
  452.     // Handle failure.
  453.     if (BrowseRet != EBrowseReturnVal::Success)
  454.     {
  455.         UE_LOG(LogLoad, Error, TEXT("%s"), *FString::Printf(TEXT("Failed to enter %s: %s. Please check the log for errors."), *DefaultMap, *Error));
  456.         const FText Message = FText::Format(NSLOCTEXT("Engine", "DefaultMapNotFound", "The default map '{0}' could not be found. Exiting."), FText::FromString(DefaultMap));
  457.         FMessageDialog::Open(EAppMsgType::Ok, Message);
  458.         FPlatformMisc::RequestExit(false);
  459.         return;
  460.     }
  461.  
  462.     OnStart();
  463. }
  464.  
  465. void UGameInstance::OnStart()
  466. {
  467.  
  468. }
  469.  
  470. bool UGameInstance::HandleOpenCommand(const TCHAR* Cmd, FOutputDevice& Ar, UWorld* InWorld)
  471. {
  472.     check(WorldContext && WorldContext->World() == InWorld);
  473.  
  474.     UEngine* const Engine = GetEngine();
  475.     return Engine->HandleOpenCommand(Cmd, Ar, InWorld);
  476. }
  477.  
  478. bool UGameInstance::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
  479. {
  480.     // @todo a bunch of stuff in UEngine probably belongs here as well
  481.     if (FParse::Command(&Cmd, TEXT("OPEN")))
  482.     {
  483.         return HandleOpenCommand(Cmd, Ar, InWorld);
  484.     }
  485.  
  486.     return false;
  487. }
  488.  
  489. ULocalPlayer* UGameInstance::CreateInitialPlayer(FString& OutError)
  490. {
  491.     return CreateLocalPlayer( 0, OutError, false );
  492. }
  493.  
  494. ULocalPlayer* UGameInstance::CreateLocalPlayer(int32 ControllerId, FString& OutError, bool bSpawnActor)
  495. {
  496.     check(GetEngine()->LocalPlayerClass != NULL);
  497.  
  498.     ULocalPlayer* NewPlayer = NULL;
  499.     int32 InsertIndex = INDEX_NONE;
  500.  
  501.     const int32 MaxSplitscreenPlayers = (GetGameViewportClient() != NULL) ? GetGameViewportClient()->MaxSplitscreenPlayers : 1;
  502.  
  503.     if (FindLocalPlayerFromControllerId( ControllerId ) != NULL)
  504.     {
  505.         OutError = FString::Printf(TEXT("A local player already exists for controller ID %d,"), ControllerId);
  506.     }
  507.     else if (LocalPlayers.Num() < MaxSplitscreenPlayers)
  508.     {
  509.         // If the controller ID is not specified then find the first available
  510.         if (ControllerId < 0)
  511.         {
  512.             for (ControllerId = 0; ControllerId < MaxSplitscreenPlayers; ++ControllerId)
  513.             {
  514.                 if (FindLocalPlayerFromControllerId( ControllerId ) == NULL)
  515.                 {
  516.                     break;
  517.                 }
  518.             }
  519.             check(ControllerId < MaxSplitscreenPlayers);
  520.         }
  521.         else if (ControllerId >= MaxSplitscreenPlayers)
  522.         {
  523.             UE_LOG(LogPlayerManagement, Warning, TEXT("Controller ID (%d) is unlikely to map to any physical device, so this player will not receive input"), ControllerId);
  524.         }
  525.  
  526.         NewPlayer = NewObject<ULocalPlayer>(GetEngine(), GetEngine()->LocalPlayerClass);
  527.         InsertIndex = AddLocalPlayer(NewPlayer, ControllerId);
  528.         if (bSpawnActor && InsertIndex != INDEX_NONE && GetWorld() != NULL)
  529.         {
  530.             if (GetWorld()->GetNetMode() != NM_Client)
  531.             {
  532.                 // server; spawn a new PlayerController immediately
  533.                 if (!NewPlayer->SpawnPlayActor("", OutError, GetWorld()))
  534.                 {
  535.                     RemoveLocalPlayer(NewPlayer);
  536.                     NewPlayer = NULL;
  537.                 }
  538.             }
  539.             else
  540.             {
  541.                 // client; ask the server to let the new player join
  542.                 NewPlayer->SendSplitJoin();
  543.             }
  544.         }
  545.     }
  546.     else
  547.     {
  548.         OutError = FString::Printf(TEXT( "Maximum number of players (%d) already created.  Unable to create more."), MaxSplitscreenPlayers);
  549.     }
  550.  
  551.     if (OutError != TEXT(""))
  552.     {
  553.         UE_LOG(LogPlayerManagement, Log, TEXT("UPlayer* creation failed with error: %s"), *OutError);
  554.     }
  555.  
  556.     return NewPlayer;
  557. }
  558.  
  559. int32 UGameInstance::AddLocalPlayer(ULocalPlayer* NewLocalPlayer, int32 ControllerId)
  560. {
  561.     if (NewLocalPlayer == NULL)
  562.     {
  563.         return INDEX_NONE;
  564.     }
  565.  
  566.     const int32 InsertIndex = LocalPlayers.Num();
  567.  
  568.     // Add to list
  569.     LocalPlayers.AddUnique(NewLocalPlayer);
  570.  
  571.     // Notify the player he/she was added
  572.     NewLocalPlayer->PlayerAdded(GetGameViewportClient(), ControllerId);
  573.  
  574.     // Notify the viewport that we added a player (so it can update splitscreen settings, etc)
  575.     if ( GetGameViewportClient() != NULL )
  576.     {
  577.         GetGameViewportClient()->NotifyPlayerAdded(InsertIndex, NewLocalPlayer);
  578.     }
  579.  
  580.     return InsertIndex;
  581. }
  582.  
  583. bool UGameInstance::RemoveLocalPlayer(ULocalPlayer* ExistingPlayer)
  584. {
  585.     // FIXME: Notify server we want to leave the game if this is an online game
  586.     if (ExistingPlayer->PlayerController != NULL)
  587.     {
  588.         // FIXME: Do this all inside PlayerRemoved?
  589.         ExistingPlayer->PlayerController->CleanupGameViewport();
  590.  
  591.         // Destroy the player's actors.
  592.         if ( ExistingPlayer->PlayerController->Role == ROLE_Authority )
  593.         {
  594.             ExistingPlayer->PlayerController->Destroy();
  595.         }
  596.     }
  597.  
  598.     // Remove the player from the context list
  599.     const int32 OldIndex = LocalPlayers.Find(ExistingPlayer);
  600.  
  601.     if (ensure(OldIndex != INDEX_NONE))
  602.     {
  603.         ExistingPlayer->PlayerRemoved();
  604.         LocalPlayers.RemoveAt(OldIndex);
  605.  
  606.         // Notify the viewport so the viewport can do the fixups, resize, etc
  607.         if (GetGameViewportClient() != NULL)
  608.         {
  609.             GetGameViewportClient()->NotifyPlayerRemoved(OldIndex, ExistingPlayer);
  610.         }
  611.     }
  612.  
  613.     // Disassociate this viewport client from the player.
  614.     // Do this after notifications, as some of them require the ViewportClient.
  615.     ExistingPlayer->ViewportClient = NULL;
  616.  
  617.     UE_LOG(LogPlayerManagement, Log, TEXT("UGameInstance::RemovePlayer: Removed player %s with ControllerId %i at index %i (%i remaining players)"), *ExistingPlayer->GetName(), ExistingPlayer->GetControllerId(), OldIndex, LocalPlayers.Num());
  618.  
  619.     return true;
  620. }
  621.  
  622. void UGameInstance::DebugCreatePlayer(int32 ControllerId)
  623. {
  624. #if !UE_BUILD_SHIPPING
  625.     FString Error;
  626.     CreateLocalPlayer(ControllerId, Error, true);
  627.     if (Error.Len() > 0)
  628.     {
  629.         UE_LOG(LogPlayerManagement, Error, TEXT("Failed to DebugCreatePlayer: %s"), *Error);
  630.     }
  631. #endif
  632. }
  633.  
  634. void UGameInstance::DebugRemovePlayer(int32 ControllerId)
  635. {
  636. #if !UE_BUILD_SHIPPING
  637.  
  638.     ULocalPlayer* const ExistingPlayer = FindLocalPlayerFromControllerId(ControllerId);
  639.     if (ExistingPlayer != NULL)
  640.     {
  641.         RemoveLocalPlayer(ExistingPlayer);
  642.     }
  643. #endif
  644. }
  645.  
  646. int32 UGameInstance::GetNumLocalPlayers() const
  647. {
  648.     return LocalPlayers.Num();
  649. }
  650.  
  651. ULocalPlayer* UGameInstance::GetLocalPlayerByIndex(const int32 Index) const
  652. {
  653.     return LocalPlayers[Index];
  654. }
  655.  
  656. APlayerController* UGameInstance::GetFirstLocalPlayerController(UWorld* World) const
  657. {
  658.     if (World == nullptr)
  659.     {
  660.         for (ULocalPlayer* Player : LocalPlayers)
  661.         {
  662.             // Returns the first non-null UPlayer::PlayerController without filtering by UWorld.
  663.             if (Player && Player->PlayerController)
  664.             {
  665.                 // return first non-null entry
  666.                 return Player->PlayerController;
  667.             }
  668.         }
  669.     }
  670.     else
  671.     {
  672.         // Only return a local PlayerController from the given World.
  673.         for (FConstPlayerControllerIterator Iterator = World->GetPlayerControllerIterator(); Iterator; ++Iterator)
  674.         {
  675.             if (*Iterator != nullptr && (*Iterator)->IsLocalController())
  676.             {
  677.                 return Iterator->Get();
  678.             }
  679.         }
  680.     }
  681.  
  682.     // didn't find one
  683.     return nullptr;
  684. }
  685.  
  686. APlayerController* UGameInstance::GetPrimaryPlayerController() const
  687. {
  688.     UWorld* World = GetWorld();
  689.     check(World);
  690.  
  691.     APlayerController* PrimaryController = nullptr;
  692.     for (FConstPlayerControllerIterator Iterator = World->GetPlayerControllerIterator(); Iterator; ++Iterator)
  693.     {
  694.         APlayerController* NextPlayer = Cast<APlayerController>(*Iterator);
  695.         if (NextPlayer && NextPlayer->PlayerState && NextPlayer->PlayerState->UniqueId.IsValid() && NextPlayer->IsPrimaryPlayer())
  696.         {
  697.             PrimaryController = NextPlayer;
  698.             break;
  699.         }
  700.     }
  701.  
  702.     return PrimaryController;
  703. }
  704.  
  705. TSharedPtr<const FUniqueNetId> UGameInstance::GetPrimaryPlayerUniqueId() const
  706. {
  707.     ULocalPlayer* PrimaryLP = nullptr;
  708.  
  709.     TArray<ULocalPlayer*>::TConstIterator LocalPlayerIt = GetLocalPlayerIterator();
  710.     for (; LocalPlayerIt && *LocalPlayerIt; ++LocalPlayerIt)
  711.     {
  712.         PrimaryLP = *LocalPlayerIt;
  713.         if (PrimaryLP && PrimaryLP->PlayerController && PrimaryLP->PlayerController->IsPrimaryPlayer())
  714.         {
  715.             break;
  716.         }
  717.     }
  718.  
  719.     TSharedPtr<const FUniqueNetId> LocalUserId = nullptr;
  720.     if (PrimaryLP)
  721.     {
  722.         LocalUserId = PrimaryLP->GetPreferredUniqueNetId();
  723.     }
  724.  
  725.     return LocalUserId;
  726. }
  727.  
  728. ULocalPlayer* UGameInstance::FindLocalPlayerFromControllerId(const int32 ControllerId) const
  729. {
  730.     for (ULocalPlayer * LP : LocalPlayers)
  731.     {
  732.         if (LP && (LP->GetControllerId() == ControllerId))
  733.         {
  734.             return LP;
  735.         }
  736.     }
  737.  
  738.     return nullptr;
  739. }
  740.  
  741. ULocalPlayer* UGameInstance::FindLocalPlayerFromUniqueNetId(const FUniqueNetId& UniqueNetId) const
  742. {
  743.     for (ULocalPlayer* Player : LocalPlayers)
  744.     {
  745.         if (Player == NULL)
  746.         {
  747.             continue;
  748.         }
  749.  
  750.         TSharedPtr<const FUniqueNetId> OtherUniqueNetId = Player->GetPreferredUniqueNetId();
  751.  
  752.         if (!OtherUniqueNetId.IsValid())
  753.         {
  754.             continue;
  755.         }
  756.  
  757.         if (*OtherUniqueNetId == UniqueNetId)
  758.         {
  759.             // Match
  760.             return Player;
  761.         }
  762.     }
  763.  
  764.     // didn't find one
  765.     return nullptr;
  766. }
  767.  
  768. ULocalPlayer* UGameInstance::FindLocalPlayerFromUniqueNetId(TSharedPtr<const FUniqueNetId> UniqueNetId) const
  769. {
  770.     if (!UniqueNetId.IsValid())
  771.     {
  772.         return nullptr;
  773.     }
  774.  
  775.     return FindLocalPlayerFromUniqueNetId(*UniqueNetId);
  776. }
  777.  
  778. ULocalPlayer* UGameInstance::GetFirstGamePlayer() const
  779. {
  780.     return (LocalPlayers.Num() > 0) ? LocalPlayers[0] : nullptr;
  781. }
  782.  
  783. void UGameInstance::CleanupGameViewport()
  784. {
  785.     // Clean up the viewports that have been closed.
  786.     for(int32 idx = LocalPlayers.Num()-1; idx >= 0; --idx)
  787.     {
  788.         ULocalPlayer *Player = LocalPlayers[idx];
  789.  
  790.         if(Player && Player->ViewportClient && !Player->ViewportClient->Viewport)
  791.         {
  792.             RemoveLocalPlayer( Player );
  793.         }
  794.     }
  795. }
  796.  
  797. TArray<class ULocalPlayer*>::TConstIterator UGameInstance::GetLocalPlayerIterator() const
  798. {
  799.     return LocalPlayers.CreateConstIterator();
  800. }
  801.  
  802. const TArray<class ULocalPlayer*>& UGameInstance::GetLocalPlayers() const
  803. {
  804.     return LocalPlayers;
  805. }
  806.  
  807. void UGameInstance::StartRecordingReplay(const FString& Name, const FString& FriendlyName, const TArray<FString>& AdditionalOptions)
  808. {
  809.     if ( FParse::Param( FCommandLine::Get(),TEXT( "NOREPLAYS" ) ) )
  810.     {
  811.         UE_LOG( LogDemo, Warning, TEXT( "UGameInstance::StartRecordingReplay: Rejected due to -noreplays option" ) );
  812.         return;
  813.     }
  814.  
  815.     UWorld* CurrentWorld = GetWorld();
  816.  
  817.     if ( CurrentWorld == nullptr )
  818.     {
  819.         UE_LOG( LogDemo, Warning, TEXT( "UGameInstance::StartRecordingReplay: GetWorld() is null" ) );
  820.         return;
  821.     }
  822.  
  823.     if ( CurrentWorld->WorldType == EWorldType::PIE )
  824.     {
  825.         UE_LOG(LogDemo, Warning, TEXT("UGameInstance::StartRecordingReplay: Function called while running a PIE instance, this is disabled."));
  826.         return;
  827.     }
  828.  
  829.     if ( CurrentWorld->DemoNetDriver && CurrentWorld->DemoNetDriver->IsPlaying() )
  830.     {
  831.         UE_LOG(LogDemo, Warning, TEXT("UGameInstance::StartRecordingReplay: A replay is already playing, cannot begin recording another one."));
  832.         return;
  833.     }
  834.  
  835.     FURL DemoURL;
  836.     FString DemoName = Name;
  837.    
  838.     DemoName.ReplaceInline( TEXT( "%m" ), *CurrentWorld->GetMapName() );
  839.  
  840.     // replace the current URL's map with a demo extension
  841.     DemoURL.Map = DemoName;
  842.     DemoURL.AddOption( *FString::Printf( TEXT( "DemoFriendlyName=%s" ), *FriendlyName ) );
  843.  
  844.     for ( const FString& Option : AdditionalOptions )
  845.     {
  846.         DemoURL.AddOption(*Option);
  847.     }
  848.  
  849.     bool bDestroyedDemoNetDriver = false;
  850.     if (!CurrentWorld->DemoNetDriver || !CurrentWorld->DemoNetDriver->bRecordMapChanges || !CurrentWorld->DemoNetDriver->IsRecordingPaused())
  851.     {
  852.         CurrentWorld->DestroyDemoNetDriver();
  853.         bDestroyedDemoNetDriver = true;
  854.  
  855.         const FName NAME_DemoNetDriver(TEXT("DemoNetDriver"));
  856.  
  857.         if (!GEngine->CreateNamedNetDriver(CurrentWorld, NAME_DemoNetDriver, NAME_DemoNetDriver))
  858.         {
  859.             UE_LOG(LogDemo, Warning, TEXT("RecordReplay: failed to create demo net driver!"));
  860.             return;
  861.         }
  862.  
  863.         CurrentWorld->DemoNetDriver = Cast< UDemoNetDriver >(GEngine->FindNamedNetDriver(CurrentWorld, NAME_DemoNetDriver));
  864.     }
  865.  
  866.     check(CurrentWorld->DemoNetDriver != nullptr);
  867.  
  868.     CurrentWorld->DemoNetDriver->SetWorld( CurrentWorld );
  869.  
  870.     // Set the new demo driver as the current collection's driver
  871.     FLevelCollection* CurrentLevelCollection = CurrentWorld->FindCollectionByType(ELevelCollectionType::DynamicSourceLevels);
  872.     if (CurrentLevelCollection)
  873.     {
  874.         CurrentLevelCollection->SetDemoNetDriver(CurrentWorld->DemoNetDriver);
  875.     }
  876.  
  877.     FString Error;
  878.  
  879.     if (bDestroyedDemoNetDriver)
  880.     {
  881.         if (!CurrentWorld->DemoNetDriver->InitListen(CurrentWorld, DemoURL, false, Error))
  882.         {
  883.             UE_LOG(LogDemo, Warning, TEXT("Demo recording - InitListen failed: %s"), *Error);
  884.             CurrentWorld->DemoNetDriver = NULL;
  885.             return;
  886.         }
  887.     }
  888.     else if (!CurrentWorld->DemoNetDriver->ContinueListen(DemoURL))
  889.     {
  890.         UE_LOG(LogDemo, Warning, TEXT("Demo recording - ContinueListen failed"));
  891.         CurrentWorld->DemoNetDriver = NULL;
  892.         return;
  893.     }
  894.  
  895.     UE_LOG(LogDemo, Log, TEXT( "Num Network Actors: %i" ), CurrentWorld->DemoNetDriver->GetNetworkObjectList().GetActiveObjects().Num() );
  896. }
  897.  
  898. void UGameInstance::StopRecordingReplay()
  899. {
  900.     UWorld* CurrentWorld = GetWorld();
  901.  
  902.     if ( CurrentWorld == nullptr )
  903.     {
  904.         UE_LOG( LogDemo, Warning, TEXT( "UGameInstance::StopRecordingReplay: GetWorld() is null" ) );
  905.         return;
  906.     }
  907.  
  908.     bool LoadDefaultMap = false;
  909.  
  910.     if ( CurrentWorld->DemoNetDriver && CurrentWorld->DemoNetDriver->IsPlaying() )
  911.     {
  912.         LoadDefaultMap = true;
  913.     }
  914.  
  915.     CurrentWorld->DestroyDemoNetDriver();
  916.  
  917.     if ( LoadDefaultMap )
  918.     {
  919.         GEngine->BrowseToDefaultMap(*GetWorldContext());
  920.     }
  921. }
  922.  
  923. void UGameInstance::PlayReplay(const FString& Name, UWorld* WorldOverride, const TArray<FString>& AdditionalOptions)
  924. {
  925.     UWorld* CurrentWorld = WorldOverride != nullptr ? WorldOverride : GetWorld();
  926.  
  927.     if ( CurrentWorld == nullptr )
  928.     {
  929.         UE_LOG( LogDemo, Warning, TEXT( "UGameInstance::PlayReplay: GetWorld() is null" ) );
  930.         return;
  931.     }
  932.  
  933.     if ( CurrentWorld->WorldType == EWorldType::PIE )
  934.     {
  935.         UE_LOG( LogDemo, Warning, TEXT( "UGameInstance::PlayReplay: Function called while running a PIE instance, this is disabled." ) );
  936.         return;
  937.     }
  938.  
  939.     CurrentWorld->DestroyDemoNetDriver();
  940.  
  941.     FURL DemoURL;
  942.     UE_LOG( LogDemo, Log, TEXT( "PlayReplay: Attempting to play demo %s" ), *Name );
  943.  
  944.     DemoURL.Map = Name;
  945.    
  946.     for ( const FString& Option : AdditionalOptions )
  947.     {
  948.         DemoURL.AddOption(*Option);
  949.     }
  950.  
  951.     const FName NAME_DemoNetDriver( TEXT( "DemoNetDriver" ) );
  952.  
  953.     if ( !GEngine->CreateNamedNetDriver( CurrentWorld, NAME_DemoNetDriver, NAME_DemoNetDriver ) )
  954.     {
  955.         UE_LOG(LogDemo, Warning, TEXT( "PlayReplay: failed to create demo net driver!" ) );
  956.         return;
  957.     }
  958.  
  959.     CurrentWorld->DemoNetDriver = Cast< UDemoNetDriver >( GEngine->FindNamedNetDriver( CurrentWorld, NAME_DemoNetDriver ) );
  960.  
  961.     check( CurrentWorld->DemoNetDriver != NULL );
  962.  
  963.     CurrentWorld->DemoNetDriver->SetWorld( CurrentWorld );
  964.  
  965.     FString Error;
  966.  
  967.     if ( !CurrentWorld->DemoNetDriver->InitConnect( CurrentWorld, DemoURL, Error ) )
  968.     {
  969.         UE_LOG(LogDemo, Warning, TEXT( "Demo playback failed: %s" ), *Error );
  970.         CurrentWorld->DestroyDemoNetDriver();
  971.     }
  972.     else
  973.     {
  974.         FCoreUObjectDelegates::PostDemoPlay.Broadcast();
  975.     }
  976. }
  977.  
  978. void UGameInstance::AddUserToReplay(const FString& UserString)
  979. {
  980.     UWorld* CurrentWorld = GetWorld();
  981.  
  982.     if ( CurrentWorld != nullptr && CurrentWorld->DemoNetDriver != nullptr )
  983.     {
  984.         CurrentWorld->DemoNetDriver->AddUserToReplay( UserString );
  985.     }
  986. }
  987.  
  988. TSubclassOf<UOnlineSession> UGameInstance::GetOnlineSessionClass()
  989. {
  990.     return UOnlineSession::StaticClass();
  991. }
  992.  
  993. bool UGameInstance::IsDedicatedServerInstance() const
  994. {
  995.     if (IsRunningDedicatedServer())
  996.     {
  997.         return true;
  998.     }
  999.     else
  1000.     {
  1001.         return WorldContext ? WorldContext->RunAsDedicated : false;
  1002.     }
  1003. }
  1004.  
  1005. FName UGameInstance::GetOnlinePlatformName() const
  1006. {
  1007.     return UOnlineEngineInterface::Get()->GetDefaultOnlineSubsystemName();
  1008. }
  1009.  
  1010. bool UGameInstance::ClientTravelToSession(int32 ControllerId, FName InSessionName)
  1011. {
  1012.     UWorld* World = GetWorld();
  1013.  
  1014.     FString URL;
  1015.     if (UOnlineEngineInterface::Get()->GetResolvedConnectString(World, InSessionName, URL))
  1016.     {
  1017.         ULocalPlayer* LP = GEngine->GetLocalPlayerFromControllerId(World, ControllerId);
  1018.         APlayerController* PC = LP ? LP->PlayerController : nullptr;
  1019.         if (PC)
  1020.         {
  1021.             PC->ClientTravel(URL, TRAVEL_Absolute);
  1022.             return true;
  1023.         }
  1024.         else
  1025.         {
  1026.             UE_LOG(LogGameSession, Warning, TEXT("Failed to find local player for controller id %d"), ControllerId);
  1027.         }
  1028.     }
  1029.     else
  1030.     {
  1031.         UE_LOG(LogGameSession, Warning, TEXT("Failed to resolve session connect string for %s"), *InSessionName.ToString());
  1032.     }
  1033.  
  1034.     return false;
  1035. }
  1036.  
  1037. void UGameInstance::NotifyPreClientTravel(const FString& PendingURL, ETravelType TravelType, bool bIsSeamlessTravel)
  1038. {
  1039.     OnNotifyPreClientTravel().Broadcast(PendingURL, TravelType, bIsSeamlessTravel);
  1040. }
  1041.  
  1042. void UGameInstance::PreloadContentForURL(FURL InURL)
  1043. {
  1044.     // Preload game mode and other content if needed here
  1045. }
  1046.  
  1047. AGameModeBase* UGameInstance::CreateGameModeForURL(FURL InURL)
  1048. {
  1049.     UWorld* World = GetWorld();
  1050.     // Init the game info.
  1051.     FString Options(TEXT(""));
  1052.     TCHAR GameParam[256] = TEXT("");
  1053.     FString Error = TEXT("");
  1054.     AWorldSettings* Settings = World->GetWorldSettings();
  1055.     for (int32 i = 0; i < InURL.Op.Num(); i++)
  1056.     {
  1057.         Options += TEXT("?");
  1058.         Options += InURL.Op[i];
  1059.         FParse::Value(*InURL.Op[i], TEXT("GAME="), GameParam, ARRAY_COUNT(GameParam));
  1060.     }
  1061.  
  1062.     UGameEngine* const GameEngine = Cast<UGameEngine>(GEngine);
  1063.  
  1064.     // Get the GameMode class. Start by using the default game type specified in the map's worldsettings.  It may be overridden by settings below.
  1065.     TSubclassOf<AGameModeBase> GameClass = Settings->DefaultGameMode;
  1066.  
  1067.     // If there is a GameMode parameter in the URL, allow it to override the default game type
  1068.     if (GameParam[0])
  1069.     {
  1070.         FString const GameClassName = UGameMapsSettings::GetGameModeForName(FString(GameParam));
  1071.  
  1072.         // If the gamename was specified, we can use it to fully load the pergame PreLoadClass packages
  1073.         if (GameEngine)
  1074.         {
  1075.             GameEngine->LoadPackagesFully(World, FULLYLOAD_Game_PreLoadClass, *GameClassName);
  1076.         }
  1077.  
  1078.         // Don't overwrite the map's world settings if we failed to load the value off the command line parameter
  1079.         TSubclassOf<AGameModeBase> GameModeParamClass = LoadClass<AGameModeBase>(nullptr, *GameClassName);
  1080.         if (GameModeParamClass)
  1081.         {
  1082.             GameClass = GameModeParamClass;
  1083.         }
  1084.         else
  1085.         {
  1086.             UE_LOG(LogLoad, Warning, TEXT("Failed to load game mode '%s' specified by URL options."), *GameClassName);
  1087.         }
  1088.     }
  1089.  
  1090.     // Next try to parse the map prefix
  1091.     if (!GameClass)
  1092.     {
  1093.         FString MapName = InURL.Map;
  1094.         FString MapNameNoPath = FPaths::GetBaseFilename(MapName);
  1095.         if (MapNameNoPath.StartsWith(PLAYWORLD_PACKAGE_PREFIX))
  1096.         {
  1097.             const int32 PrefixLen = UWorld::BuildPIEPackagePrefix(WorldContext->PIEInstance).Len();
  1098.             MapNameNoPath = MapNameNoPath.Mid(PrefixLen);
  1099.         }
  1100.  
  1101.         FString const GameClassName = UGameMapsSettings::GetGameModeForMapName(FString(MapNameNoPath));
  1102.  
  1103.         if (!GameClassName.IsEmpty())
  1104.         {
  1105.             if (GameEngine)
  1106.             {
  1107.                 GameEngine->LoadPackagesFully(World, FULLYLOAD_Game_PreLoadClass, *GameClassName);
  1108.             }
  1109.  
  1110.             TSubclassOf<AGameModeBase> GameModeParamClass = LoadClass<AGameModeBase>(nullptr, *GameClassName);
  1111.             if (GameModeParamClass)
  1112.             {
  1113.                 GameClass = GameModeParamClass;
  1114.             }
  1115.             else
  1116.             {
  1117.                 UE_LOG(LogLoad, Warning, TEXT("Failed to load game mode '%s' specified by prefixed map name %s."), *GameClassName, *MapNameNoPath);
  1118.             }
  1119.         }
  1120.     }
  1121.  
  1122.     // Fall back to game default
  1123.     if (!GameClass)
  1124.     {
  1125.         GameClass = LoadClass<AGameModeBase>(nullptr, *UGameMapsSettings::GetGlobalDefaultGameMode());
  1126.     }
  1127.  
  1128.     if (!GameClass)
  1129.     {
  1130.         // Fall back to raw GameMode
  1131.         GameClass = AGameModeBase::StaticClass();
  1132.     }
  1133.     else
  1134.     {
  1135.         // See if game instance wants to override it
  1136.         GameClass = OverrideGameModeClass(GameClass, FPaths::GetBaseFilename(InURL.Map), Options, *InURL.Portal);
  1137.     }
  1138.  
  1139.     // no matter how the game was specified, we can use it to load the PostLoadClass packages
  1140.     if (GameEngine)
  1141.     {
  1142.         GameEngine->LoadPackagesFully(World, FULLYLOAD_Game_PostLoadClass, GameClass->GetPathName());
  1143.         GameEngine->LoadPackagesFully(World, FULLYLOAD_Game_PostLoadClass, TEXT("LoadForAllGameModes"));
  1144.     }
  1145.  
  1146.     // Spawn the GameMode.
  1147.     UE_LOG(LogLoad, Log, TEXT("Game class is '%s'"), *GameClass->GetName());
  1148.     FActorSpawnParameters SpawnInfo;
  1149.     SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
  1150.     SpawnInfo.ObjectFlags |= RF_Transient;  // We never want to save game modes into a map
  1151.  
  1152.     return World->SpawnActor<AGameModeBase>(GameClass, SpawnInfo);
  1153. }
  1154.  
  1155. TSubclassOf<AGameModeBase> UGameInstance::OverrideGameModeClass(TSubclassOf<AGameModeBase> GameModeClass, const FString& MapName, const FString& Options, const FString& Portal) const
  1156. {
  1157.      return GameModeClass;
  1158. }
Advertisement
Add Comment
Please, Sign In to add comment