Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update workspace also reload the current map if needed #47

Merged
merged 11 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
94 changes: 78 additions & 16 deletions Source/PlasticSourceControl/Private/PlasticSourceControlMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,33 +451,95 @@ void FPlasticSourceControlMenu::DisplayFailureNotification(const FName& InOperat
UE_LOG(LogSourceControl, Error, TEXT("%s"), *NotificationText.ToString());
}

void FPlasticSourceControlMenu::OnSourceControlOperationComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult)
// Get the World currently loaded by the Editor (and thus, access to the corresponding map package)
UWorld* GetCurrentWorld()
{
RemoveInProgressNotification();
if (GEditor)
{
if (UWorld* EditorWorld = GEditor->GetEditorWorldContext().World())
{
return EditorWorld;
}
}
return nullptr;
}

if (InOperation->GetName() == "SyncAll")
TArray<UPackage*> ListPackagesToReload(const TArray<FString>& InUpdatedFiles)
{
TArray<UPackage*> PackagesToReload;

PackagesToReload.Reserve(InUpdatedFiles.Num() + 1);
for (const FString& FilePath : InUpdatedFiles)
{
TSharedRef<FPlasticSyncAll, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FPlasticSyncAll>(InOperation);
FString PackageName;
FString FailureReason;
if (FPackageName::TryConvertFilenameToLongPackageName(FilePath, PackageName, &FailureReason))
{
// NOTE: this will only find packages loaded in memory
if (UPackage* Package = FindPackage(nullptr, *PackageName))
{
PackagesToReload.Emplace(Package);
UE_LOG(LogSourceControl, Log, TEXT("Reload: %s"), *PackageName);
}
}
// else, it means the file is not an asset from the Content/ folder (eg config, source code, anything else)
}

#if ENGINE_MAJOR_VERSION == 5
// Detects if some packages to reload are part of the current map
// (ie assets within __ExternalActors__ or __ExternalObjects__ from the new One File Per Actor (OFPA) in UE5)
// in which case the current map need to be reloaded, so it needs to be added to the list of packages if not already there
// (then UPackageTools::ReloadPackages() will handle unloading the map at the start of the reload, avoiding some crash, and reloading it at the end)
if (UWorld* CurrentWorld = GetCurrentWorld())
{
UPackage* CurrentMapPackage = CurrentWorld->GetOutermost();

// If the current map file has been updated, it will be reloaded automatically, so no need for the following
const FString CurrentMapFileAbsolute = FPaths::ConvertRelativePathToFull(CurrentMapPackage->GetLoadedPath().GetLocalFullPath());
const bool bHasCurrentMapBeenUpdated = InUpdatedFiles.FindByPredicate(
[&CurrentMapFileAbsolute](const FString& InFilePath) { return InFilePath.Equals(CurrentMapFileAbsolute, ESearchCase::IgnoreCase); }
) != nullptr;

TArray<UPackage*> PackagesToReload;
PackagesToReload.Reserve(Operation->UpdatedFiles.Num());
for (const FString& FilePath : Operation->UpdatedFiles)
if (!bHasCurrentMapBeenUpdated)
{
FString PackageName;
FString FailureReason;
if (FPackageName::TryConvertFilenameToLongPackageName(FilePath, PackageName, &FailureReason))
static const FString GamePath = FString("/Game");
const FString CurrentMapPath = *CurrentMapPackage->GetName(); // eg "/Game/Maps/OpenWorld"
const FString CurrentMapPathWithoutGamePrefix = CurrentMapPath.RightChop(GamePath.Len()); // eg "/Maps/OpenWorld"
const FString CurrentMapExternalActorPath = FPackagePath::GetExternalActorsFolderName() + CurrentMapPathWithoutGamePrefix; // eg "/__ExternalActors__/Maps/OpenWorld"
const FString CurrentMapExternalObjectPath = FPackagePath::GetExternalObjectsFolderName() + CurrentMapPathWithoutGamePrefix; // eg "/__ExternalObjects__/Maps/OpenWorld"

bool bNeedReloadCurrentMap = false;

for (const FString& FilePath : InUpdatedFiles)
{
// NOTE: this will only find packages loaded in memory
if (UPackage* Package = FindPackage(nullptr, *PackageName))
if (FilePath.Contains(CurrentMapExternalActorPath) || FilePath.Contains(CurrentMapExternalObjectPath))
{
PackagesToReload.Emplace(Package);
UE_LOG(LogSourceControl, Log, TEXT("Reload: %s"), *PackageName);
bNeedReloadCurrentMap = true;
break;
}
}
// else, it means the file is not an asset from the Content/ folder (eg config, source code, anything else)

if (bNeedReloadCurrentMap)
{
PackagesToReload.Add(CurrentMapPackage);
UE_LOG(LogSourceControl, Log, TEXT("Reload: %s"), *CurrentMapPath);
}
}
}
#endif

// Reload packages that where updated by the Sync operation
return PackagesToReload;
}

void FPlasticSourceControlMenu::OnSourceControlOperationComplete(const FSourceControlOperationRef& InOperation, ECommandResult::Type InResult)
{
RemoveInProgressNotification();

if (InOperation->GetName() == "SyncAll")
{
// Reload packages that where updated by the Sync operation (and the current map if needed)
TSharedRef<FPlasticSyncAll, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FPlasticSyncAll>(InOperation);
TArray<UPackage*> PackagesToReload = ListPackagesToReload(Operation->UpdatedFiles);
ReloadPackages(PackagesToReload);
}
else if (InOperation->GetName() == "RevertAll")
Expand Down
54 changes: 26 additions & 28 deletions Source/PlasticSourceControl/Private/PlasticSourceControlUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "PlasticSourceControlShell.h"
#include "PlasticSourceControlState.h"
#include "ISourceControlModule.h"
#include "ScopedTempFile.h"

#include "Runtime/Launch/Resources/Version.h"
#if ENGINE_MAJOR_VERSION == 4
Expand Down Expand Up @@ -1434,47 +1435,44 @@ bool RunSync(const TArray<FString>& InFiles, const bool bInIsPartialWorkspace, T
{
bool bResult = false;

// TODO: const FScopedTempFile TempFile;
const FString TempFilename = FPaths::CreateTempFilename(*FPaths::ConvertRelativePathToFull(FPaths::ProjectLogDir()), TEXT("Plastic-Temp"), TEXT(".xml"));

TArray<FString> InfoMessages;
TArray<FString> Parameters;
Parameters.Add(FString::Printf(TEXT("--xml=\"%s\""), *TempFilename));
Parameters.Add(TEXT("--encoding=\"utf-8\""));
// Update specified directory to the head of the repository
// Detect special case for a partial checkout (CS:-1 in Gluon mode)!
if (!bInIsPartialWorkspace)
{
const FScopedTempFile TempFile;
Parameters.Add(FString::Printf(TEXT("--xml=\"%s\""), *TempFile.GetFilename()));
Parameters.Add(TEXT("--encoding=\"utf-8\""));
Parameters.Add(TEXT("--last"));
Parameters.Add(TEXT("--dontmerge"));
bResult = PlasticSourceControlUtils::RunCommand(TEXT("update"), Parameters, InFiles, InfoMessages, OutErrorMessages);
}
else
{
bResult = PlasticSourceControlUtils::RunCommand(TEXT("partial update"), Parameters, InFiles, InfoMessages, OutErrorMessages);
}

if (bResult)
{
// Parse the result of the
FString Results;
if (FFileHelper::LoadFileToString(Results, *TempFilename))
bResult = PlasticSourceControlUtils::RunCommand(TEXT("update"), Parameters, TArray<FString>(), InfoMessages, OutErrorMessages);
if (bResult)
{
FXmlFile XmlFile;
{
TRACE_CPUPROFILER_EVENT_SCOPE(PlasticSourceControlUtils::RunSync::FXmlFile::LoadFile);
bResult = XmlFile.LoadFile(Results, EConstructMethod::ConstructFromBuffer);
}
if (bResult)
{
bResult = ParseSyncResults(XmlFile, OutUpdatedFiles);
}
else
// Load and parse the result of the update command
FString Results;
if (FFileHelper::LoadFileToString(Results, *TempFile.GetFilename()))
{
UE_LOG(LogSourceControl, Error, TEXT("RunSync: XML parse error '%s'"), *XmlFile.GetLastError())
FXmlFile XmlFile;
{
TRACE_CPUPROFILER_EVENT_SCOPE(PlasticSourceControlUtils::RunSync::FXmlFile::LoadFile);
bResult = XmlFile.LoadFile(Results, EConstructMethod::ConstructFromBuffer);
}
if (bResult)
{
bResult = ParseSyncResults(XmlFile, OutUpdatedFiles);
}
else
{
UE_LOG(LogSourceControl, Error, TEXT("RunSync: XML parse error '%s'"), *XmlFile.GetLastError())
}
}
}
}
else
{
bResult = PlasticSourceControlUtils::RunCommand(TEXT("partial update"), Parameters, InFiles, InfoMessages, OutErrorMessages);
}

return bResult;
}
Expand Down
5 changes: 5 additions & 0 deletions Source/PlasticSourceControl/Private/ScopedTempFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

#include "ISourceControlModule.h" // LogSourceControl

FScopedTempFile::FScopedTempFile()
{
Filename = FPaths::CreateTempFilename(*FPaths::ProjectLogDir(), TEXT("Plastic-Temp"), TEXT(".txt"));
}

FScopedTempFile::FScopedTempFile(const FString& InText)
{
Filename = FPaths::CreateTempFilename(*FPaths::ProjectLogDir(), TEXT("Plastic-Temp"), TEXT(".txt"));
Expand Down
3 changes: 3 additions & 0 deletions Source/PlasticSourceControl/Private/ScopedTempFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
class FScopedTempFile
{
public:
/** Default constructor - only hold a temp filename */
FScopedTempFile();

/** Constructor - open & write string to temp file */
explicit FScopedTempFile(const FString& InText);
explicit FScopedTempFile(const FText& InText);
Expand Down