Merge branch 'main' of https://github.com/aws-lumberyard/o3de into Spawnable/Instantiation/EntityIdReferenceFix

This commit is contained in:
sconel
2021-05-18 11:54:29 -07:00
108 changed files with 1609 additions and 694 deletions
-1
View File
@@ -772,7 +772,6 @@ _MS_ALIGN(16) struct SSkinningData
void* pCharInstCB; // used if per char instance cbs are available in renderdll (d3d11+);
// members below are for Software Skinning
void* pCustomData; // client specific data, used for example for sw-skinning on animation side
SSkinningData** pMasterSkinningDataList; // used by the SkinningData for a Character Instance, contains a list of all Skin Instances which need SW-Skinning
SSkinningData* pNextSkinningData; // List to the next element which needs SW-Skinning
} _ALIGN(16);
@@ -20,10 +20,12 @@
namespace AZStd
{
//! Simple class for verifying that no concurrent access is occuring.
//! Simple class for verifying that no concurrent access is occurring.
//! This is *not* a synchronization primitive, and is intended simply for checking that no concurrency issues exist.
//! It will be compiled out in release builds.
//! Use concurrency_checker like a mutex (i.e. call soft_lock() and soft_unlock() around all instances of your data access).
//! Use soft_lock_shared and soft_unlock_shared around places where multiple threads are allowed to have read access
//! at the same time as long as nothing else already has a soft lock
//! It will assert if there are multiple threads accessing the locked code/data at the same time.
//! Expected use case is for defensive programming: when you do not expect any concurrent access within a system,
//! but want to verify that it stays that way in the future, without incurring the overhead of a mutex.
@@ -34,7 +36,7 @@ namespace AZStd
{
#ifdef AZ_CONCURRENCY_CHECKER_ENABLED
uint32_t count = ++m_concurrencyCounter;
AZ_Assert(count == 1, "Concurrency check failed. Multiple threads are trying to access data at the same time, or there is a lock/unlock mismatch.");
AZ_Assert(count == 1 && m_sharedConcurrencyCounter == 0, "Concurrency check failed. Multiple threads are trying to access data at the same time, or there is a lock/unlock mismatch.");
#endif
}
@@ -46,9 +48,27 @@ namespace AZStd
#endif
}
AZ_FORCE_INLINE void soft_lock_shared()
{
#ifdef AZ_CONCURRENCY_CHECKER_ENABLED
AZ_Assert(m_concurrencyCounter == 0, "Concurrency check failed. A soft_lock_shared was attempted when there was already a soft_lock.");
++m_sharedConcurrencyCounter;
#endif
}
AZ_FORCE_INLINE void soft_unlock_shared()
{
#ifdef AZ_CONCURRENCY_CHECKER_ENABLED
AZ_Assert(m_sharedConcurrencyCounter != 0, "Concurrency check failed. There is a shared_lock/shared_unlock mismatch.");
--m_sharedConcurrencyCounter;
#endif
}
private:
#ifdef AZ_CONCURRENCY_CHECKER_ENABLED
AZStd::atomic_uint32_t m_concurrencyCounter = 0;
AZStd::atomic_uint32_t m_sharedConcurrencyCounter = 0;
#endif
};
+2 -5
View File
@@ -293,15 +293,12 @@ namespace UnitTest
{
array_view<int> view({ 1,2,3,4 });
UnitTest::TestRunner::Instance().StartAssertTests();
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_EQ(0, UnitTest::TestRunner::Instance().m_numAssertsFailed);
view[4];
EXPECT_EQ(1, UnitTest::TestRunner::Instance().m_numAssertsFailed);
view[5];
EXPECT_EQ(2, UnitTest::TestRunner::Instance().m_numAssertsFailed);
UnitTest::TestRunner::Instance().StopAssertTests();
AZ_TEST_STOP_TRACE_SUPPRESSION(2);
}
}
@@ -0,0 +1,100 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AtomCore/std/parallel/concurrency_checker.h>
#include <AzCore/UnitTest/TestTypes.h>
using namespace AZStd;
namespace UnitTest
{
class ConcurrencyCheckerTestFixture
: public AllocatorsTestFixture
{
void SetUp() override
{
AllocatorsFixture::SetUp();
}
};
TEST_F(AllocatorsTestFixture, SoftLock_NoContention_NoAsserts)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
}
TEST_F(AllocatorsTestFixture, SoftLock_AlreadyLocked_Assert)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock();
AZ_TEST_START_TRACE_SUPPRESSION;
concurrencyChecker.soft_lock();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(AllocatorsTestFixture, SoftUnlock_NotAlreadyLocked_Assert)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
AZ_TEST_START_TRACE_SUPPRESSION;
concurrencyChecker.soft_unlock();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(AllocatorsTestFixture, SoftLockShared_NoContention_NoAsserts)
{
concurrency_checker concurrencyChecker;
// Multiple shared locks can be made at once,
// as long as they are all unlocked before the next soft_lock
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_unlock_shared();
concurrencyChecker.soft_unlock_shared();
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_unlock_shared();
concurrencyChecker.soft_unlock_shared();
concurrencyChecker.soft_lock();
concurrencyChecker.soft_unlock();
}
TEST_F(AllocatorsTestFixture, SoftLockShared_SharedLockAfterSoftLock_Assert)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock();
AZ_TEST_START_TRACE_SUPPRESSION;
concurrencyChecker.soft_lock_shared();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(AllocatorsTestFixture, SoftUnlockShared_NotAlreadyLocked_Assert)
{
concurrency_checker concurrencyChecker;
concurrencyChecker.soft_lock_shared();
concurrencyChecker.soft_unlock_shared();
AZ_TEST_START_TRACE_SUPPRESSION;
concurrencyChecker.soft_unlock_shared();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
}
@@ -11,6 +11,7 @@
set(FILES
ArrayView.cpp
ConcurrencyCheckerTests.cpp
InstanceDatabase.cpp
JsonSerializationUtilsTests.cpp
lru_cache.cpp
@@ -103,7 +103,7 @@ namespace AZ
}
}
/// Returns a pointer to the beginning of master vector of SmallAllocationGroups.
/// Returns a pointer to the beginning of vector of SmallAllocationGroups.
SmallAllocationGroup* ArrayHead()
{
return this - m_index;
@@ -169,7 +169,7 @@ namespace AZ
return m_marker == MARKER;
}
/// Returns the master index of the SmallAllocationGroup containing this allocation
/// Returns the index of the SmallAllocationGroup containing this allocation
uint32_t GetSmallAllocationIndex() const
{
return (uint32_t)(m_data & 0xFFFFFFFF);
@@ -40,7 +40,7 @@ namespace AZ
// Afterwards use the AZ::IO::Path Append function append the filename as a child
// of the framework directory
AZ::IO::FixedMaxPathString fileName{ fullPath.Filename().Native() };
fullPath.ReplaceFilename(AZ::IO::PathView((fileName + ".framework").c_str()));
fullPath.ReplaceFilename(AZ::IO::PathView(AZStd::string_view(fileName + ".framework")));
fullPath /= fileName;
}
}
@@ -115,7 +115,7 @@ namespace AZ::IO
// If used, the source path will be treated as the destination path
// and no transformations will be done. Pass this flag when the path is to be the actual
// path on the disk/in the packs and doesn't need adjustment (or after it has come through adjustments already)
// if this is set, AdjustFileName will not map the input path into the master folder (Ex: Shaders will not be converted to Game\Shaders)
// if this is set, AdjustFileName will not map the input path into the folder (Ex: Shaders will not be converted to Game\Shaders)
FLAGS_PATH_REAL = 1 << 16,
// AdjustFileName will always copy the file path to the destination path:
@@ -318,7 +318,6 @@ namespace AZ::IO
virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nFlags = 0, bool bAllowUseFileSystem = false) = 0;
virtual ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator handle) = 0;
virtual bool FindClose(AZ::IO::ArchiveFileIterator handle) = 0;
// virtual bool IsOutOfDate(const char * szCompiledName, const char * szMasterFile)=0;
//returns file modification time
virtual IArchive::FileTime GetModificationTime(AZ::IO::HandleType fileHandle) = 0;
@@ -47,7 +47,7 @@ namespace AZ::IO
enum EPakFlags
{
// support for absolute and other complex path specifications -
// all paths will be treated relatively to the current directory (normally MasterCD)
// all paths will be treated relatively to the current directory
FLAGS_ABSOLUTE_PATHS = 1,
// if this is set, the object will only understand relative to the zip file paths,
@@ -18,6 +18,7 @@
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzFramework/Physics/Collision/CollisionLayers.h>
#include <AzFramework/Physics/Common/PhysicsTypes.h>
#include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
#include <AzFramework/Physics/Configuration/SimulatedBodyConfiguration.h>
@@ -36,7 +37,7 @@ namespace Physics
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_name;
ShapeConfigurationList m_shapes;
AzPhysics::ShapeColliderPairList m_shapes;
};
class CharacterColliderConfiguration
@@ -57,7 +57,7 @@ namespace Physics
classElement.RemoveElement(shapesIndex);
// add a new vector in the new format
const int newShapesIndex = classElement.AddElement<ShapeConfigurationList>(context, "shapes");
const int newShapesIndex = classElement.AddElement<AzPhysics::ShapeColliderPairList>(context, "shapes");
if (newShapesIndex != -1)
{
AZ::SerializeContext::DataElementNode& newShapesElement = classElement.GetSubElement(newShapesIndex);
@@ -65,7 +65,9 @@ namespace Physics
// convert the old shapes into the new format and add to the vector
for (AZ::SerializeContext::DataElementNode shape : shapesCopy)
{
const int pairIndex = newShapesElement.AddElementWithData<ShapeConfigurationPair>(context, "element", ShapeConfigurationPair());
const int pairIndex = newShapesElement.AddElementWithData<AzPhysics::ShapeColliderPair>(
context, "element", AzPhysics::ShapeColliderPair());
AZ::SerializeContext::DataElementNode& pairElement = newShapesElement.GetSubElement(pairIndex);
ColliderConfiguration colliderConfig;
@@ -131,8 +133,8 @@ namespace Physics
AZ::SerializeContext::DataElementNode* baseBaseClass1 = baseClass1->FindSubElement(AZ_CRC("BaseClass1", 0xd4925735));
if (baseBaseClass1 && baseBaseClass1->FindSubElementAndGetData<AZStd::string>(AZ_CRC("name", 0x5e237e06), name))
{
ShapeConfigurationList shapes;
if (nodeElement.FindSubElementAndGetData<ShapeConfigurationList>(AZ_CRC("shapes", 0x93dba512), shapes))
AzPhysics::ShapeColliderPairList shapes;
if (nodeElement.FindSubElementAndGetData<AzPhysics::ShapeColliderPairList>(AZ_CRC("shapes", 0x93dba512), shapes))
{
CharacterColliderNodeConfiguration newColliderNodeConfig;
newColliderNodeConfig.m_name = name;
@@ -70,7 +70,10 @@ namespace AzPhysics
using SimulatedBodyHandleList = AZStd::vector<SimulatedBodyHandle>;
//! Helper used for pairing the ShapeConfiguration and ColliderConfiguration together which is used when creating a Simulated Body.
using ShapeColliderPair = AZStd::pair<Physics::ColliderConfiguration*, Physics::ShapeConfiguration*>;
using ShapeColliderPair = AZStd::pair<
AZStd::shared_ptr<Physics::ColliderConfiguration>,
AZStd::shared_ptr<Physics::ShapeConfiguration>>;
using ShapeColliderPairList = AZStd::vector<ShapeColliderPair>;
//! Flags used to specifying which properties of a body to compute.
enum class MassComputeFlags : AZ::u8
@@ -80,9 +80,6 @@ namespace Physics
void OnContactOffsetChanged();
};
using ShapeConfigurationPair = AZStd::pair<AZStd::shared_ptr<ColliderConfiguration>, AZStd::shared_ptr<ShapeConfiguration>>;
using ShapeConfigurationList = AZStd::vector<ShapeConfigurationPair>;
struct RayCastRequest;
class Shape
@@ -264,12 +264,12 @@ namespace AzFramework
// SampleRPC =
// {
// // Two callbacks can be registered to the NetRPC
// // A function to be invoked on the Master - OnMaster
// // A function to be invoked on the main server - OnServer
// // and a function to be invoked on the Proxy - OnProxy
// //
// // Every NetRPC needs to have a valid OnMaster function, while OnProxy is optional.
// OnMaster = function()
// Debug.Log("Function to be invoked on the Master.");
// // Every NetRPC needs to have a valid OnServer function, while OnProxy is optional.
// OnServer = function()
// Debug.Log("Function to be invoked on the server.");
// end
//
// OnProxy = function()
@@ -41,7 +41,7 @@ namespace Neighborhood {
//---------------------------------------------------------------------
void NeighborReplica::OnReplicaActivate(const GridMate::ReplicaContext& /*rc*/)
{
// TODO: Should we send the message to ourselves as well (master)?
// TODO: Should we send the message to ourselves as well?
if (IsProxy())
{
AZ_Assert(m_persistentName.Get().c_str(), "Received NeighborReplica with missing persistent name!");
@@ -52,7 +52,7 @@ namespace Neighborhood {
//---------------------------------------------------------------------
void NeighborReplica::OnReplicaDeactivate(const GridMate::ReplicaContext& /*rc*/)
{
// TODO: Should we send the message to ourselves as well (master)?
// TODO: Should we send the message to ourselves as well?
if (IsProxy())
{
EBUS_EVENT(NeighborhoodBus, OnNodeLeft, *this);
@@ -30,7 +30,8 @@ namespace AzFramework
AZ_CVAR(float, ed_cameraSystemOrbitDollyScrollSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemOrbitDollyCursorSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemScrollTranslateSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 60.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMinOrbitDistance, 6.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemLookSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemTranslateSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemRotateSpeed, 0.005f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
@@ -532,20 +533,39 @@ namespace AzFramework
if (Beginning())
{
float hit_distance = 0.0f;
AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_cameraSystemDefaultPlaneHeight))
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY(), hit_distance);
const auto hasLookAt = [&nextCamera, &targetCamera, lookAtFn = m_lookAtFn] {
if (lookAtFn)
{
if (const auto lookAt = lookAtFn())
{
auto transform = AZ::Transform::CreateLookAt(targetCamera.m_lookAt, *lookAt);
nextCamera.m_lookDist = -lookAt->GetDistance(targetCamera.m_lookAt);
UpdateCameraFromTransform(nextCamera, transform);
if (hit_distance > 0.0f)
return true;
}
}
return false;
}();
if (!hasLookAt)
{
hit_distance = AZStd::min<float>(hit_distance, ed_cameraSystemMaxOrbitDistance);
nextCamera.m_lookDist = -hit_distance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * hit_distance;
}
else
{
nextCamera.m_lookDist = -ed_cameraSystemMaxOrbitDistance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * ed_cameraSystemMaxOrbitDistance;
float hit_distance = 0.0f;
AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_cameraSystemDefaultPlaneHeight))
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY(), hit_distance);
if (hit_distance > 0.0f)
{
hit_distance = AZStd::min<float>(hit_distance, ed_cameraSystemMaxOrbitDistance);
nextCamera.m_lookDist = -hit_distance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * hit_distance;
}
else
{
nextCamera.m_lookDist = -ed_cameraSystemMinOrbitDistance;
nextCamera.m_lookAt =
targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * ed_cameraSystemMinOrbitDistance;
}
}
}
@@ -199,6 +199,7 @@ namespace AzFramework
public:
explicit RotateCameraInput(InputChannelId rotateChannelId);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -239,6 +240,7 @@ namespace AzFramework
public:
PanCameraInput(InputChannelId panChannelId, PanAxesFn panAxesFn);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -279,6 +281,7 @@ namespace AzFramework
public:
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
void ResetImpl() override;
@@ -348,6 +351,7 @@ namespace AzFramework
class OrbitDollyScrollCameraInput : public CameraInput
{
public:
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
};
@@ -357,6 +361,7 @@ namespace AzFramework
public:
explicit OrbitDollyCursorMoveCameraInput(InputChannelId dollyChannelId);
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
@@ -367,6 +372,7 @@ namespace AzFramework
class ScrollTranslationCameraInput : public CameraInput
{
public:
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
};
@@ -374,16 +380,32 @@ namespace AzFramework
class OrbitCameraInput : public CameraInput
{
public:
using LookAtFn = AZStd::function<AZStd::optional<AZ::Vector3>()>;
// CameraInput overrides ...
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
bool Exclusive() const override
{
return true;
}
bool Exclusive() const override;
Cameras m_orbitCameras;
//! Override the default behavior for how a look-at point is calculated.
void SetLookAtFn(const LookAtFn& lookAtFn);
private:
LookAtFn m_lookAtFn;
};
inline void OrbitCameraInput::SetLookAtFn(const LookAtFn& lookAtFn)
{
m_lookAtFn = lookAtFn;
}
inline bool OrbitCameraInput::Exclusive() const
{
return true;
}
struct WindowSize;
//! Map from a generic InputChannel event to a camera specific InputEvent.
@@ -0,0 +1,167 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzQtComponents/Utilities/SelectionProxyModel.h>
#include <QAbstractProxyModel>
namespace AzQtComponents
{
SelectionProxyModel::SelectionProxyModel(QItemSelectionModel* sourceSelectionModel, QAbstractProxyModel* proxyModel, QObject* parent)
: QItemSelectionModel(proxyModel, parent)
, m_sourceSelectionModel(sourceSelectionModel)
{
connect(sourceSelectionModel, &QItemSelectionModel::selectionChanged, this, &SelectionProxyModel::OnSourceSelectionChanged);
connect(sourceSelectionModel, &QItemSelectionModel::currentChanged, this, &SelectionProxyModel::OnSourceSelectionCurrentChanged);
connect(proxyModel, &QAbstractItemModel::rowsInserted, this, &SelectionProxyModel::OnProxyModelRowsInserted);
connect(this, &QItemSelectionModel::selectionChanged, this, &SelectionProxyModel::OnProxySelectionChanged);
// Find the chain of proxy models
QAbstractProxyModel* sourceProxyModel = proxyModel;
while (sourceProxyModel)
{
m_proxyModels.push_back(sourceProxyModel);
sourceProxyModel = qobject_cast<QAbstractProxyModel*>(sourceProxyModel->sourceModel());
}
const QItemSelection currentSelection = mapFromSource(m_sourceSelectionModel->selection());
QItemSelectionModel::select(currentSelection, QItemSelectionModel::ClearAndSelect);
const QModelIndex currentModelIndex = mapFromSource(m_sourceSelectionModel->currentIndex());
QItemSelectionModel::setCurrentIndex(currentModelIndex, QItemSelectionModel::ClearAndSelect);
}
void SelectionProxyModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
{
const QModelIndex sourcetIndex = mapToSource(index);
m_sourceSelectionModel->setCurrentIndex(sourcetIndex, command);
}
void SelectionProxyModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
{
const QModelIndex sourceIndex = mapToSource(index);
m_sourceSelectionModel->select(sourceIndex, command);
}
void SelectionProxyModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
{
const QItemSelection sourceSelection = mapToSource(selection);
m_sourceSelectionModel->select(sourceSelection, command);
}
void SelectionProxyModel::clear()
{
m_sourceSelectionModel->clear();
}
void SelectionProxyModel::reset()
{
m_sourceSelectionModel->reset();
}
void SelectionProxyModel::clearCurrentIndex()
{
m_sourceSelectionModel->clearCurrentIndex();
}
void SelectionProxyModel::OnSourceSelectionCurrentChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
{
QModelIndex targetCurrent = mapFromSource(current);
QItemSelectionModel::setCurrentIndex(targetCurrent, QItemSelectionModel::NoUpdate);
}
void SelectionProxyModel::OnSourceSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
QItemSelection targetSelected = mapFromSource(selected);
QItemSelection targetDeselected = mapFromSource(deselected);
QItemSelectionModel::select(targetSelected, QItemSelectionModel::Select);
QItemSelectionModel::select(targetDeselected, QItemSelectionModel::Deselect);
}
void SelectionProxyModel::OnProxySelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
const QItemSelection sourceSelected = mapToSource(selected);
const QItemSelection sourceDeselected = mapToSource(deselected);
// Disconnect from the selectionChanged signal in the source model to prevent recursion. We could also block the signals
// of the source selection model, but someone else may be connected to its signals and expect to get an update.
disconnect(m_sourceSelectionModel, &QItemSelectionModel::selectionChanged, this, &SelectionProxyModel::OnSourceSelectionChanged);
if (selected.empty() && deselected.empty())
{
// Force the signal to fire
emit m_sourceSelectionModel->selectionChanged({}, {});
}
else
{
m_sourceSelectionModel->select(sourceSelected, QItemSelectionModel::Select);
m_sourceSelectionModel->select(sourceDeselected, QItemSelectionModel::Deselect);
}
connect(m_sourceSelectionModel, &QItemSelectionModel::selectionChanged, this, &SelectionProxyModel::OnSourceSelectionChanged);
}
void SelectionProxyModel::OnProxyModelRowsInserted([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int first, [[maybe_unused]] int last)
{
QModelIndex sourceIndex = m_sourceSelectionModel->currentIndex();
QModelIndex targetIndex = mapFromSource(sourceIndex);
if (targetIndex != currentIndex())
{
QItemSelectionModel::setCurrentIndex(targetIndex, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
}
QItemSelection sourceSelection = m_sourceSelectionModel->selection();
QItemSelection targetSelection = mapFromSource(sourceSelection);
if (targetSelection != selection())
{
QItemSelectionModel::select(targetSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
}
QModelIndex SelectionProxyModel::mapFromSource(const QModelIndex& sourceIndex)
{
QModelIndex mappedIndex = sourceIndex;
for (QVector<QAbstractProxyModel*>::const_reverse_iterator itProxy = m_proxyModels.rbegin(); itProxy != m_proxyModels.rend(); ++itProxy)
{
mappedIndex = (*itProxy)->mapFromSource(mappedIndex);
}
return mappedIndex;
}
QItemSelection SelectionProxyModel::mapFromSource(const QItemSelection& sourceSelection)
{
QItemSelection mappedSelection = sourceSelection;
for (QVector<QAbstractProxyModel*>::const_reverse_iterator itProxy = m_proxyModels.rbegin(); itProxy != m_proxyModels.rend(); ++itProxy)
{
mappedSelection = (*itProxy)->mapSelectionFromSource(mappedSelection);
}
return mappedSelection;
}
QModelIndex SelectionProxyModel::mapToSource(const QModelIndex& targetIndex)
{
QModelIndex mappedIndex = targetIndex;
for (QVector<QAbstractProxyModel*>::const_iterator itProxy = m_proxyModels.begin(); itProxy != m_proxyModels.end(); ++itProxy)
{
mappedIndex = (*itProxy)->mapToSource(mappedIndex);
}
return mappedIndex;
}
QItemSelection SelectionProxyModel::mapToSource(const QItemSelection& targetSelection)
{
QItemSelection mappedSelection = targetSelection;
for (QVector<QAbstractProxyModel*>::const_iterator itProxy = m_proxyModels.begin(); itProxy != m_proxyModels.end(); ++itProxy)
{
mappedSelection = (*itProxy)->mapSelectionToSource(mappedSelection);
}
return mappedSelection;
}
} // namespace AzQtComponents
@@ -0,0 +1,66 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzQtComponents/AzQtComponentsAPI.h>
#include <QtCore/QItemSelectionModel>
#include <QVector>
#endif
QT_FORWARD_DECLARE_CLASS(QAbstractProxyModel)
namespace AzQtComponents
{
//! This class is a QItemSelectionModel that syncs through proxy models and maintains
//! selection. In Qt we can have a model being filtered/sorted by proxy models. If the
//! selection model is connected to the original model, the view needs a new selection
//! model that understands the filtering. This class does that conversion.
//! @Note: this class does not support changing proxy models (anywhere in the chain).
//! The class will have to be recreated with the new proxy model.
class AZ_QT_COMPONENTS_API SelectionProxyModel
: public QItemSelectionModel
{
Q_OBJECT // AUTOMOC
public:
SelectionProxyModel(QItemSelectionModel* sourceSelectionModel, QAbstractProxyModel* proxyModel, QObject* parent = nullptr);
void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) override;
void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) override;
void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override;
void clear() override;
void reset() override;
void clearCurrentIndex() override;
private slots:
void OnSourceSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void OnSourceSelectionCurrentChanged(const QModelIndex& current, const QModelIndex& previous);
void OnProxySelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void OnProxyModelRowsInserted(const QModelIndex& parent, int first, int last);
private:
QModelIndex mapFromSource(const QModelIndex& sourceIndex);
QItemSelection mapFromSource(const QItemSelection& sourceSelection);
QModelIndex mapToSource(const QModelIndex& targetIndex);
QItemSelection mapToSource(const QItemSelection& targetSelection);
// Contains the chain of proxy models that leads us to the real model. The outer-most proxy model
// comes first and is followed by inner proxy models.
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QVector<QAbstractProxyModel*> m_proxyModels;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
QItemSelectionModel* m_sourceSelectionModel;
};
} // namespace AzQtComponents
@@ -287,6 +287,8 @@ set(FILES
Utilities/ScreenUtilities.cpp
Utilities/ScreenGrabber.h
Utilities/ScopedCleanup.h
Utilities/SelectionProxyModel.cpp
Utilities/SelectionProxyModel.h
Utilities/TextUtilities.cpp
Utilities/TextUtilities.h
)
@@ -296,7 +296,7 @@ DefaultTrafficControl::OnReceived(TrafficControlConnectionId id, DataGramControl
if (m_maxRecvPackets != 0)
{
--cd->m_recvPacketAllowance;
if (cd->m_recvPacketAllowance == 0) // hit the limit -> let's blacklist connection
if (cd->m_recvPacketAllowance == 0) // hit the limit
{
cd->m_canReceiveData = false;
}
@@ -49,6 +49,7 @@
#include <AzToolsFramework/API/ComponentEntityObjectBus.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
// AtomToolsFramework
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
@@ -1238,6 +1239,20 @@ void EditorViewportWidget::SetViewportId(int id)
auto firstPersonWheelCamera = AZStd::make_shared<AzFramework::ScrollTranslationCameraInput>();
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
orbitCamera->SetLookAtFn([]() -> AZStd::optional<AZ::Vector3> {
AZStd::optional<AZ::Transform> manipulatorTransform;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
if (manipulatorTransform)
{
return manipulatorTransform->GetTranslation();
}
return {};
});
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraOrbitLookButton);
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation);
auto orbitDollyWheelCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
@@ -11,16 +11,23 @@
*/
#include <NewProjectSettingsScreen.h>
#include <PythonBindingsInterface.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFileDialog>
#include <QLabel>
#include <QLineEdit>
#include <QRadioButton>
#include <QButtonGroup>
#include <QPushButton>
#include <QSpacerItem>
#include <QStandardPaths>
namespace O3DE::ProjectManager
{
constexpr const char* k_pathProperty = "Path";
NewProjectSettingsScreen::NewProjectSettingsScreen(QWidget* parent)
: ScreenWidget(parent)
{
@@ -29,19 +36,27 @@ namespace O3DE::ProjectManager
QVBoxLayout* vLayout = new QVBoxLayout(this);
QLabel* projectNameLabel = new QLabel(this);
projectNameLabel->setText("Project Name");
QLabel* projectNameLabel = new QLabel(tr("Project Name"), this);
vLayout->addWidget(projectNameLabel);
QLineEdit* projectNameLineEdit = new QLineEdit(this);
vLayout->addWidget(projectNameLineEdit);
m_projectNameLineEdit = new QLineEdit(tr("New Project"), this);
vLayout->addWidget(m_projectNameLineEdit);
QLabel* projectPathLabel = new QLabel(this);
projectPathLabel->setText("Project Location");
QLabel* projectPathLabel = new QLabel(tr("Project Location"), this);
vLayout->addWidget(projectPathLabel);
QLineEdit* projectPathLineEdit = new QLineEdit(this);
vLayout->addWidget(projectPathLineEdit);
{
QHBoxLayout* projectPathLayout = new QHBoxLayout(this);
m_projectPathLineEdit = new QLineEdit(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), this);
projectPathLayout->addWidget(m_projectPathLineEdit);
QPushButton* browseButton = new QPushButton(tr("Browse"), this);
connect(browseButton, &QPushButton::pressed, this, &NewProjectSettingsScreen::HandleBrowseButton);
projectPathLayout->addWidget(browseButton);
vLayout->addLayout(projectPathLayout);
}
QLabel* projectTemplateLabel = new QLabel(this);
projectTemplateLabel->setText("Project Template");
@@ -50,14 +65,21 @@ namespace O3DE::ProjectManager
QHBoxLayout* templateLayout = new QHBoxLayout(this);
vLayout->addItem(templateLayout);
QRadioButton* projectTemplateStandardRadioButton = new QRadioButton(this);
projectTemplateStandardRadioButton->setText("Standard (Recommened)");
projectTemplateStandardRadioButton->setChecked(true);
templateLayout->addWidget(projectTemplateStandardRadioButton);
m_projectTemplateButtonGroup = new QButtonGroup(this);
auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates();
if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty())
{
for (auto projectTemplate : templatesResult.GetValue())
{
QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this);
radioButton->setProperty(k_pathProperty, projectTemplate.m_path);
m_projectTemplateButtonGroup->addButton(radioButton);
QRadioButton* projectTemplateEmptyRadioButton = new QRadioButton(this);
projectTemplateEmptyRadioButton->setText("Empty");
templateLayout->addWidget(projectTemplateEmptyRadioButton);
templateLayout->addWidget(radioButton);
}
m_projectTemplateButtonGroup->buttons().first()->setChecked(true);
}
QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
vLayout->addItem(verticalSpacer);
@@ -76,7 +98,57 @@ namespace O3DE::ProjectManager
QString NewProjectSettingsScreen::GetNextButtonText()
{
return "Create Project";
return tr("Next");
}
void NewProjectSettingsScreen::HandleBrowseButton()
{
QString defaultPath = m_projectPathLineEdit->text();
if (defaultPath.isEmpty())
{
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("New project path"), defaultPath));
if (!directory.isEmpty())
{
m_projectPathLineEdit->setText(directory);
}
}
ProjectInfo NewProjectSettingsScreen::GetProjectInfo()
{
ProjectInfo projectInfo;
projectInfo.m_projectName = m_projectNameLineEdit->text();
projectInfo.m_path = QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + projectInfo.m_projectName);
return projectInfo;
}
QString NewProjectSettingsScreen::GetProjectTemplatePath()
{
return m_projectTemplateButtonGroup->checkedButton()->property(k_pathProperty).toString();
}
bool NewProjectSettingsScreen::Validate()
{
bool projectNameIsValid = true;
if (m_projectNameLineEdit->text().isEmpty())
{
projectNameIsValid = false;
}
bool projectPathIsValid = true;
if (m_projectPathLineEdit->text().isEmpty())
{
projectPathIsValid = false;
}
QDir path(QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + m_projectNameLineEdit->text()));
if (path.exists() && !path.isEmpty())
{
projectPathIsValid = false;
}
return projectNameIsValid && projectPathIsValid;
}
} // namespace O3DE::ProjectManager
@@ -13,8 +13,12 @@
#if !defined(Q_MOC_RUN)
#include <ScreenWidget.h>
#include <ProjectInfo.h>
#endif
QT_FORWARD_DECLARE_CLASS(QButtonGroup)
QT_FORWARD_DECLARE_CLASS(QLineEdit)
namespace O3DE::ProjectManager
{
class NewProjectSettingsScreen
@@ -25,6 +29,19 @@ namespace O3DE::ProjectManager
~NewProjectSettingsScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
QString GetNextButtonText() override;
ProjectInfo GetProjectInfo();
QString GetProjectTemplatePath();
bool Validate();
protected slots:
void HandleBrowseButton();
private:
QLineEdit* m_projectNameLineEdit;
QLineEdit* m_projectPathLineEdit;
QButtonGroup* m_projectTemplateButtonGroup;
};
} // namespace O3DE::ProjectManager
@@ -14,12 +14,11 @@
namespace O3DE::ProjectManager
{
ProjectInfo::ProjectInfo(const QString& path, const QString& projectName, const QString& productName, const AZ::Uuid projectId,
ProjectInfo::ProjectInfo(const QString& path, const QString& projectName, const QString& displayName,
const QString& imagePath, const QString& backgroundImagePath, bool isNew)
: m_path(path)
, m_projectName(projectName)
, m_productName(productName)
, m_projectId(projectId)
, m_displayName(displayName)
, m_imagePath(imagePath)
, m_backgroundImagePath(backgroundImagePath)
, m_isNew(isNew)
@@ -28,6 +27,6 @@ namespace O3DE::ProjectManager
bool ProjectInfo::IsValid() const
{
return !m_path.isEmpty() && !m_projectId.IsNull();
return !m_path.isEmpty() && !m_projectName.isEmpty();
}
} // namespace O3DE::ProjectManager
@@ -23,7 +23,7 @@ namespace O3DE::ProjectManager
{
public:
ProjectInfo() = default;
ProjectInfo(const QString& path, const QString& projectName, const QString& productName, const AZ::Uuid projectId,
ProjectInfo(const QString& path, const QString& projectName, const QString& displayName,
const QString& imagePath, const QString& backgroundImagePath, bool isNew);
bool IsValid() const;
@@ -33,8 +33,7 @@ namespace O3DE::ProjectManager
// From project.json
QString m_projectName;
QString m_productName;
AZ::Uuid m_projectId;
QString m_displayName;
// Used on projects home screen
QString m_imagePath;
@@ -12,10 +12,13 @@
#include <ProjectSettingsCtrl.h>
#include <ScreensCtrl.h>
#include <PythonBindingsInterface.h>
#include <NewProjectSettingsScreen.h>
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include <QPushButton>
#include <QMessageBox>
namespace O3DE::ProjectManager
{
@@ -65,7 +68,8 @@ namespace O3DE::ProjectManager
}
void ProjectSettingsCtrl::HandleNextButton()
{
ProjectManagerScreen screenEnum = m_screensCtrl->GetCurrentScreen()->GetScreenEnum();
ScreenWidget* currentScreen = m_screensCtrl->GetCurrentScreen();
ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum();
auto screenOrderIter = m_screensOrder.begin();
for (; screenOrderIter != m_screensOrder.end(); ++screenOrderIter)
{
@@ -76,6 +80,22 @@ namespace O3DE::ProjectManager
}
}
if (screenEnum == ProjectManagerScreen::NewProjectSettings)
{
auto newProjectScreen = reinterpret_cast<NewProjectSettingsScreen*>(currentScreen);
if (newProjectScreen)
{
if (!newProjectScreen->Validate())
{
QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings"));
return;
}
m_projectInfo = newProjectScreen->GetProjectInfo();
m_projectTemplatePath = newProjectScreen->GetProjectTemplatePath();
}
}
if (screenOrderIter != m_screensOrder.end())
{
m_screensCtrl->ChangeToScreen(*screenOrderIter);
@@ -83,7 +103,15 @@ namespace O3DE::ProjectManager
}
else
{
emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome);
auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo);
if (result.IsSuccess())
{
emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome);
}
else
{
QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project."));
}
}
}
@@ -12,13 +12,13 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include "ProjectInfo.h"
#include <ScreenWidget.h>
#include <ScreensCtrl.h>
#include <QPushButton>
#endif
namespace O3DE::ProjectManager
{
class ProjectSettingsCtrl
@@ -40,6 +40,9 @@ namespace O3DE::ProjectManager
QPushButton* m_backButton;
QPushButton* m_nextButton;
QVector<ProjectManagerScreen> m_screensOrder;
QString m_projectTemplatePath;
ProjectInfo m_projectInfo;
};
} // namespace O3DE::ProjectManager
@@ -53,6 +53,173 @@ namespace Platform
#define Py_To_String(obj) obj.cast<std::string>().c_str()
#define Py_To_String_Optional(dict, key, default_string) dict.contains(key) ? Py_To_String(dict[key]) : default_string
namespace RedirectOutput
{
using RedirectOutputFunc = AZStd::function<void(const char*)>;
struct RedirectOutput
{
PyObject_HEAD RedirectOutputFunc write;
};
PyObject* RedirectWrite(PyObject* self, PyObject* args)
{
std::size_t written(0);
RedirectOutput* selfimpl = reinterpret_cast<RedirectOutput*>(self);
if (selfimpl->write)
{
char* data;
if (!PyArg_ParseTuple(args, "s", &data))
{
return PyLong_FromSize_t(0);
}
selfimpl->write(data);
written = strlen(data);
}
return PyLong_FromSize_t(written);
}
PyObject* RedirectFlush([[maybe_unused]] PyObject* self,[[maybe_unused]] PyObject* args)
{
// no-op
return Py_BuildValue("");
}
PyMethodDef RedirectMethods[] = {
{"write", RedirectWrite, METH_VARARGS, "sys.stdout.write"},
{"flush", RedirectFlush, METH_VARARGS, "sys.stdout.flush"},
{"write", RedirectWrite, METH_VARARGS, "sys.stderr.write"},
{"flush", RedirectFlush, METH_VARARGS, "sys.stderr.flush"},
{0, 0, 0, 0} // sentinel
};
PyTypeObject RedirectOutputType = {
PyVarObject_HEAD_INIT(0, 0) "azlmbr_redirect.RedirectOutputType", // tp_name
sizeof(RedirectOutput), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"azlmbr_redirect objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
RedirectMethods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
PyModuleDef RedirectOutputModule = {
PyModuleDef_HEAD_INIT, "azlmbr_redirect", 0, -1, 0,
};
// Internal state
PyObject* g_redirect_stdout = nullptr;
PyObject* g_redirect_stdout_saved = nullptr;
PyObject* g_redirect_stderr = nullptr;
PyObject* g_redirect_stderr_saved = nullptr;
PyMODINIT_FUNC PyInit_RedirectOutput(void)
{
g_redirect_stdout = nullptr;
g_redirect_stdout_saved = nullptr;
g_redirect_stderr = nullptr;
g_redirect_stderr_saved = nullptr;
RedirectOutputType.tp_new = PyType_GenericNew;
if (PyType_Ready(&RedirectOutputType) < 0)
{
return 0;
}
PyObject* redirectModule = PyModule_Create(&RedirectOutputModule);
if (redirectModule)
{
Py_INCREF(&RedirectOutputType);
PyModule_AddObject(redirectModule, "Redirect", reinterpret_cast<PyObject*>(&RedirectOutputType));
}
return redirectModule;
}
void SetRedirection(const char* funcname, PyObject*& saved, PyObject*& current, RedirectOutputFunc func)
{
if (PyType_Ready(&RedirectOutputType) < 0)
{
AZ_Warning("python", false, "RedirectOutputType not ready!");
return;
}
if (!current)
{
saved = PySys_GetObject(funcname); // borrowed
current = RedirectOutputType.tp_new(&RedirectOutputType, 0, 0);
}
RedirectOutput* redirectOutput = reinterpret_cast<RedirectOutput*>(current);
redirectOutput->write = func;
PySys_SetObject(funcname, current);
}
void ResetRedirection(const char* funcname, PyObject*& saved, PyObject*& current)
{
if (current)
{
PySys_SetObject(funcname, saved);
}
Py_XDECREF(current);
current = nullptr;
}
PyObject* s_RedirectModule = nullptr;
void Intialize(PyObject* module)
{
s_RedirectModule = module;
SetRedirection("stdout", g_redirect_stdout_saved, g_redirect_stdout, []([[maybe_unused]] const char* msg) {
AZ_TracePrintf("Python", msg);
});
SetRedirection("stderr", g_redirect_stderr_saved, g_redirect_stderr, []([[maybe_unused]] const char* msg) {
AZ_TracePrintf("Python", msg);
});
PySys_WriteStdout("RedirectOutput installed");
}
void Shutdown()
{
ResetRedirection("stdout", g_redirect_stdout_saved, g_redirect_stdout);
ResetRedirection("stderr", g_redirect_stderr_saved, g_redirect_stderr);
Py_XDECREF(s_RedirectModule);
s_RedirectModule = nullptr;
}
} // namespace RedirectOutput
namespace O3DE::ProjectManager
{
PythonBindings::PythonBindings(const AZ::IO::PathView& enginePath)
@@ -92,6 +259,8 @@ namespace O3DE::ProjectManager
AZ_TracePrintf("python", "Py_GetExecPrefix=%ls \n", Py_GetExecPrefix());
AZ_TracePrintf("python", "Py_GetProgramFullPath=%ls \n", Py_GetProgramFullPath());
PyImport_AppendInittab("azlmbr_redirect", RedirectOutput::PyInit_RedirectOutput);
try
{
// ignore system location for sites site-packages
@@ -101,6 +270,8 @@ namespace O3DE::ProjectManager
const bool initializeSignalHandlers = true;
pybind11::initialize_interpreter(initializeSignalHandlers);
RedirectOutput::Intialize(PyImport_ImportModule("azlmbr_redirect"));
// Acquire GIL before calling Python code
AZStd::lock_guard<decltype(m_lock)> lock(m_lock);
pybind11::gil_scoped_acquire acquire;
@@ -113,6 +284,7 @@ namespace O3DE::ProjectManager
// import required modules
m_registration = pybind11::module::import("cmake.Tools.registration");
m_engineTemplate = pybind11::module::import("cmake.Tools.engine_template");
return result == 0 && !PyErr_Occurred();
} catch ([[maybe_unused]] const std::exception& e)
@@ -126,6 +298,7 @@ namespace O3DE::ProjectManager
{
if (Py_IsInitialized())
{
RedirectOutput::Shutdown();
pybind11::finalize_interpreter();
}
else
@@ -204,9 +377,28 @@ namespace O3DE::ProjectManager
}
}
AZ::Outcome<ProjectInfo> PythonBindings::CreateProject([[maybe_unused]] const ProjectTemplateInfo& projectTemplate,[[maybe_unused]] const ProjectInfo& projectInfo)
AZ::Outcome<ProjectInfo> PythonBindings::CreateProject(const QString& projectTemplatePath, const ProjectInfo& projectInfo)
{
return AZ::Failure();
ProjectInfo createdProjectInfo;
bool result = ExecuteWithLock([&] {
pybind11::str projectPath = projectInfo.m_path.toStdString();
pybind11::str templatePath = projectTemplatePath.toStdString();
auto createProjectResult = m_engineTemplate.attr("create_project")(projectPath, templatePath);
if (createProjectResult.cast<int>() == 0)
{
createdProjectInfo = ProjectInfoFromPath(projectPath);
}
});
if (!result || !createdProjectInfo.IsValid())
{
return AZ::Failure();
}
else
{
return AZ::Success(AZStd::move(createdProjectInfo));
}
}
AZ::Outcome<ProjectInfo> PythonBindings::GetProject(const QString& path)
@@ -275,10 +467,8 @@ namespace O3DE::ProjectManager
{
try
{
// required fields
projectInfo.m_productName = Py_To_String(projectData["product_name"]);
projectInfo.m_projectName = Py_To_String(projectData["project_name"]);
projectInfo.m_projectId = AZ::Uuid(Py_To_String(projectData["project_id"]));
projectInfo.m_displayName = Py_To_String_Optional(projectData,"display_name", projectInfo.m_projectName);
}
catch ([[maybe_unused]] const std::exception& e)
{
@@ -43,7 +43,7 @@ namespace O3DE::ProjectManager
AZ::Outcome<QVector<GemInfo>> GetGems() override;
// Project
AZ::Outcome<ProjectInfo> CreateProject(const ProjectTemplateInfo& projectTemplate, const ProjectInfo& projectInfo) override;
AZ::Outcome<ProjectInfo> CreateProject(const QString& projectTemplatePath, const ProjectInfo& projectInfo) override;
AZ::Outcome<ProjectInfo> GetProject(const QString& path) override;
AZ::Outcome<QVector<ProjectInfo>> GetProjects() override;
bool UpdateProject(const ProjectInfo& projectInfo) override;
@@ -62,6 +62,7 @@ namespace O3DE::ProjectManager
bool StopPython();
AZ::IO::FixedMaxPath m_enginePath;
pybind11::handle m_engineTemplate;
AZStd::recursive_mutex m_lock;
pybind11::handle m_registration;
};
@@ -70,11 +70,11 @@ namespace O3DE::ProjectManager
/**
* Create a project
* @param projectTemplate the project template to use
* @param projectTemplatePath the path to the project template to use
* @param projectInfo the project info to use
* @return an outcome with ProjectInfo on success
*/
virtual AZ::Outcome<ProjectInfo> CreateProject(const ProjectTemplateInfo& projectTemplate, const ProjectInfo& projectInfo) = 0;
virtual AZ::Outcome<ProjectInfo> CreateProject(const QString& projectTemplatePath, const ProjectInfo& projectInfo) = 0;
/**
* Get info about a project
@@ -173,8 +173,7 @@ namespace AZ
return false;
}
const SliceComponent::SliceList& sliceList = sliceComponent->GetSlices();
AZ_Warning("Convert-Slice", sliceList.empty(), " Slice depends on other slices, this conversion will lose data.\n");
AZ_Warning("Convert-Slice", sliceComponent->GetSlices().empty(), " Slice depends on other slices, this conversion will lose data.\n");
// Create the Prefab with the entities from the slice
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sourceInstance(
@@ -10,8 +10,7 @@
*
*/
#ifndef DRILLER_WORKSPACE_SETTINGS_MASTER_H
#define DRILLER_WORKSPACE_SETTINGS_MASTER_H
#pragma once
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/unordered_map.h>
@@ -77,7 +76,3 @@ namespace Driller
SavedWorkspaceMap m_WorkspaceSaveData;
};
}
#pragma once
#endif // DRILLER_WORKSPACE_SETTINGS_MASTER_H