Merge branch 'stabilization/2110' into Prism/AddEditGemsButton

This commit is contained in:
nggieber
2021-11-16 15:58:14 -08:00
119 changed files with 1555 additions and 1329 deletions
@@ -0,0 +1,25 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
namespace AzToolsFramework::EmbeddedPython
{
// When using embedded Python, some platforms need to explicitly load the python library.
// For any modules that depend on 3rdParty::Python package, the AZ::Module should inherit this class.
class PythonLoader
{
public:
PythonLoader();
~PythonLoader();
private:
void* m_embeddedLibPythonHandle{ nullptr };
};
} // namespace AzToolsFramework::EmbeddedPython
@@ -47,6 +47,7 @@ set(FILES
API/EntityCompositionRequestBus.h
API/EntityCompositionNotificationBus.h
API/EditorViewportIconDisplayInterface.h
API/PythonLoader.h
API/ViewPaneOptions.h
API/ViewportEditorModeTrackerInterface.h
Application/Ticker.h
@@ -0,0 +1,20 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/API/PythonLoader.h>
namespace AzToolsFramework::EmbeddedPython
{
PythonLoader::PythonLoader()
{
}
PythonLoader::~PythonLoader()
{
}
}
@@ -0,0 +1,34 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/API/PythonLoader.h>
#include <AzCore/Debug/Trace.h>
#include <dlfcn.h>
namespace AzToolsFramework::EmbeddedPython
{
PythonLoader::PythonLoader()
{
constexpr char libPythonName[] = "libpython3.7m.so.1.0";
if (m_embeddedLibPythonHandle = dlopen(libPythonName, RTLD_NOW | RTLD_GLOBAL);
m_embeddedLibPythonHandle == nullptr)
{
char* err = dlerror();
AZ_Error("PythonLoader", false, "Failed to load %s with error: %s\n", libPythonName, err ? err : "Unknown Error");
}
}
PythonLoader::~PythonLoader()
{
if (m_embeddedLibPythonHandle)
{
dlclose(m_embeddedLibPythonHandle);
}
}
} // namespace AzToolsFramework::EmbeddedPython
@@ -7,4 +7,5 @@
#
set(FILES
AzToolsFramework/API/PythonLoader_Linux.cpp
)
@@ -7,4 +7,5 @@
#
set(FILES
../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp
)
@@ -7,4 +7,5 @@
#
set(FILES
../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp
)
@@ -23,6 +23,8 @@
#include <AzCore/RTTI/BehaviorContext.h>
//////////////////////////////////////////////////////////////////////////
#include <xxhash/xxhash.h>
namespace AssetBuilderSDK
{
const char* const ErrorWindow = "Error"; //Use this window name to log error messages.
@@ -1599,4 +1601,70 @@ namespace AssetBuilderSDK
{
return m_errorsOccurred;
}
AZ::u64 GetHashFromIOStream(AZ::IO::GenericStream& readStream, AZ::IO::SizeType* bytesReadOut, int hashMsDelay)
{
constexpr AZ::u64 HashBufferSize = 1024 * 64;
char buffer[HashBufferSize];
if(readStream.IsOpen() && readStream.CanRead())
{
AZ::IO::SizeType bytesRead;
auto* state = XXH64_createState();
if(state == nullptr)
{
AZ_Assert(false, "Failed to create hash state");
return 0;
}
if (XXH64_reset(state, 0) == XXH_ERROR)
{
AZ_Assert(false, "Failed to reset hash state");
return 0;
}
do
{
// In edge cases where another process is writing to this file while this hashing is occuring and that file wasn't locked,
// the following read check can fail because it performs an end of file check, and asserts and shuts down if the read size
// was smaller than the buffer and the read is not at the end of the file. The logic used to check end of file internal to read
// will be out of date in the edge cases where another process is actively writing to this file while this hash is running.
// The stream's length ends up more accurate in this case, preventing this assert and shut down.
// One area this occurs is the navigation mesh file (mnmnavmission0.bai) that's temporarily created when exporting a level,
// the navigation system can still be writing to this file when hashing begins, causing the EoF marker to change.
AZ::IO::SizeType remainingToRead = AZStd::min(readStream.GetLength() - readStream.GetCurPos(), aznumeric_cast<AZ::IO::SizeType>(AZ_ARRAY_SIZE(buffer)));
bytesRead = readStream.Read(remainingToRead, buffer);
if(bytesReadOut)
{
*bytesReadOut += bytesRead;
}
XXH64_update(state, buffer, bytesRead);
// Used by unit tests to force the race condition mentioned above, to verify the crash fix.
if(hashMsDelay > 0)
{
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(hashMsDelay));
}
} while (bytesRead > 0);
auto hash = XXH64_digest(state);
XXH64_freeState(state);
return hash;
}
return 0;
}
AZ::u64 GetFileHash(const char* filePath, AZ::IO::SizeType* bytesReadOut, int hashMsDelay)
{
constexpr bool ErrorOnReadFailure = true;
AZ::IO::FileIOStream readStream(filePath, AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, ErrorOnReadFailure);
return GetHashFromIOStream(readStream, bytesReadOut, hashMsDelay);
}
}
@@ -910,6 +910,19 @@ namespace AssetBuilderSDK
//! There can be multiple builders running at once, so we need to filter out ones coming from other builders
AZStd::thread_id m_jobThreadId;
};
//! Get hash for a whole file
//! @filePath the path for the file
//! @bytesReadOut output the read file size in bytes
//! @hashMsDelay [Do not use except for unit test] add a delay in ms for between each block reading.
AZ::u64 GetFileHash(const char* filePath, AZ::IO::SizeType* bytesReadOut = nullptr, int hashMsDelay = 0);
//! Get hash for a generic IO stream
//! @readStream the input readable stream
//! @bytesReadOut output the read size in bytes
//! @hashMsDelay [Do not use except for unit test] add a delay in ms for between each block reading.
AZ::u64 GetHashFromIOStream(AZ::IO::GenericStream& readStream, AZ::IO::SizeType* bytesReadOut = nullptr, int hashMsDelay = 0);
} // namespace AssetBuilderSDK
namespace AZ
@@ -32,6 +32,7 @@ ly_add_target(
PUBLIC
AZ::AzFramework
AZ::AzToolsFramework
3rdParty::xxhash
)
ly_add_source_properties(
SOURCES AssetBuilderSDK/AssetBuilderSDK.cpp
@@ -1161,7 +1161,7 @@ namespace AssetUtilities
{
#ifndef AZ_TESTS_ENABLED
// Only used for unit tests, speed is critical for GetFileHash.
AZ_UNUSED(hashMsDelay);
hashMsDelay = 0;
#endif
bool useFileHashing = ShouldUseFileHashing();
@@ -1170,10 +1170,10 @@ namespace AssetUtilities
return 0;
}
AZ::u64 hash = 0;
if(!force)
{
auto* fileStateInterface = AZ::Interface<AssetProcessor::IFileStateRequests>::Get();
AZ::u64 hash = 0;
if (fileStateInterface && fileStateInterface->GetHash(filePath, &hash))
{
@@ -1181,64 +1181,8 @@ namespace AssetUtilities
}
}
char buffer[FileHashBufferSize];
constexpr bool ErrorOnReadFailure = true;
AZ::IO::FileIOStream readStream(filePath, AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, ErrorOnReadFailure);
if(readStream.IsOpen() && readStream.CanRead())
{
AZ::IO::SizeType bytesRead;
auto* state = XXH64_createState();
if(state == nullptr)
{
AZ_Assert(false, "Failed to create hash state");
return 0;
}
if (XXH64_reset(state, 0) == XXH_ERROR)
{
AZ_Assert(false, "Failed to reset hash state");
return 0;
}
do
{
// In edge cases where another process is writing to this file while this hashing is occuring and that file wasn't locked,
// the following read check can fail because it performs an end of file check, and asserts and shuts down if the read size
// was smaller than the buffer and the read is not at the end of the file. The logic used to check end of file internal to read
// will be out of date in the edge cases where another process is actively writing to this file while this hash is running.
// The stream's length ends up more accurate in this case, preventing this assert and shut down.
// One area this occurs is the navigation mesh file (mnmnavmission0.bai) that's temporarily created when exporting a level,
// the navigation system can still be writing to this file when hashing begins, causing the EoF marker to change.
AZ::IO::SizeType remainingToRead = AZStd::min(readStream.GetLength() - readStream.GetCurPos(), aznumeric_cast<AZ::IO::SizeType>(AZ_ARRAY_SIZE(buffer)));
bytesRead = readStream.Read(remainingToRead, buffer);
if(bytesReadOut)
{
*bytesReadOut += bytesRead;
}
XXH64_update(state, buffer, bytesRead);
#ifdef AZ_TESTS_ENABLED
// Used by unit tests to force the race condition mentioned above, to verify the crash fix.
if(hashMsDelay > 0)
{
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(hashMsDelay));
}
#endif
} while (bytesRead > 0);
auto hash = XXH64_digest(state);
XXH64_freeState(state);
return hash;
}
return 0;
hash = AssetBuilderSDK::GetFileHash(filePath, bytesReadOut, hashMsDelay);
return hash;
}
AZ::u64 AdjustTimestamp(QDateTime timestamp)
@@ -363,7 +363,10 @@ namespace O3DE::ProjectManager
{
const QString selectedGemPath = m_gemModel->GetPath(modelIndex);
// Remove gem from gems to be added
const bool wasAdded = GemModel::WasPreviouslyAdded(modelIndex);
const bool wasAddedDependency = GemModel::WasPreviouslyAddedDependency(modelIndex);
// Remove gem from gems to be added to update any dependencies
GemModel::SetIsAdded(*m_gemModel, modelIndex, false);
// Unregister the gem
@@ -391,6 +394,8 @@ namespace O3DE::ProjectManager
// Select remote gem
QModelIndex remoteGemIndex = m_gemModel->FindIndexByNameString(selectedGemName);
GemModel::SetWasPreviouslyAdded(*m_gemModel, remoteGemIndex, wasAdded);
GemModel::SetWasPreviouslyAddedDependency(*m_gemModel, remoteGemIndex, wasAddedDependency);
QModelIndex proxyIndex = m_proxyModel->mapFromSource(remoteGemIndex);
m_proxyModel->GetSelectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect);
}
@@ -523,7 +528,9 @@ namespace O3DE::ProjectManager
const QString& gemPath = GemModel::GetPath(modelIndex);
// make sure any remote gems we added were downloaded successfully
if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote && GemModel::GetDownloadStatus(modelIndex) != GemInfo::Downloaded)
const GemInfo::DownloadStatus status = GemModel::GetDownloadStatus(modelIndex);
if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote &&
!(status == GemInfo::Downloaded || status == GemInfo::DownloadSuccessful))
{
QMessageBox::critical(
nullptr, "Cannot add gem that isn't downloaded",
@@ -9,12 +9,14 @@
#include <ProjectBuilderController.h>
#include <ProjectBuilderWorker.h>
#include <ProjectButtonWidget.h>
#include <ProjectManagerSettings.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <QMessageBox>
#include <QDesktopServices>
#include <QUrl>
namespace O3DE::ProjectManager
{
ProjectBuilderController::ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent)
@@ -27,6 +29,15 @@ namespace O3DE::ProjectManager
m_worker = new ProjectBuilderWorker(m_projectInfo);
m_worker->moveToThread(&m_workerThread);
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
// Remove key here in case Project Manager crashing while building that causes HandleResults to not be called
QString settingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName);
settingsRegistry->Remove(settingsKey.toStdString().c_str());
SaveProjectManagerSettings();
}
connect(&m_workerThread, &QThread::finished, m_worker, &ProjectBuilderWorker::deleteLater);
connect(&m_workerThread, &QThread::started, m_worker, &ProjectBuilderWorker::BuildProject);
connect(m_worker, &ProjectBuilderWorker::Done, this, &ProjectBuilderController::HandleResults);
@@ -80,6 +91,8 @@ namespace O3DE::ProjectManager
void ProjectBuilderController::HandleResults(const QString& result)
{
QString settingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName);
if (!result.isEmpty())
{
if (result.contains(tr("log")))
@@ -109,12 +122,26 @@ namespace O3DE::ProjectManager
emit NotifyBuildProject(m_projectInfo);
}
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
settingsRegistry->Remove(settingsKey.toStdString().c_str());
SaveProjectManagerSettings();
}
emit Done(false);
return;
}
else
{
m_projectInfo.m_buildFailed = false;
auto settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
settingsRegistry->Set(settingsKey.toStdString().c_str(), true);
SaveProjectManagerSettings();
}
}
emit Done(true);
@@ -0,0 +1,54 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "ProjectManagerSettings.h"
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/Utils/Utils.h>
namespace O3DE::ProjectManager
{
void SaveProjectManagerSettings()
{
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
dumperSettings.m_prettifyOutput = true;
dumperSettings.m_jsonPointerPrefix = ProjectManagerKeyPrefix;
AZStd::string stringBuffer;
AZ::IO::ByteContainerStream stringStream(&stringBuffer);
if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(
*settingsRegistry, ProjectManagerKeyPrefix, stringStream, dumperSettings))
{
AZ_Warning("ProjectManager", false, "Could not save Project Manager settings to stream");
return;
}
AZ::IO::FixedMaxPath o3deUserPath = AZ::Utils::GetO3deManifestDirectory();
o3deUserPath /= AZ::SettingsRegistryInterface::RegistryFolder;
o3deUserPath /= "ProjectManager.setreg";
bool saved = false;
constexpr auto configurationMode =
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
AZ::IO::SystemFile outputFile;
if (outputFile.Open(o3deUserPath.c_str(), configurationMode))
{
saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size();
}
AZ_Warning("ProjectManager", saved, "Unable to save Project Manager registry file to path: %s", o3deUserPath.c_str());
}
QString GetProjectBuiltSuccessfullyKey(const QString& projectName)
{
return QString("%1/Projects/%2/BuiltSuccessfully").arg(ProjectManagerKeyPrefix).arg(projectName);
}
}
@@ -0,0 +1,21 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QString>
#endif
namespace O3DE::ProjectManager
{
static constexpr char ProjectManagerKeyPrefix[] = "/O3DE/ProjectManager";
void SaveProjectManagerSettings();
QString GetProjectBuiltSuccessfullyKey(const QString& projectName);
}
@@ -14,6 +14,7 @@
#include <ProjectUtils.h>
#include <ProjectBuilderController.h>
#include <ScreensCtrl.h>
#include <ProjectManagerSettings.h>
#include <AzQtComponents/Components/FlowLayout.h>
#include <AzCore/Platform.h>
@@ -22,6 +23,7 @@
#include <AzFramework/Process/ProcessCommon.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
@@ -270,17 +272,36 @@ namespace O3DE::ProjectManager
// Add any missing project buttons and restore buttons to default state
for (const ProjectInfo& project : projectsVector)
{
ProjectButton* currentButton = nullptr;
if (!m_projectButtons.contains(QDir::toNativeSeparators(project.m_path)))
{
m_projectButtons.insert(QDir::toNativeSeparators(project.m_path), CreateProjectButton(project));
currentButton = CreateProjectButton(project);
m_projectButtons.insert(QDir::toNativeSeparators(project.m_path), currentButton);
}
else
{
auto projectButtonIter = m_projectButtons.find(QDir::toNativeSeparators(project.m_path));
if (projectButtonIter != m_projectButtons.end())
{
projectButtonIter.value()->RestoreDefaultState();
m_projectsFlowLayout->addWidget(projectButtonIter.value());
currentButton = projectButtonIter.value();
currentButton->RestoreDefaultState();
m_projectsFlowLayout->addWidget(currentButton);
}
}
// Check whether project manager has successfully built the project
if (currentButton)
{
auto settingsRegistry = AZ::SettingsRegistry::Get();
bool projectBuiltSuccessfully = false;
if (settingsRegistry)
{
QString settingsKey = GetProjectBuiltSuccessfullyKey(project.m_projectName);
settingsRegistry->Get(projectBuiltSuccessfully, settingsKey.toStdString().c_str());
}
if (!projectBuiltSuccessfully)
{
currentButton->ShowBuildRequired();
}
}
}
@@ -15,6 +15,9 @@
#include <UpdateProjectCtrl.h>
#include <UpdateProjectSettingsScreen.h>
#include <ProjectUtils.h>
#include <ProjectManagerSettings.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <QDialogButtonBox>
#include <QMessageBox>
@@ -300,6 +303,21 @@ namespace O3DE::ProjectManager
}
}
if (newProjectSettings.m_projectName != m_projectInfo.m_projectName)
{
// update reg key
QString oldSettingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName);
QString newSettingsKey = GetProjectBuiltSuccessfullyKey(newProjectSettings.m_projectName);
auto settingsRegistry = AZ::SettingsRegistry::Get();
bool projectBuiltSuccessfully = false;
if (settingsRegistry && settingsRegistry->Get(projectBuiltSuccessfully, oldSettingsKey.toStdString().c_str()))
{
settingsRegistry->Set(newSettingsKey.toStdString().c_str(), projectBuiltSuccessfully);
SaveProjectManagerSettings();
}
}
if (!newProjectSettings.m_newPreviewImagePath.isEmpty())
{
if (!ProjectUtils::ReplaceProjectFile(
@@ -58,6 +58,8 @@ set(FILES
Source/CreateProjectCtrl.cpp
Source/UpdateProjectCtrl.h
Source/UpdateProjectCtrl.cpp
Source/ProjectManagerSettings.h
Source/ProjectManagerSettings.cpp
Source/ProjectsScreen.h
Source/ProjectsScreen.cpp
Source/ProjectSettingsScreen.h