I was able to override the engine class, but I’m getting a linker error.
Error lnk2019
0>CppProjectGameEngine.cpp.obj: Error : LNK2019: Unresolved external symbol reference "public: void __cdecl FRegisterComponentContext::Process(void)" (?Process@FRegisterComponentContext@@QEAAXXZ) in function "public: virtual bool __cdecl UCppProjectGameEngine:: LoadMap(struct FWorldContext &,struct FURL,class UPendingNetGame *,class FString &)" (?LoadMap@UCppProjectGameEngine@@UEAA_NAEAUFWorldContext@@UFURL@@PEAVUPendingNetGame@@AEAVFString@@@Z).
0>CppProjectGameEngine.cpp.obj: Error : LNK2019: Unresolved external symbol reference "public: void __cdecl FSeamlessTravelHandler::CancelTravel(void)" (?CancelTravel@FSeamlessTravelHandler@@QEAAXXZ) in function "public: virtual bool __cdecl UCppProjectGameEngine:: LoadMap(struct FWorldContext &,struct FURL,class UPendingNetGame *,class FString &)" (?LoadMap@UCppProjectGameEngine@@UEAA_NAEAUFWorldContext@@UFURL@@PEAVUPendingNetGame@@AEAVFString@@@Z).
0>G:\GameDev\WIP\CppProject\Binaries\Win64\UE4Editor-CppProject.dll: Error : LNK1120: Unresolved externals: 2
Build failed at 18:14:21
My .Build.cs
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject", "Engine", "UnrealEd",
"UMG", "MoviePlayer", "InputCore", "NetCore",
"Networking", "RHI", "RenderCore", "AppFramework",
"TraceLog", "NetCommon", "Renderer"
});
PrivateDependencyModuleNames.AddRange(new string[] {});
PrivateDependencyModuleNames.AddRange(new string[] {
"Slate", "SlateCore", "MoviePlayer",
"MovieScene", "UMG", "DeveloperSettings"
});
Two things that I can’t include in the project.
FRegisterComponentContext
FSeamlessTravelHandler
My .cpp class
#include "CppProjectGameEngine.h"
#include "AudioDevice.h"
//#include "Engine.h"
#include "EngineUtils.h"
#include "ShaderCompiler.h"
#include "Engine/LocalPlayer.h"
#include "GameFramework/GameModeBase.h"
#include "HAL/ThreadHeartBeat.h"
#include "Containers/IndirectArray.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Object.h"
#include "Misc/Guid.h"
#include "Templates/SubclassOf.h"
#include "Engine/EngineTypes.h"
#include "Engine/EngineBaseTypes.h"
#include "UObject/SoftObjectPath.h"
#include "Engine/Level.h"
#include "Misc/BufferedOutputDevice.h"
#include "Misc/FrameRate.h"
#include "Subsystems/SubsystemCollection.h"
#include "Subsystems/EngineSubsystem.h"
#include "RHI.h"
#include "AudioDeviceManager.h"
#include "Engine/LevelStreaming.h"
#include "Templates/UniqueObj.h"
// Should we TrimMemory in the middle of LoadMap - this can reduce memory spike during startup at the expense of load time
// if some objects need to be reloaded due to a GC happening partway through
int32 GDelayTrimMemoryDuringMapLoadMode = 0;
static FAutoConsoleVariableRef GDelayTrimMemoryDuringMapLoadModeCVar(
TEXT("Engine.DelayTrimMemoryDuringMapLoadMode"),
GDelayTrimMemoryDuringMapLoadMode,
TEXT("0: TrimMemory during LoadMap as normal\n")
TEXT("1: Delay TrimMemory until the end of LoadMap (initial boot up)\n")
TEXT("2: Delay TrimMemory in _every_ LoadMap call"),
ECVF_Default
);
//struct FWorldContext;
namespace
{
/**
* Attempts to set process limits as configured in Engine.ini or elsewhere.
* Assumed to be called during initialization.
*/
void SetConfiguredProcessLimits()
{
int32 VirtualMemoryLimitInKB = 0;
if (GConfig)
{
GConfig->GetInt(TEXT("ProcessLimits"), TEXT("VirtualMemoryLimitInKB"), VirtualMemoryLimitInKB, GEngineIni);
}
// command line parameters take precendence
FParse::Value(FCommandLine::Get(), TEXT("virtmemkb="), VirtualMemoryLimitInKB);
if (VirtualMemoryLimitInKB > 0)
{
UE_LOG(LogInit, Display, TEXT("Limiting process virtual memory size to %d KB"), VirtualMemoryLimitInKB);
if (!FPlatformProcess::SetProcessLimits(EProcessResource::VirtualMemory, static_cast< uint64 >(VirtualMemoryLimitInKB) * 1024))
{
UE_LOG(LogInit, Fatal, TEXT("Could not limit process virtual memory usage to %d KB"), VirtualMemoryLimitInKB);
}
}
}
UWorld* CreatePIEWorldByLoadingFromPackage(const FWorldContext& WorldContext, const FString& SourceWorldPackage, UPackage*& OutPackage)
{
// Load map from the disk in case editor does not have it
const FString PIEPackageName = UWorld::ConvertToPIEPackageName(SourceWorldPackage, WorldContext.PIEInstance);
// Set the world type in the static map, so that UWorld::PostLoad can set the world type
const FName PIEPackageFName = FName(*PIEPackageName);
UWorld::WorldTypePreLoadMap.FindOrAdd( PIEPackageFName ) = WorldContext.WorldType;
FSoftObjectPath::AddPIEPackageName(PIEPackageFName);
uint32 LoadFlags = LOAD_None;
UPackage* NewPackage = CreatePackage( *PIEPackageName);
if (NewPackage != nullptr && WorldContext.WorldType == EWorldType::PIE)
{
NewPackage->SetPackageFlags(PKG_PlayInEditor);
LoadFlags |= LOAD_PackageForPIE;
}
OutPackage = LoadPackage(NewPackage, *SourceWorldPackage, LoadFlags);
// Clean up the world type list now that PostLoad has occurred
UWorld::WorldTypePreLoadMap.Remove( PIEPackageFName );
if (OutPackage == nullptr)
{
return nullptr;
}
UWorld* NewWorld = UWorld::FindWorldInPackage(OutPackage);
// If the world was not found, follow a redirector if there is one.
if ( !NewWorld )
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(OutPackage);
if ( NewWorld )
{
OutPackage = NewWorld->GetOutermost();
}
}
check(NewWorld);
OutPackage->PIEInstanceID = WorldContext.PIEInstance;
OutPackage->SetPackageFlags(PKG_PlayInEditor);
// Rename streaming levels to PIE
for (ULevelStreaming* StreamingLevel : NewWorld->GetStreamingLevels())
{
StreamingLevel->RenameForPIE(WorldContext.PIEInstance);
}
return NewWorld;
}
}
bool UCppProjectGameEngine::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
return Super::Exec(InWorld, Cmd, Ar);
}
void UCppProjectGameEngine::Start()
{
Super::Start();
}
EBrowseReturnVal::Type UCppProjectGameEngine::Browse(FWorldContext& WorldContext, FURL URL, FString& Error)
{
return Super::Browse(WorldContext, URL, Error);
}
bool UCppProjectGameEngine::LoadMap(FWorldContext& WorldContext, FURL URL, UPendingNetGame* Pending, FString& Error)
{
//TRACE_LOADTIME_REQUEST_GROUP_SCOPE(TEXT("LoadMap - %s"), *URL.Map);
STAT_ADD_CUSTOMMESSAGE_NAME( STAT_NamedMarker, *(FString( TEXT( "LoadMap - " ) + URL.Map )) );
TRACE_BOOKMARK(TEXT("LoadMap - %s"), *URL.Map);
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UEngine::LoadMap"), STAT_LoadMap, STATGROUP_LoadTime);
LLM_SCOPE(ELLMTag::LoadMapMisc);
FDisableHitchDetectorScope SuspendHitchDetector;
//NETWORK_PROFILER(GNetworkProfiler.TrackSessionChange(true,URL));
MALLOC_PROFILER( FMallocProfiler::SnapshotMemoryLoadMapStart( URL.Map ) );
Error = TEXT("");
//FLoadTimeTracker::Get().ResetRawLoadTimes();
// make sure level streaming isn't frozen
if (WorldContext.World())
{
WorldContext.World()->bIsLevelStreamingFrozen = false;
}
// send a callback message
FCoreUObjectDelegates::PreLoadMap.Broadcast(URL.Map);
// make sure there is a matching PostLoadMap() no matter how we exit
struct FPostLoadMapCaller
{
FPostLoadMapCaller()
: bCalled(false)
{}
~FPostLoadMapCaller()
{
if (!bCalled)
{
FCoreUObjectDelegates::PostLoadMapWithWorld.Broadcast(nullptr);
}
}
void Broadcast(UWorld* World)
{
if (ensure(!bCalled))
{
bCalled = true;
FCoreUObjectDelegates::PostLoadMapWithWorld.Broadcast(World);
}
}
private:
bool bCalled;
} PostLoadMapCaller;
// Cancel any pending texture streaming requests. This avoids a significant delay on consoles
// when loading a map and there are a lot of outstanding texture streaming requests from the previous map.
UTexture2D::CancelPendingTextureStreaming();
// play a load map movie if specified in ini
bStartedLoadMapMovie = false;
if (WorldContext.World() && WorldContext.World()->PersistentLevel)
{
CleanupPackagesToFullyLoad(WorldContext, FULLYLOAD_Map, WorldContext.World()->PersistentLevel->GetOutermost()->GetName());
}
// cleanup the existing per-game pacakges
// @todo: It should be possible to not unload/load packages if we are going from/to the same GameMode.
// would have to save the game pathname here and pass it in to SetGameMode below
CleanupPackagesToFullyLoad(WorldContext, FULLYLOAD_Game_PreLoadClass, TEXT(""));
CleanupPackagesToFullyLoad(WorldContext, FULLYLOAD_Game_PostLoadClass, TEXT(""));
CleanupPackagesToFullyLoad(WorldContext, FULLYLOAD_Mutator, TEXT(""));
// Cancel any pending async map changes after flushing async loading. We flush async loading before canceling the map change
// to avoid completion after cancellation to not leave references to the "to be changed to" level around. Async loading is
// implicitly flushed again later on during garbage collection.
FlushAsyncLoading();
CancelPendingMapChange(WorldContext);
WorldContext.SeamlessTravelHandler.CancelTravel();
double StartTime = FPlatformTime::Seconds();
UE_LOG(LogLoad, Log, TEXT("LoadMap: %s"), *URL.ToString() );
GInitRunaway();
#if !UE_BUILD_SHIPPING
const bool bOldWorldWasShowingCollisionForHiddenComponents = WorldContext.World() && WorldContext.World()->bCreateRenderStateForHiddenComponentsWithCollsion;
#endif
// Unload the current world
if( WorldContext.World() )
{
WorldContext.World()->BeginTearingDown();
if(!URL.HasOption(TEXT("quiet")) )
{
TransitionType = ETransitionType::Loading;
TransitionDescription = URL.Map;
if (URL.HasOption(TEXT("Game=")))
{
TransitionGameMode = URL.GetOption(TEXT("Game="), TEXT(""));
}
else
{
TransitionGameMode = TEXT("");
}
// Display loading screen.
// Check if a loading movie is playing. If so it is not safe to redraw the viewport due to potential race conditions with font rendering
bool bIsLoadingMovieCurrentlyPlaying = FCoreDelegates::IsLoadingMovieCurrentlyPlaying.IsBound() ? FCoreDelegates::IsLoadingMovieCurrentlyPlaying.Execute() : false;
if(!bIsLoadingMovieCurrentlyPlaying)
{
LoadMapRedrawViewports();
}
TransitionType = ETransitionType::None;
}
// Clean up networking
ShutdownWorldNetDriver(WorldContext.World());
// Make sure there are no pending visibility requests.
WorldContext.World()->FlushLevelStreaming(EFlushLevelStreamingType::Visibility);
// send a message that all levels are going away (NULL means every sublevel is being removed
// without a call to RemoveFromWorld for each)
//if (WorldContext.World()->GetNumLevels() > 1)
{
// TODO: Consider actually broadcasting for each level?
FWorldDelegates::LevelRemovedFromWorld.Broadcast(nullptr, WorldContext.World());
}
// Disassociate the players from their PlayerControllers in this world.
if (WorldContext.OwningGameInstance != nullptr)
{
for(auto It = WorldContext.OwningGameInstance->GetLocalPlayerIterator(); It; ++It)
{
ULocalPlayer *Player = *It;
if(Player->PlayerController && Player->PlayerController->GetWorld() == WorldContext.World())
{
if(Player->PlayerController->GetPawn())
{
WorldContext.World()->DestroyActor(Player->PlayerController->GetPawn(), true);
}
WorldContext.World()->DestroyActor(Player->PlayerController, true);
Player->PlayerController = nullptr;
}
// reset split join info so we'll send one after loading the new map if necessary
Player->bSentSplitJoin = false;
// When loading maps, clear out mids that are referenced as they may prevent the world from shutting down cleanly and the local player will not be cleaned up until later
Player->CleanupViewState();
}
}
for (FActorIterator ActorIt(WorldContext.World()); ActorIt; ++ActorIt)
{
ActorIt->RouteEndPlay(EEndPlayReason::LevelTransition);
}
// Do this after destroying pawns/playercontrollers, in case that spawns new things (e.g. dropped weapons)
WorldContext.World()->CleanupWorld();
if( GEngine )
{
// clear any "DISPLAY" properties referencing level objects
if (GEngine->GameViewport != nullptr)
{
ClearDebugDisplayProperties();
}
GEngine->WorldDestroyed(WorldContext.World());
}
WorldContext.World()->RemoveFromRoot();
// mark everything else contained in the world to be deleted
for (auto LevelIt(WorldContext.World()->GetLevelIterator()); LevelIt; ++LevelIt)
{
const ULevel* Level = *LevelIt;
if (Level)
{
CastChecked<UWorld>(Level->GetOuter())->MarkObjectsPendingKill();
}
}
for (ULevelStreaming* LevelStreaming : WorldContext.World()->GetStreamingLevels())
{
// If an unloaded levelstreaming still has a loaded level we need to mark its objects to be deleted as well
if (LevelStreaming->GetLoadedLevel() && (!LevelStreaming->ShouldBeLoaded() || !LevelStreaming->ShouldBeVisible()))
{
CastChecked<UWorld>(LevelStreaming->GetLoadedLevel()->GetOuter())->MarkObjectsPendingKill();
}
}
// Stop all audio to remove references to current level.
if (FAudioDevice* AudioDevice = WorldContext.World()->GetAudioDeviceRaw())
{
AudioDevice->Flush(WorldContext.World());
AudioDevice->SetTransientMasterVolume(1.0f);
}
WorldContext.SetCurrentWorld(nullptr);
}
// trim memory to clear up allocations from the previous level (also flushes rendering)
if (GDelayTrimMemoryDuringMapLoadMode == 0)
{
TrimMemory();
}
// Cancels the Forced StreamType for textures using a timer.
if (!IStreamingManager::HasShutdown())
{
IStreamingManager::Get().CancelForcedResources();
}
if (FPlatformProperties::RequiresCookedData())
{
appDefragmentTexturePool();
appDumpTextureMemoryStats(TEXT(""));
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// if we aren't trimming memory above, then the world won't be fully cleaned up at this point, so don't bother checking
if (GDelayTrimMemoryDuringMapLoadMode == 0)
{
// Dump info
VerifyLoadMapWorldCleanup();
}
#endif
MALLOC_PROFILER( FMallocProfiler::SnapshotMemoryLoadMapMid( URL.Map ); )
WorldContext.OwningGameInstance->PreloadContentForURL(URL);
UPackage* WorldPackage = NULL;
UWorld* NewWorld = NULL;
// If this world is a PIE instance, we need to check if we are traveling to another PIE instance's world.
// If we are, we need to set the PIERemapPrefix so that we load a copy of that world, instead of loading the
// PIE world directly.
if (!WorldContext.PIEPrefix.IsEmpty())
{
for (const FWorldContext& WorldContextFromList : WorldList)
{
// We want to ignore our own PIE instance so that we don't unnecessarily set the PIERemapPrefix if we are not traveling to
// a server.
if (WorldContextFromList.World() != WorldContext.World())
{
if (!WorldContextFromList.PIEPrefix.IsEmpty() && URL.Map.Contains(WorldContextFromList.PIEPrefix))
{
FString SourceWorldPackage = UWorld::RemovePIEPrefix(URL.Map);
// We are loading a new world for this context, so clear out PIE fixups that might be lingering.
// (note we dont want to do this in DuplicateWorldForPIE, since that is also called on streaming worlds.
GPlayInEditorID = WorldContext.PIEInstance;
UpdatePlayInEditorWorldDebugString(&WorldContext);
FLazyObjectPtr::ResetPIEFixups();
NewWorld = UWorld::DuplicateWorldForPIE(SourceWorldPackage, nullptr);
if (NewWorld == nullptr)
{
NewWorld = CreatePIEWorldByLoadingFromPackage(WorldContext, SourceWorldPackage, WorldPackage);
if (NewWorld == nullptr)
{
Error = FString::Printf(TEXT("Failed to load package '%s' while in PIE"), *SourceWorldPackage);
return false;
}
}
else
{
WorldPackage = CastChecked<UPackage>(NewWorld->GetOuter());
}
NewWorld->StreamingLevelsPrefix = UWorld::BuildPIEPackagePrefix(WorldContext.PIEInstance);
GIsPlayInEditorWorld = true;
}
}
}
}
// Is this a minimal net RPC world?
if (WorldContext.WorldType == EWorldType::GameRPC)
{
UGameInstance::CreateMinimalNetRPCWorld(*URL.Map, WorldPackage, NewWorld);
}
const FString URLTrueMapName = URL.Map;
// Normal map loading
if (NewWorld == NULL)
{
// Set the world type in the static map, so that UWorld::PostLoad can set the world type
const FName URLMapFName = FName(*URL.Map);
UWorld::WorldTypePreLoadMap.FindOrAdd( URLMapFName ) = WorldContext.WorldType;
// See if the level is already in memory
WorldPackage = FindPackage(nullptr, *URL.Map);
bool bPackageAlreadyLoaded = (WorldPackage != nullptr);
// If the level isn't already in memory, load level from disk
if (WorldPackage == nullptr)
{
WorldPackage = LoadPackage(nullptr, *URL.Map, (WorldContext.WorldType == EWorldType::PIE ? LOAD_PackageForPIE : LOAD_None));
}
// Clean up the world type list now that PostLoad has occurred
UWorld::WorldTypePreLoadMap.Remove( URLMapFName );
if (WorldPackage == nullptr)
{
// it is now the responsibility of the caller to deal with a NULL return value and alert the user if necessary
Error = FString::Printf(TEXT("Failed to load package '%s'"), *URL.Map);
return false;
}
// Find the newly loaded world.
NewWorld = UWorld::FindWorldInPackage(WorldPackage);
// If the world was not found, it could be a redirector to a world. If so, follow it to the destination world.
if ( !NewWorld )
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(WorldPackage);
if ( NewWorld )
{
// Treat this as an already loaded package because we were loaded by the redirector
bPackageAlreadyLoaded = true;
WorldPackage = NewWorld->GetOutermost();
}
}
// This can still be null if the package name is ambiguous, for example if there exists a umap and uasset with the same
// name.
if (NewWorld == nullptr)
{
// it is now the responsibility of the caller to deal with a NULL return value and alert the user if necessary
Error = FString::Printf(TEXT("Failed to load package '%s'"), *URL.Map);
return false;
}
NewWorld->PersistentLevel->HandleLegacyMapBuildData();
FScopeCycleCounterUObject MapScope(WorldPackage);
if (WorldContext.WorldType == EWorldType::PIE)
{
// If we are a PIE world and the world we just found is already initialized, then we're probably reloading the editor world and we
// need to create a PIE world by duplication instead
if (bPackageAlreadyLoaded || NewWorld->WorldType == EWorldType::Editor)
{
if (WorldContext.PIEInstance == -1)
{
// Assume if we get here, that it's safe to just give a PIE instance so that we can duplicate the world
// If we won't duplicate the world, we'll refer to the existing world (most likely the editor version, and it can be modified under our feet, which is bad)
// So far, the only known way to get here is when we use the console "open" command while in a client PIE instance connected to non PIE server
// (i.e. multi process PIE where client is in current editor process, and dedicated server was launched as separate process)
WorldContext.PIEInstance = 0;
}
NewWorld = CreatePIEWorldByDuplication(WorldContext, NewWorld, URL.Map);
// CreatePIEWorldByDuplication clears GIsPlayInEditorWorld so set it again
GIsPlayInEditorWorld = true;
}
// Otherwise we are probably loading new map while in PIE, so we need to rename world package and all streaming levels
else if (WorldContext.PIEInstance != -1 && ((Pending == nullptr) || (Pending->GetDemoNetDriver() != nullptr)))
{
NewWorld->RenameToPIEWorld(WorldContext.PIEInstance);
}
ResetPIEAudioSetting(NewWorld);
#if WITH_EDITOR
// PIE worlds should use the same feature level as the editor
if (WorldContext.PIEWorldFeatureLevel != ERHIFeatureLevel::Num && NewWorld->FeatureLevel != WorldContext.PIEWorldFeatureLevel)
{
NewWorld->ChangeFeatureLevel(WorldContext.PIEWorldFeatureLevel);
}
#endif
}
else if (WorldContext.WorldType == EWorldType::Game)
{
// If we are a game world and the world we just found is already initialized, then we're probably trying to load
// an independent fresh copy of the world into a different context. Create a package with a prefixed name
// and load the world from disk to keep the instances independent. If this is the case, assume the creator
// of the FWorldContext was aware and set WorldContext.PIEInstance to a valid value.
if (NewWorld->bIsWorldInitialized && WorldContext.PIEInstance != -1)
{
NewWorld = CreatePIEWorldByLoadingFromPackage(WorldContext, URL.Map, WorldPackage);
if (NewWorld == nullptr)
{
Error = FString::Printf(TEXT("Failed to load package '%s' into a new game world."), *URL.Map);
return false;
}
}
}
}
NewWorld->SetGameInstance(WorldContext.OwningGameInstance);
GWorld = NewWorld;
WorldContext.SetCurrentWorld(NewWorld);
WorldContext.World()->WorldType = WorldContext.WorldType;
#if !UE_BUILD_SHIPPING
GWorld->bCreateRenderStateForHiddenComponentsWithCollsion = bOldWorldWasShowingCollisionForHiddenComponents;
#endif
// Fixme: hacky but we need to set PackageFlags here if we are in a PIE Context.
// Also, don't add to root when in PIE, since PIE doesn't remove world from root
if (WorldContext.WorldType == EWorldType::PIE)
{
check(WorldContext.World()->GetOutermost()->HasAnyPackageFlags(PKG_PlayInEditor));
WorldContext.World()->ClearFlags(RF_Standalone);
}
else
{
WorldContext.World()->AddToRoot();
}
// In the PIE case the world will already have been initialized as part of CreatePIEWorldByDuplication
if (!WorldContext.World()->bIsWorldInitialized)
{
WorldContext.World()->InitWorld();
}
// Handle pending level.
if( Pending )
{
check(Pending == WorldContext.PendingNetGame);
MovePendingLevel(WorldContext);
}
else
{
check(!WorldContext.World()->GetNetDriver());
}
WorldContext.World()->SetGameMode(URL);
if (FAudioDevice* AudioDevice = WorldContext.World()->GetAudioDeviceRaw())
{
AudioDevice->SetDefaultBaseSoundMix(WorldContext.World()->GetWorldSettings()->DefaultBaseSoundMix);
}
// Listen for clients.
if (Pending == NULL && (!GIsClient || URL.HasOption(TEXT("Listen"))))
{
if (!WorldContext.World()->Listen(URL))
{
UE_LOG(LogNet, Error, TEXT("LoadMap: failed to Listen(%s)"), *URL.ToString());
}
}
const TCHAR* MutatorString = URL.GetOption(TEXT("Mutator="), TEXT(""));
if (MutatorString)
{
TArray<FString> Mutators;
FString(MutatorString).ParseIntoArray(Mutators, TEXT(","), true);
for (int32 MutatorIndex = 0; MutatorIndex < Mutators.Num(); MutatorIndex++)
{
LoadPackagesFully(WorldContext.World(), FULLYLOAD_Mutator, Mutators[MutatorIndex]);
}
}
// Process global shader results before we try to render anything
// Do this before we register components, as USkinnedMeshComponents require the GPU skin cache global shaders when creating render state.
if (GShaderCompilingManager)
{
GShaderCompilingManager->ProcessAsyncResults(false, true);
}
{
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UEngine::LoadMap.LoadPackagesFully"), STAT_LoadMap_LoadPackagesFully, STATGROUP_LoadTime);
// load any per-map packages
check(WorldContext.World()->PersistentLevel);
LoadPackagesFully(WorldContext.World(), FULLYLOAD_Map, WorldContext.World()->PersistentLevel->GetOutermost()->GetName());
// Make sure "always loaded" sub-levels are fully loaded
WorldContext.World()->FlushLevelStreaming(EFlushLevelStreamingType::Visibility);
if (!GIsEditor && !IsRunningDedicatedServer())
{
// If requested, duplicate dynamic levels here after the source levels are created.
WorldContext.World()->DuplicateRequestedLevels(FName(*URL.Map));
}
}
// Note that AI system will be created only if ai-system-creation conditions are met
WorldContext.World()->CreateAISystem();
// Initialize gameplay for the level.
{
FRegisterComponentContext Context(WorldContext.World());
WorldContext.World()->InitializeActorsForPlay(URL, true, &Context);
Context.Process();
}
// calling it after InitializeActorsForPlay has been called to have all potential bounding boxed initialized
FNavigationSystem::AddNavigationSystemToWorld(*WorldContext.World(), FNavigationSystemRunMode::GameMode);
// Remember the URL. Put this before spawning player controllers so that
// a player controller can get the map name during initialization and
// have it be correct
WorldContext.LastURL = URL;
WorldContext.LastURL.Map = URLTrueMapName;
if (WorldContext.World()->GetNetMode() == NM_Client)
{
WorldContext.LastRemoteURL = URL;
}
// Spawn play actors for all active local players
if (WorldContext.OwningGameInstance != NULL)
{
for(auto It = WorldContext.OwningGameInstance->GetLocalPlayerIterator(); It; ++It)
{
FString Error2;
if(!(*It)->SpawnPlayActor(URL.ToString(1),Error2,WorldContext.World()))
{
UE_LOG(LogEngine, Fatal, TEXT("Couldn't spawn player: %s"), *Error2);
}
}
}
// Prime texture streaming.
IStreamingManager::Get().NotifyLevelChange();
// if (GEngine && GEngine->XRSystem.IsValid())
// {
// GEngine->XRSystem->OnBeginPlay(WorldContext);
// }
WorldContext.World()->BeginPlay();
// send a callback message
PostLoadMapCaller.Broadcast(WorldContext.World());
WorldContext.World()->bWorldWasLoadedThisTick = true;
// We want to update streaming immediately so that there's no tick prior to processing any levels that should be initially visible
// that requires calculating the scene, so redraw everything now to take care of it all though don't present the frame.
RedrawViewports(false);
// RedrawViewports() may have added a dummy playerstart location. Remove all views to start from fresh the next Tick().
//IStreamingManager::Get().RemoveStreamingViews( RemoveStreamingViews_All );
// See if we need to record network demos
if ( WorldContext.World()->GetAuthGameMode() == NULL || !WorldContext.World()->GetAuthGameMode()->IsHandlingReplays() )
{
if ( URL.HasOption( TEXT( "DemoRec" ) ) && WorldContext.OwningGameInstance != nullptr )
{
const TCHAR* DemoRecName = URL.GetOption( TEXT( "DemoRec=" ), NULL );
// Record the demo, optionally with the specified custom name.
WorldContext.OwningGameInstance->StartRecordingReplay( FString(DemoRecName), WorldContext.World()->GetMapName(), URL.Op );
}
}
STAT_ADD_CUSTOMMESSAGE_NAME( STAT_NamedMarker, *(FString( TEXT( "LoadMapComplete - " ) + URL.Map )) );
TRACE_BOOKMARK(TEXT("LoadMapComplete - %s"), *URL.Map);
MALLOC_PROFILER( FMallocProfiler::SnapshotMemoryLoadMapEnd( URL.Map ); )
double StopTime = FPlatformTime::Seconds();
UE_LOG(LogLoad, Log, TEXT("Took %f seconds to LoadMap(%s)"), StopTime - StartTime, *URL.Map);
//FLoadTimeTracker::Get().DumpRawLoadTimes();
WorldContext.OwningGameInstance->LoadComplete(StopTime - StartTime, *URL.Map);
// perform the delayed TrimMemory if desired
if (GDelayTrimMemoryDuringMapLoadMode != 0)
{
TrimMemory();
if (GDelayTrimMemoryDuringMapLoadMode == 1)
{
// all future map loads should be normal
GDelayTrimMemoryDuringMapLoadMode = 0;
}
}
// Successfully started local level.
return true;
//return Super::LoadMap(WorldContext, URL, Pending, Error);
}
All I did was copy the code from the engine. Moreover, if you comment out problem areas, then it even starts and gathers. But I would like to keep these 2 components and include them.
I am in a bit of despair. I checked a lot of information but did not find anything that solved my problem. Because I really hope to override the level loading for my game.