Enforce Content:CloneAssetFile() running on the main thread, to avoid a bug that occurs when a particle emitter is created from one of the templates due to the creation coming from the content thread.

This commit is contained in:
Menotdan
2024-04-11 17:37:31 -04:00
parent b2f9da4113
commit e137d31839

View File

@@ -14,6 +14,7 @@
#include "Engine/Engine/EngineService.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Threading/MainThreadTask.h"
#include "Engine/Graphics/Graphics.h"
#include "Engine/Engine/Time.h"
#include "Engine/Engine/Globals.h"
@@ -688,101 +689,135 @@ bool Content::FastTmpAssetClone(const StringView& path, String& resultPath)
return false;
}
bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPath, const Guid& dstId)
class CloneAssetFileTask : public MainThreadTask
{
PROFILE_CPU();
ASSERT(FileSystem::AreFilePathsEqual(srcPath, dstPath) == false && dstId.IsValid());
public:
StringView dstPath;
StringView srcPath;
Guid dstId;
bool* output;
LOG(Info, "Cloning asset \'{0}\' to \'{1}\'({2}).", srcPath, dstPath, dstId);
// Check source file
if (!FileSystem::FileExists(srcPath))
protected:
bool Run() override
{
LOG(Warning, "Missing source file.");
return true;
}
// Special case for json resources
if (JsonStorageProxy::IsValidExtension(FileSystem::GetExtension(srcPath).ToLower()))
{
if (FileSystem::CopyFile(dstPath, srcPath))
{
LOG(Warning, "Cannot copy file to destination.");
return true;
}
if (JsonStorageProxy::ChangeId(dstPath, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
*output = Content::CloneAssetFile(dstPath, srcPath, dstId);
return false;
}
};
// Check if destination file is missing
if (!FileSystem::FileExists(dstPath))
bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPath, const Guid& dstId)
{
// Best to run this on the main thread to avoid clone conflicts.
if (IsInMainThread())
{
// Use quick file copy
if (FileSystem::CopyFile(dstPath, srcPath))
PROFILE_CPU();
ASSERT(FileSystem::AreFilePathsEqual(srcPath, dstPath) == false && dstId.IsValid());
LOG(Info, "Cloning asset \'{0}\' to \'{1}\'({2}).", srcPath, dstPath, dstId);
// Check source file
if (!FileSystem::FileExists(srcPath))
{
LOG(Warning, "Cannot copy file to destination.");
LOG(Warning, "Missing source file.");
return true;
}
// Change ID
auto storage = ContentStorageManager::GetStorage(dstPath);
FlaxStorage::Entry e;
storage->GetEntry(0, e);
if (storage == nullptr || storage->ChangeAssetID(e, dstId))
// Special case for json resources
if (JsonStorageProxy::IsValidExtension(FileSystem::GetExtension(srcPath).ToLower()))
{
LOG(Warning, "Cannot change asset ID.");
return true;
if (FileSystem::CopyFile(dstPath, srcPath))
{
LOG(Warning, "Cannot copy file to destination.");
return true;
}
if (JsonStorageProxy::ChangeId(dstPath, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
return false;
}
// Check if destination file is missing
if (!FileSystem::FileExists(dstPath))
{
// Use quick file copy
if (FileSystem::CopyFile(dstPath, srcPath))
{
LOG(Warning, "Cannot copy file to destination.");
return true;
}
// Change ID
auto storage = ContentStorageManager::GetStorage(dstPath);
FlaxStorage::Entry e;
storage->GetEntry(0, e);
if (storage == nullptr || storage->ChangeAssetID(e, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
}
else
{
// Use temporary file
String tmpPath = Globals::TemporaryFolder / Guid::New().ToString(Guid::FormatType::D);
if (FileSystem::CopyFile(tmpPath, srcPath))
{
LOG(Warning, "Cannot copy file.");
return true;
}
// Change asset ID
{
auto storage = ContentStorageManager::GetStorage(tmpPath);
if (!storage)
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
FlaxStorage::Entry e;
storage->GetEntry(0, e);
if (storage->ChangeAssetID(e, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
}
// Unlock destination file
ContentStorageManager::EnsureAccess(dstPath);
// Copy temp file to the destination
if (FileSystem::CopyFile(dstPath, tmpPath))
{
LOG(Warning, "Cannot copy file to destination.");
return true;
}
// Cleanup
FileSystem::DeleteFile(tmpPath);
// Reload storage
if (auto storage = ContentStorageManager::GetStorage(dstPath))
{
storage->Reload();
}
}
}
else
{
// Use temporary file
String tmpPath = Globals::TemporaryFolder / Guid::New().ToString(Guid::FormatType::D);
if (FileSystem::CopyFile(tmpPath, srcPath))
{
LOG(Warning, "Cannot copy file.");
return true;
}
CloneAssetFileTask* task = New<CloneAssetFileTask>();
task->dstId = dstId;
task->dstPath = dstPath;
task->srcPath = srcPath;
// Change asset ID
{
auto storage = ContentStorageManager::GetStorage(tmpPath);
if (!storage)
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
FlaxStorage::Entry e;
storage->GetEntry(0, e);
if (storage->ChangeAssetID(e, dstId))
{
LOG(Warning, "Cannot change asset ID.");
return true;
}
}
bool result = false;
task->output = &result;
task->Start();
task->Wait();
// Unlock destination file
ContentStorageManager::EnsureAccess(dstPath);
// Copy temp file to the destination
if (FileSystem::CopyFile(dstPath, tmpPath))
{
LOG(Warning, "Cannot copy file to destination.");
return true;
}
// Cleanup
FileSystem::DeleteFile(tmpPath);
// Reload storage
if (auto storage = ContentStorageManager::GetStorage(dstPath))
{
storage->Reload();
}
return result;
}
return false;