You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
LIMITATIONS: 1) Re-instancing will only update UClass instance data. 2) Adding and removing properties should only be done towards the end of a class or structure and can not be followed by complex data types. 3) Adding and removing properties from a base class should not be done if a derived class contains complex data types. KNOWN ISSUES: 1) Changes to enumerations and structures will not be reflected in existing blueprints. However, adding new nodes to the blueprint will show the updated enumeration or structure. 2) If a class contains an enumeration or structure as a member, the class will not be re-instanced if enumeration or structure is changed. CHANGES: 1) LiveCodingServer 1a) Modified to always execute certain static instances during load. 1b) Modified to exclude the _Statics static structures to avoid patching to old copies. 2) Added support for LiveCoding reinstancing 2a) Refactored deferred registration system for UClass, UEnum, and UScriptStruct to use a common system that works for normal game, hot reload and live coding. 2b) Type specific version check data is possible (i.e. enum doesn't have a size) 2c) Single registration static for UClass 2d) Single registration class for all types that is just a blind forward to API. 2e) Static and dynamic registrations use different API entry points to avoid having overloaded argument lists that just apply to one or the other. 2f) Shims for older API 3) New common "Reload" system to avoid using HotReload code. 3a) Support common delegates regardless of who is reloading/reinstancing. 3b) Re-instancing code moved from HotReload to Kismet2 (where the bulk of the re-instance code already existed). 3c) Modified PyWrapper to use new helper class instead of depending on HotRelaod 3d) Added WITH_RELOAD which is defined if HotReload or LiveCoding is enabled. 3e) Modifed existing code to use new #define and delegates. Robert did the review on the changes covered by Part 2. Remaining changes are all straightforward. #rb robert.manuszewski #jira UE-74493 [CL 15736777 by Tim Smith in ue5-main branch]
642 lines
16 KiB
C++
642 lines
16 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "LiveCodingModule.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Features/IModularFeatures.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "Misc/CoreDelegates.h"
|
|
#include "LiveCodingLog.h"
|
|
#include "External/LC_EntryPoint.h"
|
|
#include "External/LC_API.h"
|
|
#include "Misc/App.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "LiveCodingSettings.h"
|
|
#include "ISettingsModule.h"
|
|
#include "ISettingsSection.h"
|
|
#include "Windows/WindowsHWrapper.h"
|
|
#if WITH_EDITOR
|
|
#include "Kismet2/ReloadUtilities.h"
|
|
#else
|
|
#include "UObject/Reload.h"
|
|
#endif
|
|
#if WITH_ENGINE
|
|
#include "Engine/Engine.h"
|
|
#endif
|
|
|
|
IMPLEMENT_MODULE(FLiveCodingModule, LiveCoding)
|
|
|
|
#define LOCTEXT_NAMESPACE "LiveCodingModule"
|
|
|
|
bool GIsCompileActive = false;
|
|
bool GHasLoadedPatch = false;
|
|
FString GLiveCodingConsolePath;
|
|
FString GLiveCodingConsoleArguments;
|
|
FLiveCodingModule* GLiveCodingModule = nullptr;
|
|
|
|
#if IS_MONOLITHIC
|
|
extern const TCHAR* GLiveCodingEngineDir;
|
|
extern const TCHAR* GLiveCodingProject;
|
|
#endif
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wextern-initializer"
|
|
#endif
|
|
|
|
LPP_PRECOMPILE_HOOK(FLiveCodingModule::PreCompileHook);
|
|
LPP_POSTCOMPILE_HOOK(FLiveCodingModule::PostCompileHook);
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
#if !WITH_EDITOR
|
|
class FNullReload : public IReload
|
|
{
|
|
public:
|
|
FNullReload(FLiveCodingModule& InLiveCodingModule)
|
|
: LiveCodingModule(InLiveCodingModule)
|
|
{
|
|
BeginReload(EActiveReloadType::LiveCoding, *this);
|
|
}
|
|
|
|
~FNullReload()
|
|
{
|
|
EndReload();
|
|
}
|
|
|
|
virtual EActiveReloadType GetType() const
|
|
{
|
|
return EActiveReloadType::LiveCoding;
|
|
}
|
|
|
|
|
|
virtual const TCHAR* GetPrefix() const
|
|
{
|
|
return TEXT("LIVECODING");
|
|
}
|
|
|
|
virtual void NotifyFunctionRemap(FNativeFuncPtr NewFunctionPointer, FNativeFuncPtr OldFunctionPointer)
|
|
{
|
|
}
|
|
|
|
virtual void NotifyChange(UClass* New, UClass* Old) override
|
|
{
|
|
bGotClassChange |= Old != nullptr && Old != New;
|
|
}
|
|
|
|
virtual void NotifyChange(UEnum* New, UEnum* Old) override
|
|
{
|
|
}
|
|
|
|
virtual void NotifyChange(UScriptStruct* New, UScriptStruct* Old) override
|
|
{
|
|
bGotClassChange |= Old != nullptr && Old != New;
|
|
}
|
|
|
|
virtual void Reinstance()
|
|
{
|
|
if (bGotClassChange)
|
|
{
|
|
static const TCHAR* Message = TEXT("Object structure changes detected. LiveCoding re-instancing isn't supported in builds without the editor");
|
|
UE_LOG(LogLiveCoding, Error, TEXT("%s"), Message);
|
|
#if WITH_ENGINE
|
|
if (GEngine)
|
|
{
|
|
GEngine->AddOnScreenDebugMessage(uint64(uintptr_t(&LiveCodingModule)), 5.f, FColor::Red, Message);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private:
|
|
FLiveCodingModule& LiveCodingModule;
|
|
bool bGotClassChange = false;
|
|
};
|
|
#endif
|
|
|
|
FLiveCodingModule::FLiveCodingModule()
|
|
: bEnabledLastTick(false)
|
|
, bEnabledForSession(false)
|
|
, bStarted(false)
|
|
, bUpdateModulesInTick(false)
|
|
, FullEnginePluginsDir(FPaths::ConvertRelativePathToFull(FPaths::EnginePluginsDir()))
|
|
, FullProjectDir(FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()))
|
|
, FullProjectPluginsDir(FPaths::ConvertRelativePathToFull(FPaths::ProjectPluginsDir()))
|
|
{
|
|
GLiveCodingModule = this;
|
|
}
|
|
|
|
FLiveCodingModule::~FLiveCodingModule()
|
|
{
|
|
GLiveCodingModule = nullptr;
|
|
}
|
|
|
|
void FLiveCodingModule::StartupModule()
|
|
{
|
|
Settings = GetMutableDefault<ULiveCodingSettings>();
|
|
|
|
IConsoleManager& ConsoleManager = IConsoleManager::Get();
|
|
|
|
EnableCommand = ConsoleManager.RegisterConsoleCommand(
|
|
TEXT("LiveCoding"),
|
|
TEXT("Enables live coding support"),
|
|
FConsoleCommandDelegate::CreateRaw(this, &FLiveCodingModule::EnableForSession, true),
|
|
ECVF_Cheat
|
|
);
|
|
|
|
CompileCommand = ConsoleManager.RegisterConsoleCommand(
|
|
TEXT("LiveCoding.Compile"),
|
|
TEXT("Initiates a live coding compile"),
|
|
FConsoleCommandDelegate::CreateRaw(this, &FLiveCodingModule::Compile),
|
|
ECVF_Cheat
|
|
);
|
|
|
|
#if IS_MONOLITHIC
|
|
FString DefaultEngineDir = GLiveCodingEngineDir;
|
|
#else
|
|
FString DefaultEngineDir = FPaths::EngineDir();
|
|
#endif
|
|
#if USE_DEBUG_LIVE_CODING_CONSOLE
|
|
static const TCHAR* DefaultConsolePath = TEXT("Binaries/Win64/LiveCodingConsole-Win64-Debug.exe");
|
|
#else
|
|
static const TCHAR* DefaultConsolePath = TEXT("Binaries/Win64/LiveCodingConsole.exe");
|
|
#endif
|
|
ConsolePathVariable = ConsoleManager.RegisterConsoleVariable(
|
|
TEXT("LiveCoding.ConsolePath"),
|
|
FPaths::ConvertRelativePathToFull(DefaultEngineDir / DefaultConsolePath),
|
|
TEXT("Path to the live coding console application"),
|
|
ECVF_Cheat
|
|
);
|
|
|
|
#if IS_MONOLITHIC
|
|
FString SourceProject = (GLiveCodingProject != nullptr)? GLiveCodingProject : TEXT("");
|
|
#else
|
|
FString SourceProject = FPaths::IsProjectFilePathSet() ? FPaths::GetProjectFilePath() : TEXT("");
|
|
#endif
|
|
SourceProjectVariable = ConsoleManager.RegisterConsoleVariable(
|
|
TEXT("LiveCoding.SourceProject"),
|
|
FPaths::ConvertRelativePathToFull(SourceProject),
|
|
TEXT("Path to the project that this target was built from"),
|
|
ECVF_Cheat
|
|
);
|
|
|
|
EndFrameDelegateHandle = FCoreDelegates::OnEndFrame.AddRaw(this, &FLiveCodingModule::Tick);
|
|
|
|
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
|
if (SettingsModule != nullptr)
|
|
{
|
|
SettingsSection = SettingsModule->RegisterSettings("Editor", "General", "Live Coding",
|
|
LOCTEXT("LiveCodingSettingsName", "Live Coding"),
|
|
LOCTEXT("LiveCodintSettingsDescription", "Settings for recompiling C++ code while the engine is running."),
|
|
GetMutableDefault<ULiveCodingSettings>()
|
|
);
|
|
}
|
|
|
|
LppStartup();
|
|
|
|
if (Settings->bEnabled && !FApp::IsUnattended())
|
|
{
|
|
if(Settings->Startup == ELiveCodingStartupMode::Automatic)
|
|
{
|
|
StartLiveCoding();
|
|
ShowConsole();
|
|
}
|
|
else if(Settings->Startup == ELiveCodingStartupMode::AutomaticButHidden)
|
|
{
|
|
GLiveCodingConsoleArguments = L"-Hidden";
|
|
StartLiveCoding();
|
|
}
|
|
}
|
|
|
|
if(FParse::Param(FCommandLine::Get(), TEXT("LiveCoding")))
|
|
{
|
|
StartLiveCoding();
|
|
}
|
|
|
|
bEnabledLastTick = Settings->bEnabled;
|
|
}
|
|
|
|
void FLiveCodingModule::ShutdownModule()
|
|
{
|
|
LppShutdown();
|
|
|
|
FCoreDelegates::OnEndFrame.Remove(EndFrameDelegateHandle);
|
|
|
|
IConsoleManager& ConsoleManager = IConsoleManager::Get();
|
|
ConsoleManager.UnregisterConsoleObject(SourceProjectVariable);
|
|
ConsoleManager.UnregisterConsoleObject(ConsolePathVariable);
|
|
ConsoleManager.UnregisterConsoleObject(CompileCommand);
|
|
ConsoleManager.UnregisterConsoleObject(EnableCommand);
|
|
}
|
|
|
|
void FLiveCodingModule::EnableByDefault(bool bEnable)
|
|
{
|
|
if(Settings->bEnabled != bEnable)
|
|
{
|
|
Settings->bEnabled = bEnable;
|
|
if(SettingsSection.IsValid())
|
|
{
|
|
SettingsSection->Save();
|
|
}
|
|
}
|
|
EnableForSession(bEnable);
|
|
}
|
|
|
|
bool FLiveCodingModule::IsEnabledByDefault() const
|
|
{
|
|
return Settings->bEnabled;
|
|
}
|
|
|
|
void FLiveCodingModule::EnableForSession(bool bEnable)
|
|
{
|
|
if (bEnable)
|
|
{
|
|
if(!bStarted)
|
|
{
|
|
StartLiveCoding();
|
|
ShowConsole();
|
|
}
|
|
else
|
|
{
|
|
bEnabledForSession = true;
|
|
ShowConsole();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(bStarted)
|
|
{
|
|
UE_LOG(LogLiveCoding, Display, TEXT("Console will be hidden but remain running in the background. Restart to disable completely."));
|
|
LppSetActive(false);
|
|
LppSetVisible(false);
|
|
bEnabledForSession = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FLiveCodingModule::IsEnabledForSession() const
|
|
{
|
|
return bEnabledForSession;
|
|
}
|
|
|
|
bool FLiveCodingModule::CanEnableForSession() const
|
|
{
|
|
#if !IS_MONOLITHIC
|
|
FModuleManager& ModuleManager = FModuleManager::Get();
|
|
if(ModuleManager.HasAnyOverridenModuleFilename())
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool FLiveCodingModule::HasStarted() const
|
|
{
|
|
return bStarted;
|
|
}
|
|
|
|
void FLiveCodingModule::ShowConsole()
|
|
{
|
|
if (bStarted)
|
|
{
|
|
LppSetVisible(true);
|
|
LppSetActive(true);
|
|
LppShowConsole();
|
|
}
|
|
}
|
|
|
|
void FLiveCodingModule::Compile()
|
|
{
|
|
if(!GIsCompileActive)
|
|
{
|
|
EnableForSession(true);
|
|
if(bStarted)
|
|
{
|
|
UpdateModules(); // Need to do this immediately rather than waiting until next tick
|
|
LppTriggerRecompile();
|
|
GIsCompileActive = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FLiveCodingModule::IsCompiling() const
|
|
{
|
|
return GIsCompileActive;
|
|
}
|
|
|
|
void FLiveCodingModule::Tick()
|
|
{
|
|
if (LppWantsRestart())
|
|
{
|
|
LppRestart(lpp::LPP_RESTART_BEHAVIOR_REQUEST_EXIT, 0);
|
|
}
|
|
|
|
if (Settings->bEnabled != bEnabledLastTick && Settings->Startup != ELiveCodingStartupMode::Manual)
|
|
{
|
|
EnableForSession(Settings->bEnabled);
|
|
bEnabledLastTick = Settings->bEnabled;
|
|
if (IsEnabledByDefault() && !IsEnabledForSession())
|
|
{
|
|
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("NoEnableLiveCodingAfterHotReload", "Live Coding cannot be enabled while hot-reloaded modules are active. Please close the editor and build from your IDE before restarting."));
|
|
}
|
|
}
|
|
|
|
if (bUpdateModulesInTick)
|
|
{
|
|
UpdateModules();
|
|
bUpdateModulesInTick = false;
|
|
}
|
|
|
|
// Needs to happen after updating modules, since "Quick Restart" functionality may try to install patch immediately
|
|
extern void LppSyncPoint();
|
|
LppSyncPoint();
|
|
|
|
if (!GIsCompileActive && Reload.IsValid())
|
|
{
|
|
if (GHasLoadedPatch)
|
|
{
|
|
CheckForClassChanges();
|
|
OnPatchCompleteDelegate.Broadcast();
|
|
GHasLoadedPatch = false;
|
|
}
|
|
Reload.Reset();
|
|
}
|
|
}
|
|
|
|
void FLiveCodingModule::CheckForClassChanges()
|
|
{
|
|
#if WITH_COREUOBJECT && WITH_ENGINE
|
|
|
|
// During the module loading process, the list of changed classes will be recorded. Invoking this method will
|
|
// result in the RegisterForReinstancing method being invoked which in turn records the classes in the ClassesToReinstance
|
|
// member variable being populated.
|
|
ProcessNewlyLoadedUObjects();
|
|
|
|
#if WITH_EDITOR
|
|
Reload->Finalize();
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
ILiveCodingModule::FOnPatchCompleteDelegate& FLiveCodingModule::GetOnPatchCompleteDelegate()
|
|
{
|
|
return OnPatchCompleteDelegate;
|
|
}
|
|
|
|
bool FLiveCodingModule::StartLiveCoding()
|
|
{
|
|
if(!bStarted)
|
|
{
|
|
// Make sure there aren't any hot reload modules already active
|
|
if (!CanEnableForSession())
|
|
{
|
|
UE_LOG(LogLiveCoding, Error, TEXT("Unable to start live coding session. Some modules have already been hot reloaded."));
|
|
return false;
|
|
}
|
|
|
|
// Setup the console path
|
|
GLiveCodingConsolePath = ConsolePathVariable->GetString();
|
|
if (!FPaths::FileExists(GLiveCodingConsolePath))
|
|
{
|
|
UE_LOG(LogLiveCoding, Error, TEXT("Unable to start live coding session. Missing executable '%s'. Use the LiveCoding.ConsolePath console variable to modify."), *GLiveCodingConsolePath);
|
|
return false;
|
|
}
|
|
|
|
// Get the source project filename
|
|
FString SourceProject = SourceProjectVariable->GetString();
|
|
if (SourceProject.Len() > 0 && !FPaths::FileExists(SourceProject))
|
|
{
|
|
UE_LOG(LogLiveCoding, Error, TEXT("Unable to start live coding session. Unable to find source project file '%s'."), *SourceProject);
|
|
return false;
|
|
}
|
|
|
|
UE_LOG(LogLiveCoding, Display, TEXT("Starting LiveCoding"));
|
|
|
|
// Enable external build system
|
|
LppUseExternalBuildSystem();
|
|
|
|
// Enable the server
|
|
FString ProjectPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()).ToLower();
|
|
FString ProcessGroup = FString::Printf(TEXT("UE4_%s_0x%08x"), FApp::GetProjectName(), GetTypeHash(ProjectPath));
|
|
LppRegisterProcessGroup(TCHAR_TO_ANSI(*ProcessGroup));
|
|
|
|
// Build the command line
|
|
FString KnownTargetName = FPlatformMisc::GetUBTTargetName();
|
|
FString Arguments = FString::Printf(TEXT("%s %s %s"),
|
|
*KnownTargetName,
|
|
FPlatformMisc::GetUBTPlatform(),
|
|
LexToString(FApp::GetBuildConfiguration()));
|
|
|
|
UE_LOG(LogLiveCoding, Display, TEXT("LiveCodingConsole Arguments: %s"), *Arguments);
|
|
|
|
if(SourceProject.Len() > 0)
|
|
{
|
|
Arguments += FString::Printf(TEXT(" -Project=\"%s\""), *FPaths::ConvertRelativePathToFull(SourceProject));
|
|
}
|
|
LppSetBuildArguments(*Arguments);
|
|
|
|
// Create a mutex that allows UBT to detect that we shouldn't hot-reload into this executable. The handle to it will be released automatically when the process exits.
|
|
FString ExecutablePath = FPaths::ConvertRelativePathToFull(FPlatformProcess::ExecutablePath());
|
|
|
|
FString MutexName = TEXT("Global\\LiveCoding_");
|
|
for (int Idx = 0; Idx < ExecutablePath.Len(); Idx++)
|
|
{
|
|
TCHAR Character = ExecutablePath[Idx];
|
|
if (Character == '/' || Character == '\\' || Character == ':')
|
|
{
|
|
MutexName += '+';
|
|
}
|
|
else
|
|
{
|
|
MutexName += Character;
|
|
}
|
|
}
|
|
|
|
ensure(CreateMutex(NULL, Windows::FALSE, *MutexName));
|
|
|
|
// Configure all the current modules. For non-commandlets, schedule it to be done in the first Tick() so we can batch everything together.
|
|
if (IsRunningCommandlet())
|
|
{
|
|
UpdateModules();
|
|
}
|
|
else
|
|
{
|
|
bUpdateModulesInTick = true;
|
|
}
|
|
|
|
// Register a delegate to listen for new modules loaded from this point onwards
|
|
ModulesChangedDelegateHandle = FModuleManager::Get().OnModulesChanged().AddRaw(this, &FLiveCodingModule::OnModulesChanged);
|
|
|
|
// Mark it as started
|
|
bStarted = true;
|
|
bEnabledForSession = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FLiveCodingModule::UpdateModules()
|
|
{
|
|
if (bEnabledForSession)
|
|
{
|
|
#if IS_MONOLITHIC
|
|
wchar_t FullFilePath[WINDOWS_MAX_PATH];
|
|
verify(GetModuleFileName(hInstance, FullFilePath, UE_ARRAY_COUNT(FullFilePath)));
|
|
LppEnableModule(FullFilePath);
|
|
#else
|
|
TArray<FModuleStatus> ModuleStatuses;
|
|
FModuleManager::Get().QueryModules(ModuleStatuses);
|
|
|
|
TArray<FString> EnableModules;
|
|
for (const FModuleStatus& ModuleStatus : ModuleStatuses)
|
|
{
|
|
if (ModuleStatus.bIsLoaded)
|
|
{
|
|
FName ModuleName(*ModuleStatus.Name);
|
|
if (!ConfiguredModules.Contains(ModuleName))
|
|
{
|
|
FString FullFilePath = FPaths::ConvertRelativePathToFull(ModuleStatus.FilePath);
|
|
if (ShouldPreloadModule(ModuleName, FullFilePath))
|
|
{
|
|
EnableModules.Add(FullFilePath);
|
|
}
|
|
else
|
|
{
|
|
LppWaitForToken(LppEnableLazyLoadedModule(*FullFilePath));
|
|
}
|
|
ConfiguredModules.Add(ModuleName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (EnableModules.Num() > 0)
|
|
{
|
|
TArray<const TCHAR*> EnableModuleFileNames;
|
|
for (const FString& EnableModule : EnableModules)
|
|
{
|
|
EnableModuleFileNames.Add(*EnableModule);
|
|
}
|
|
LppWaitForToken(LppEnableModules(EnableModuleFileNames.GetData(), EnableModuleFileNames.Num()));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void FLiveCodingModule::OnModulesChanged(FName ModuleName, EModuleChangeReason Reason)
|
|
{
|
|
#if !IS_MONOLITHIC
|
|
if (Reason == EModuleChangeReason::ModuleLoaded)
|
|
{
|
|
// Assume that Tick() won't be called if we're running a commandlet
|
|
if (IsRunningCommandlet())
|
|
{
|
|
UpdateModules();
|
|
}
|
|
else
|
|
{
|
|
bUpdateModulesInTick = true;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool FLiveCodingModule::ShouldPreloadModule(const FName& Name, const FString& FullFilePath) const
|
|
{
|
|
// For the hooks to work properly, we always have to load the live coding module
|
|
if (Name == TEXT(LIVE_CODING_MODULE_NAME))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (Settings->PreloadNamedModules.Contains(Name))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (FullFilePath.StartsWith(FullProjectDir))
|
|
{
|
|
if (Settings->bPreloadProjectModules == Settings->bPreloadProjectPluginModules)
|
|
{
|
|
return Settings->bPreloadProjectModules;
|
|
}
|
|
|
|
if(FullFilePath.StartsWith(FullProjectPluginsDir))
|
|
{
|
|
return Settings->bPreloadProjectPluginModules;
|
|
}
|
|
else
|
|
{
|
|
return Settings->bPreloadProjectModules;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FApp::IsEngineInstalled())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (Settings->bPreloadEngineModules == Settings->bPreloadEnginePluginModules)
|
|
{
|
|
return Settings->bPreloadEngineModules;
|
|
}
|
|
|
|
if(FullFilePath.StartsWith(FullEnginePluginsDir))
|
|
{
|
|
return Settings->bPreloadEnginePluginModules;
|
|
}
|
|
else
|
|
{
|
|
return Settings->bPreloadEngineModules;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLiveCodingModule::PreCompileHook()
|
|
{
|
|
UE_LOG(LogLiveCoding, Display, TEXT("Starting Live Coding compile."));
|
|
GIsCompileActive = true;
|
|
BeginReload();
|
|
}
|
|
|
|
void FLiveCodingModule::PostCompileHook()
|
|
{
|
|
UE_LOG(LogLiveCoding, Display, TEXT("Live Coding compile done. See Live Coding console for more information."));
|
|
GIsCompileActive = false;
|
|
}
|
|
|
|
void FLiveCodingModule::BeginReload()
|
|
{
|
|
if (GLiveCodingModule != nullptr)
|
|
{
|
|
if (!GLiveCodingModule->Reload.IsValid())
|
|
{
|
|
#if WITH_EDITOR
|
|
GLiveCodingModule->Reload.Reset(new FReload(EActiveReloadType::LiveCoding, TEXT("LIVECODING"), *GLog));
|
|
#else
|
|
GLiveCodingModule->Reload.Reset(new FNullReload(*GLiveCodingModule));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// Invoked from LC_ClientCommandActions
|
|
void LiveCodingBeginPatch()
|
|
{
|
|
GHasLoadedPatch = true;
|
|
// If we are beginning a patch from a restart from the console, we need to create the reload object
|
|
FLiveCodingModule::BeginReload();
|
|
}
|
|
|
|
// Invoked from LC_ClientCommandActions
|
|
void LiveCodingEndCompile()
|
|
{
|
|
GIsCompileActive = false;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|