Merging latest main
This commit is contained in:
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -307,6 +307,8 @@ namespace AZ
|
||||
Asset(AssetLoadBehavior loadBehavior = AssetLoadBehavior::Default);
|
||||
/// Create an asset from a valid asset data (created asset), might not be loaded or currently loading.
|
||||
Asset(AssetData* assetData, AssetLoadBehavior loadBehavior);
|
||||
/// Create an asset from a valid asset data (created asset) and set the asset id for both, might not be loaded or currently loading.
|
||||
Asset(const AZ::Data::AssetId& id, AssetData* assetData, AssetLoadBehavior loadBehavior);
|
||||
/// Initialize asset pointer with id, type, and hint. No data construction will occur until QueueLoad is called.
|
||||
Asset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type, const AZStd::string& hint = AZStd::string());
|
||||
|
||||
@@ -787,6 +789,18 @@ namespace AZ
|
||||
SetData(assetData);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
template<class T>
|
||||
Asset<T>::Asset(const AssetId& id, AssetData* assetData, AssetLoadBehavior loadBehavior)
|
||||
: m_assetId(id)
|
||||
, m_assetType(azrtti_typeid<T>())
|
||||
, m_loadBehavior(loadBehavior)
|
||||
{
|
||||
AZ_Assert(!assetData->m_assetId.IsValid(), "Asset data already has an ID set.");
|
||||
assetData->m_assetId = id;
|
||||
SetData(assetData);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
template<class T>
|
||||
Asset<T>::Asset(const AssetId& id, const AZ::Data::AssetType& type, const AZStd::string& hint)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1740,7 +1740,10 @@ namespace AZ
|
||||
if (!iter->IsInstantiated())
|
||||
{
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
Data::Asset<SliceAsset> thisAsset = Data::AssetManager::Instance().FindAsset(GetMyAsset()->GetId(), AZ::Data::AssetLoadBehavior::Default);
|
||||
Data::Asset<SliceAsset> thisAsset = GetMyAsset()
|
||||
? Data::Asset<SliceAsset>(Data::AssetManager::Instance().FindAsset(
|
||||
GetMyAsset()->GetId(), AZ::Data::AssetLoadBehavior::Default))
|
||||
: Data::Asset<SliceAsset>();
|
||||
AZ_Warning("Slice", false, "Removing %d instances of slice asset %s from parent asset %s due to failed instantiation. "
|
||||
"Saving parent asset will result in loss of slice data.",
|
||||
iter->GetInstances().size(),
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/std/string/osstring.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace AZ
|
||||
|
||||
@@ -39,8 +39,8 @@ namespace AZ
|
||||
// Append .framework to the name of full path
|
||||
// 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(fileName + ".framework");
|
||||
AZ::IO::FixedMaxPathString fileName{ fullPath.Filename().Native() };
|
||||
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
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ namespace AzPhysics
|
||||
namespace
|
||||
{
|
||||
const float TimestepMin = 0.001f; //1000fps
|
||||
const float TimestepMax = 0.05f; //20fps
|
||||
const float TimestepMax = 0.1f; //10fps
|
||||
}
|
||||
|
||||
AZ_CLASS_ALLOCATOR_IMPL(SystemConfiguration, AZ::SystemAllocator, 0);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace AzPhysics
|
||||
|
||||
static constexpr float DefaultFixedTimestep = 0.0166667f; //! Value represents 1/60th or 60 FPS.
|
||||
|
||||
float m_maxTimestep = 1.f / 20.f; //!< Maximum fixed timestep in seconds to run the physics update.
|
||||
float m_maxTimestep = 0.1f; //!< Maximum fixed timestep in seconds to run the physics update (10FPS).
|
||||
float m_fixedTimestep = DefaultFixedTimestep; //!< Timestep in seconds to run the physics update. See DefaultFixedTimestep.
|
||||
|
||||
AZ::u64 m_raycastBufferSize = 32; //!< Maximum number of hits that will be returned from a raycast.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
//! SessionConnectionConfig
|
||||
//! The properties for handling join session request.
|
||||
struct SessionConnectionConfig
|
||||
{
|
||||
// A unique identifier for registered player in session.
|
||||
AZStd::string m_playerSessionId;
|
||||
|
||||
// The DNS identifier assigned to the instance that is running the session.
|
||||
AZStd::string m_dnsName;
|
||||
|
||||
// The IP address of the session.
|
||||
AZStd::string m_ipAddress;
|
||||
|
||||
// The port number for the session.
|
||||
uint16_t m_port;
|
||||
};
|
||||
|
||||
//! SessionConnectionConfig
|
||||
//! The properties for handling player connect/disconnect
|
||||
struct PlayerConnectionConfig
|
||||
{
|
||||
// A unique identifier for player connection.
|
||||
uint32_t m_playerConnectionId;
|
||||
|
||||
// A unique identifier for registered player in session.
|
||||
AZStd::string m_playerSessionId;
|
||||
};
|
||||
|
||||
//! ISessionHandlingClientRequests
|
||||
//! The session handling events to invoke multiplayer component handle the work on client side
|
||||
class ISessionHandlingClientRequests
|
||||
{
|
||||
public:
|
||||
// Handle the player join session process
|
||||
// @param sessionConnectionConfig The required properties to handle the player join session process
|
||||
// @return The result of player join session process
|
||||
virtual bool HandlePlayerJoinSession(const SessionConnectionConfig& sessionConnectionConfig) = 0;
|
||||
|
||||
// Handle the player leave session process
|
||||
virtual void HandlePlayerLeaveSession() = 0;
|
||||
};
|
||||
|
||||
//! ISessionHandlingServerRequests
|
||||
//! The session handling events to invoke server provider handle the work on server side
|
||||
class ISessionHandlingServerRequests
|
||||
{
|
||||
public:
|
||||
// Handle the destroy session process
|
||||
virtual void HandleDestroySession() = 0;
|
||||
|
||||
// Validate the player join session process
|
||||
// @param playerConnectionConfig The required properties to validate the player join session process
|
||||
// @return The result of player join session validation
|
||||
virtual bool ValidatePlayerJoinSession(const PlayerConnectionConfig& playerConnectionConfig) = 0;
|
||||
|
||||
// Handle the player leave session process
|
||||
// @param playerConnectionConfig The required properties to handle the player leave session process
|
||||
virtual void HandlePlayerLeaveSession(const PlayerConnectionConfig& playerConnectionConfig) = 0;
|
||||
};
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/Session/ISessionRequests.h>
|
||||
#include <AzFramework/Session/SessionConfig.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
void CreateSessionRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<CreateSessionRequest>()
|
||||
->Version(0)
|
||||
->Field("creatorId", &CreateSessionRequest::m_creatorId)
|
||||
->Field("sessionProperties", &CreateSessionRequest::m_sessionProperties)
|
||||
->Field("sessionName", &CreateSessionRequest::m_sessionName)
|
||||
->Field("maxPlayer", &CreateSessionRequest::m_maxPlayer)
|
||||
;
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<CreateSessionRequest>("CreateSessionRequest", "The container for CreateSession request parameters")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CreateSessionRequest::m_creatorId,
|
||||
"CreatorId", "A unique identifier for a player or entity creating the session")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CreateSessionRequest::m_sessionProperties,
|
||||
"SessionProperties", "A collection of custom properties for a session")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CreateSessionRequest::m_sessionName,
|
||||
"SessionName", "A descriptive label that is associated with a session")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &CreateSessionRequest::m_maxPlayer,
|
||||
"MaxPlayer", "The maximum number of players that can be connected simultaneously to the session")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SearchSessionsRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<SearchSessionsRequest>()
|
||||
->Version(0)
|
||||
->Field("filterExpression", &SearchSessionsRequest::m_filterExpression)
|
||||
->Field("sortExpression", &SearchSessionsRequest::m_sortExpression)
|
||||
->Field("maxResult", &SearchSessionsRequest::m_maxResult)
|
||||
->Field("nextToken", &SearchSessionsRequest::m_nextToken)
|
||||
;
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<SearchSessionsRequest>("SearchSessionsRequest", "The container for SearchSessions request parameters")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsRequest::m_filterExpression,
|
||||
"FilterExpression", "String containing the search criteria for the session search")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsRequest::m_sortExpression,
|
||||
"SortExpression", "Instructions on how to sort the search results")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsRequest::m_maxResult,
|
||||
"MaxResult", "The maximum number of results to return")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsRequest::m_nextToken,
|
||||
"NextToken", "A token that indicates the start of the next sequential page of results")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SearchSessionsResponse::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<SearchSessionsResponse>()
|
||||
->Version(0)
|
||||
->Field("sessionConfigs", &SearchSessionsResponse::m_sessionConfigs)
|
||||
->Field("nextToken", &SearchSessionsResponse::m_nextToken)
|
||||
;
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<SearchSessionsResponse>("SearchSessionsResponse", "The container for SearchSession request results")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsResponse::m_sessionConfigs,
|
||||
"SessionConfigs", "A collection of sessions that match the search criteria and sorted in specific order")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsResponse::m_nextToken,
|
||||
"NextToken", "A token that indicates the start of the next sequential page of results")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JoinSessionRequest::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<JoinSessionRequest>()
|
||||
->Version(0)
|
||||
->Field("sessionId", &JoinSessionRequest::m_sessionId)
|
||||
->Field("playerId", &JoinSessionRequest::m_playerId)
|
||||
->Field("playerData", &JoinSessionRequest::m_playerData)
|
||||
;
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<JoinSessionRequest>("JoinSessionRequest", "The container for JoinSession request parameters")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &JoinSessionRequest::m_sessionId,
|
||||
"SessionId", "A unique identifier for the session")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &JoinSessionRequest::m_playerId,
|
||||
"PlayerId", "A unique identifier for a player. Player IDs are developer-defined")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &JoinSessionRequest::m_playerData,
|
||||
"PlayerData", "Developer-defined information related to a player")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
struct SessionConfig;
|
||||
|
||||
//! CreateSessionRequest
|
||||
//! The container for CreateSession request parameters.
|
||||
struct CreateSessionRequest
|
||||
{
|
||||
AZ_RTTI(CreateSessionRequest, "{E39C2A45-89C9-4CFB-B337-9734DC798930}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
CreateSessionRequest() = default;
|
||||
virtual ~CreateSessionRequest() = default;
|
||||
|
||||
// A unique identifier for a player or entity creating the session.
|
||||
AZStd::string m_creatorId;
|
||||
|
||||
// A collection of custom properties for a session.
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> m_sessionProperties;
|
||||
|
||||
// A descriptive label that is associated with a session.
|
||||
AZStd::string m_sessionName;
|
||||
|
||||
// The maximum number of players that can be connected simultaneously to the session.
|
||||
uint64_t m_maxPlayer;
|
||||
};
|
||||
|
||||
//! SearchSessionsRequest
|
||||
//! The container for SearchSessions request parameters.
|
||||
struct SearchSessionsRequest
|
||||
{
|
||||
AZ_RTTI(SearchSessionsRequest, "{B49207A8-8549-4ADB-B7D9-D7A4932F9B4B}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
SearchSessionsRequest() = default;
|
||||
virtual ~SearchSessionsRequest() = default;
|
||||
|
||||
// String containing the search criteria for the session search. If no filter expression is included, the request returns results
|
||||
// for all active sessions.
|
||||
AZStd::string m_filterExpression;
|
||||
|
||||
// Instructions on how to sort the search results. If no sort expression is included, the request returns results in random order.
|
||||
AZStd::string m_sortExpression;
|
||||
|
||||
// The maximum number of results to return.
|
||||
uint8_t m_maxResult;
|
||||
|
||||
// A token that indicates the start of the next sequential page of results.
|
||||
AZStd::string m_nextToken;
|
||||
};
|
||||
|
||||
//! SearchSessionsResponse
|
||||
//! The container for SearchSession request results.
|
||||
struct SearchSessionsResponse
|
||||
{
|
||||
AZ_RTTI(SearchSessionsResponse, "{F93DE7DC-D381-4E08-8A3B-0B08F7C38714}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
SearchSessionsResponse() = default;
|
||||
virtual ~SearchSessionsResponse() = default;
|
||||
|
||||
// A collection of sessions that match the search criteria and sorted in specific order.
|
||||
AZStd::vector<SessionConfig> m_sessionConfigs;
|
||||
|
||||
// A token that indicates the start of the next sequential page of results.
|
||||
AZStd::string m_nextToken;
|
||||
};
|
||||
|
||||
//! JoinSessionRequest
|
||||
//! The container for JoinSession request parameters.
|
||||
struct JoinSessionRequest
|
||||
{
|
||||
AZ_RTTI(JoinSessionRequest, "{519769E8-3CDE-4385-A0D7-24DBB3685657}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
JoinSessionRequest() = default;
|
||||
virtual ~JoinSessionRequest() = default;
|
||||
|
||||
// A unique identifier for the session.
|
||||
AZStd::string m_sessionId;
|
||||
|
||||
// A unique identifier for a player. Player IDs are developer-defined.
|
||||
AZStd::string m_playerId;
|
||||
|
||||
// Developer-defined information related to a player.
|
||||
AZStd::string m_playerData;
|
||||
};
|
||||
|
||||
//! ISessionRequests
|
||||
//! Pure virtual session interface class to abstract the details of session handling from application code.
|
||||
class ISessionRequests
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ISessionRequests, "{D6C41A71-DD8D-47FE-8515-FAF90670AE2F}");
|
||||
|
||||
ISessionRequests() = default;
|
||||
virtual ~ISessionRequests() = default;
|
||||
|
||||
// Create a session for players to find and join.
|
||||
// @param createSessionRequest The request of CreateSession operation
|
||||
// @return The request id if session creation request succeeds; empty if it fails
|
||||
virtual AZStd::string CreateSession(const CreateSessionRequest& createSessionRequest) = 0;
|
||||
|
||||
// Retrieve all active sessions that match the given search criteria and sorted in specific order.
|
||||
// @param searchSessionsRequest The request of SearchSessions operation
|
||||
// @return The response of SearchSessions operation
|
||||
virtual SearchSessionsResponse SearchSessions(const SearchSessionsRequest& searchSessionsRequest) const = 0;
|
||||
|
||||
// Reserve an open player slot in a session, and perform connection from client to server.
|
||||
// @param joinSessionRequest The request of JoinSession operation
|
||||
// @return True if joining session succeeds; False otherwise
|
||||
virtual bool JoinSession(const JoinSessionRequest& joinSessionRequest) = 0;
|
||||
|
||||
// Disconnect player from session.
|
||||
virtual void LeaveSession() = 0;
|
||||
};
|
||||
|
||||
//! ISessionAsyncRequests
|
||||
//! Async version of ISessionRequests
|
||||
class ISessionAsyncRequests
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ISessionAsyncRequests, "{471542AF-96B9-4930-82FE-242A4E68432D}");
|
||||
|
||||
ISessionAsyncRequests() = default;
|
||||
virtual ~ISessionAsyncRequests() = default;
|
||||
|
||||
// CreateSession Async
|
||||
// @param createSessionRequest The request of CreateSession operation
|
||||
virtual void CreateSessionAsync(const CreateSessionRequest& createSessionRequest) = 0;
|
||||
|
||||
// SearchSessions Async
|
||||
// @param searchSessionsRequest The request of SearchSessions operation
|
||||
virtual void SearchSessionsAsync(const SearchSessionsRequest& searchSessionsRequest) const = 0;
|
||||
|
||||
// JoinSession Async
|
||||
// @param joinSessionRequest The request of JoinSession operation
|
||||
virtual void JoinSessionAsync(const JoinSessionRequest& joinSessionRequest) = 0;
|
||||
|
||||
// LeaveSession Async
|
||||
virtual void LeaveSessionAsync() = 0;
|
||||
};
|
||||
|
||||
//! SessionAsyncRequestNotifications
|
||||
//! The notifications correspond to session async requests
|
||||
class SessionAsyncRequestNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// OnCreateSessionAsyncComplete is fired once CreateSessionAsync completes
|
||||
// @param createSessionResponse The request id if session creation request succeeds; empty if it fails
|
||||
virtual void OnCreateSessionAsyncComplete(const AZStd::string& createSessionReponse) = 0;
|
||||
|
||||
// OnSearchSessionsAsyncComplete is fired once SearchSessionsAsync completes
|
||||
// @param searchSessionsResponse The response of SearchSessions call
|
||||
virtual void OnSearchSessionsAsyncComplete(const SearchSessionsResponse& searchSessionsResponse) = 0;
|
||||
|
||||
// OnJoinSessionAsyncComplete is fired once JoinSessionAsync completes
|
||||
// @param joinSessionsResponse True if joining session succeeds; False otherwise
|
||||
virtual void OnJoinSessionAsyncComplete(bool joinSessionsResponse) = 0;
|
||||
|
||||
// OnLeaveSessionAsyncComplete is fired once LeaveSessionAsync completes
|
||||
virtual void OnLeaveSessionAsyncComplete() = 0;
|
||||
};
|
||||
using SessionAsyncRequestNotificationBus = AZ::EBus<SessionAsyncRequestNotifications>;
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/Session/SessionConfig.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
void SessionConfig::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<SessionConfig>()
|
||||
->Version(0)
|
||||
->Field("creationTime", &SessionConfig::m_creationTime)
|
||||
->Field("terminationTime", &SessionConfig::m_terminationTime)
|
||||
->Field("creatorId", &SessionConfig::m_creatorId)
|
||||
->Field("sessionProperties", &SessionConfig::m_sessionProperties)
|
||||
->Field("sessionId", &SessionConfig::m_sessionId)
|
||||
->Field("sessionName", &SessionConfig::m_sessionName)
|
||||
->Field("dnsName", &SessionConfig::m_dnsName)
|
||||
->Field("ipAddress", &SessionConfig::m_ipAddress)
|
||||
->Field("port", &SessionConfig::m_port)
|
||||
->Field("maxPlayer", &SessionConfig::m_maxPlayer)
|
||||
->Field("currentPlayer", &SessionConfig::m_currentPlayer)
|
||||
->Field("status", &SessionConfig::m_status)
|
||||
->Field("statusReason", &SessionConfig::m_statusReason)
|
||||
;
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<SessionConfig>("SessionConfig", "Properties describing a session")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_creationTime,
|
||||
"CreationTime", "A time stamp indicating when this session was created. Format is a number expressed in Unix time as milliseconds.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_terminationTime,
|
||||
"TerminationTime", "A time stamp indicating when this data object was terminated. Same format as creation time.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_creatorId,
|
||||
"CreatorId", "A unique identifier for a player or entity creating the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionProperties,
|
||||
"SessionProperties", "A collection of custom properties for a session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionId,
|
||||
"SessionId", "A unique identifier for the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionName,
|
||||
"SessionName", "A descriptive label that is associated with a session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_dnsName,
|
||||
"DnsName", "The DNS identifier assigned to the instance that is running the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_ipAddress,
|
||||
"IpAddress", "The IP address of the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_port,
|
||||
"Port", "The port number for the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_maxPlayer,
|
||||
"MaxPlayer", "The maximum number of players that can be connected simultaneously to the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_currentPlayer,
|
||||
"CurrentPlayer", "Number of players currently in the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_status,
|
||||
"Status", "Current status of the session.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_statusReason,
|
||||
"StatusReason", "Provides additional information about session status.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
//! SessionConfig
|
||||
//! Properties describing a session.
|
||||
struct SessionConfig
|
||||
{
|
||||
AZ_RTTI(SessionConfig, "{992DD4BE-8BA5-4071-8818-B99FD2952086}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
SessionConfig() = default;
|
||||
virtual ~SessionConfig() = default;
|
||||
|
||||
// A time stamp indicating when this session was created. Format is a number expressed in Unix time as milliseconds.
|
||||
uint64_t m_creationTime;
|
||||
|
||||
// A time stamp indicating when this data object was terminated. Same format as creation time.
|
||||
uint64_t m_terminationTime;
|
||||
|
||||
// A unique identifier for a player or entity creating the session.
|
||||
AZStd::string m_creatorId;
|
||||
|
||||
// A collection of custom properties for a session.
|
||||
AZStd::unordered_map<AZStd::string, AZStd::string> m_sessionProperties;
|
||||
|
||||
// A unique identifier for the session.
|
||||
AZStd::string m_sessionId;
|
||||
|
||||
// A descriptive label that is associated with a session.
|
||||
AZStd::string m_sessionName;
|
||||
|
||||
// The DNS identifier assigned to the instance that is running the session.
|
||||
AZStd::string m_dnsName;
|
||||
|
||||
// The IP address of the session.
|
||||
AZStd::string m_ipAddress;
|
||||
|
||||
// The port number for the session.
|
||||
uint16_t m_port;
|
||||
|
||||
// The maximum number of players that can be connected simultaneously to the session.
|
||||
uint64_t m_maxPlayer;
|
||||
|
||||
// Number of players currently in the session.
|
||||
uint64_t m_currentPlayer;
|
||||
|
||||
// Current status of the session.
|
||||
AZStd::string m_status;
|
||||
|
||||
// Provides additional information about session status.
|
||||
AZStd::string m_statusReason;
|
||||
};
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
struct SessionConfig;
|
||||
|
||||
//! SessionNotifications
|
||||
//! The session notifications to listen for performing required operations
|
||||
class SessionNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// OnSessionHealthCheck is fired in health check process
|
||||
// @return The result of all OnSessionHealthCheck
|
||||
virtual bool OnSessionHealthCheck() = 0;
|
||||
|
||||
// OnCreateSessionBegin is fired at the beginning of session creation
|
||||
// @param sessionConfig The properties to describe a session
|
||||
// @return The result of all OnCreateSessionBegin notifications
|
||||
virtual bool OnCreateSessionBegin(const SessionConfig& sessionConfig) = 0;
|
||||
|
||||
// OnDestroySessionBegin is fired at the beginning of session termination
|
||||
// @return The result of all OnDestroySessionBegin notifications
|
||||
virtual bool OnDestroySessionBegin() = 0;
|
||||
};
|
||||
using SessionNotificationBus = AZ::EBus<SessionNotifications>;
|
||||
} // namespace AzFramework
|
||||
@@ -84,6 +84,7 @@ namespace AzFramework
|
||||
};
|
||||
|
||||
using EntitySpawnCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstEntityContainerView)>;
|
||||
using EntityPreInsertionCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableEntityContainerView)>;
|
||||
using EntityDespawnCallback = AZStd::function<void(EntitySpawnTicket&)>;
|
||||
using ReloadSpawnableCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstEntityContainerView)>;
|
||||
using ListEntitiesCallback = AZStd::function<void(EntitySpawnTicket&, SpawnableConstEntityContainerView)>;
|
||||
@@ -110,7 +111,8 @@ namespace AzFramework
|
||||
//! @param completionCallback Optional callback that's called when spawning entities has completed. This can be called from
|
||||
//! a different thread than the one that made the function call. The returned list of entities contains all the newly
|
||||
//! created entities.
|
||||
virtual void SpawnAllEntities(EntitySpawnTicket& ticket, EntitySpawnCallback completionCallback = {}) = 0;
|
||||
virtual void SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback = {},
|
||||
EntitySpawnCallback completionCallback = {}) = 0;
|
||||
//! Spawn instances of some entities in the spawnable.
|
||||
//! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them.
|
||||
//! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from.
|
||||
@@ -118,7 +120,7 @@ namespace AzFramework
|
||||
//! a different thread than the one that made this function call. The returned list of entities contains all the newly
|
||||
//! created entities.
|
||||
virtual void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
|
||||
EntitySpawnCallback completionCallback = {}) = 0;
|
||||
EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) = 0;
|
||||
//! Removes all entities in the provided list from the environment.
|
||||
//! @param ticket The ticket previously used to spawn entities with.
|
||||
//! @param completionCallback Optional callback that's called when despawning entities has completed. This can be called from
|
||||
|
||||
@@ -11,20 +11,24 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Serialization/IdUtils.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Entity/GameEntityContextBus.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzFramework/Spawnable/SpawnableEntitiesManager.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, EntitySpawnCallback completionCallback)
|
||||
void SpawnableEntitiesManager::SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback,
|
||||
EntitySpawnCallback completionCallback)
|
||||
{
|
||||
SpawnAllEntitiesCommand queueEntry;
|
||||
queueEntry.m_ticket = &ticket;
|
||||
queueEntry.m_completionCallback = AZStd::move(completionCallback);
|
||||
queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback);
|
||||
{
|
||||
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
|
||||
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
|
||||
@@ -32,13 +36,15 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
|
||||
EntitySpawnCallback completionCallback)
|
||||
void SpawnableEntitiesManager::SpawnEntities(
|
||||
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
|
||||
EntityPreInsertionCallback preInsertionCallback, EntitySpawnCallback completionCallback)
|
||||
{
|
||||
SpawnEntitiesCommand queueEntry;
|
||||
queueEntry.m_ticket = &ticket;
|
||||
queueEntry.m_entityIndices = AZStd::move(entityIndices);
|
||||
queueEntry.m_completionCallback = AZStd::move(completionCallback);
|
||||
queueEntry.m_preInsertionCallback = AZStd::move(preInsertionCallback);
|
||||
{
|
||||
AZStd::scoped_lock queueLock(m_pendingRequestQueueMutex);
|
||||
queueEntry.m_ticketId = GetTicketPayload<Ticket>(ticket).m_nextTicketId++;
|
||||
@@ -205,32 +211,85 @@ namespace AzFramework
|
||||
AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate);
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
clone->SetId(AZ::Entity::MakeId());
|
||||
|
||||
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate,
|
||||
EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext)
|
||||
{
|
||||
return AZ::IdUtils::Remapper<AZ::EntityId>::CloneObjectAndGenerateNewIdsAndFixRefs(
|
||||
&entityTemplate, templateToCloneEntityIdMap, &serializeContext);
|
||||
}
|
||||
|
||||
bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext)
|
||||
{
|
||||
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
|
||||
if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId)
|
||||
{
|
||||
size_t spawnedEntitiesCount = ticket.m_spawnedEntities.size();
|
||||
AZStd::vector<AZ::Entity*>& spawnedEntities = ticket.m_spawnedEntities;
|
||||
AZStd::vector<size_t>& spawnedEntityIndices = ticket.m_spawnedEntityIndices;
|
||||
|
||||
const Spawnable::EntityList& entities = ticket.m_spawnable->GetEntities();
|
||||
size_t entitiesSize = entities.size();
|
||||
ticket.m_spawnedEntities.reserve(ticket.m_spawnedEntities.size() + entitiesSize);
|
||||
ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesSize);
|
||||
// Keep track how many entities there were in the array initially
|
||||
size_t spawnedEntitiesInitialCount = spawnedEntities.size();
|
||||
|
||||
for(size_t i=0; i<entitiesSize; ++i)
|
||||
// These are 'template' entities we'll be cloning from
|
||||
const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities();
|
||||
size_t entitiesToSpawnSize = entitiesToSpawn.size();
|
||||
|
||||
// Map keeps track of ids from template (spawnable) to clone (instance)
|
||||
// Allowing patch ups of fields referring to entityIds outside of a given entity
|
||||
EntityIdMap templateToCloneEntityIdMap;
|
||||
|
||||
// Reserve buffers
|
||||
spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize);
|
||||
spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize);
|
||||
templateToCloneEntityIdMap.reserve(entitiesToSpawnSize);
|
||||
|
||||
// Mark all indices as spawned
|
||||
for (size_t i = 0; i < entitiesToSpawnSize; ++i)
|
||||
{
|
||||
ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[i], serializeContext));
|
||||
ticket.m_spawnedEntityIndices.push_back(i);
|
||||
const AZ::Entity& entityTemplate = *entitiesToSpawn[i];
|
||||
|
||||
AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext);
|
||||
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
|
||||
spawnedEntities.emplace_back(clone);
|
||||
spawnedEntityIndices.push_back(i);
|
||||
}
|
||||
|
||||
// loadAll is true if every entity has been spawned only once
|
||||
if (spawnedEntities.size() == entitiesToSpawnSize)
|
||||
{
|
||||
ticket.m_loadAll = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Case where there were already spawns from a previous request
|
||||
ticket.m_loadAll = false;
|
||||
}
|
||||
|
||||
// Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context.
|
||||
if (request.m_preInsertionCallback)
|
||||
{
|
||||
request.m_preInsertionCallback(*request.m_ticket, SpawnableEntityContainerView(
|
||||
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
|
||||
}
|
||||
|
||||
// Add to the game context, now the entities are active
|
||||
AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(),
|
||||
[](AZ::Entity* entity)
|
||||
{
|
||||
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity);
|
||||
});
|
||||
|
||||
// Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context.
|
||||
if (request.m_completionCallback)
|
||||
{
|
||||
request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView(
|
||||
ticket.m_spawnedEntities.begin() + spawnedEntitiesCount, ticket.m_spawnedEntities.end()));
|
||||
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
|
||||
}
|
||||
|
||||
m_onSpawnedEvent.Signal(ticket.m_spawnable);
|
||||
@@ -249,24 +308,56 @@ namespace AzFramework
|
||||
Ticket& ticket = GetTicketPayload<Ticket>(*request.m_ticket);
|
||||
if (ticket.m_spawnable.IsReady() && request.m_ticketId == ticket.m_currentTicketId)
|
||||
{
|
||||
size_t spawnedEntitiesCount = ticket.m_spawnedEntities.size();
|
||||
AZStd::vector<AZ::Entity*>& spawnedEntities = ticket.m_spawnedEntities;
|
||||
AZStd::vector<size_t>& spawnedEntityIndices = ticket.m_spawnedEntityIndices;
|
||||
|
||||
const Spawnable::EntityList& entities = ticket.m_spawnable->GetEntities();
|
||||
size_t entitiesSize = entities.size();
|
||||
ticket.m_spawnedEntities.reserve(ticket.m_spawnedEntities.size() + entitiesSize);
|
||||
ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesSize);
|
||||
// Keep track how many entities there were in the array initially
|
||||
size_t spawnedEntitiesInitialCount = spawnedEntities.size();
|
||||
|
||||
// These are 'template' entities we'll be cloning from
|
||||
const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities();
|
||||
size_t entitiesToSpawnSize = request.m_entityIndices.size();
|
||||
|
||||
spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize);
|
||||
spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize);
|
||||
|
||||
for (size_t index : request.m_entityIndices)
|
||||
{
|
||||
ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[index], serializeContext));
|
||||
ticket.m_spawnedEntityIndices.push_back(index);
|
||||
if (index < entitiesToSpawn.size())
|
||||
{
|
||||
const AZ::Entity& entityTemplate = *entitiesToSpawn[index];
|
||||
|
||||
AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate);
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
clone->SetId(AZ::Entity::MakeId());
|
||||
|
||||
spawnedEntities.push_back(clone);
|
||||
spawnedEntityIndices.push_back(index);
|
||||
|
||||
}
|
||||
}
|
||||
ticket.m_loadAll = false;
|
||||
|
||||
// Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context.
|
||||
if (request.m_preInsertionCallback)
|
||||
{
|
||||
request.m_preInsertionCallback(
|
||||
*request.m_ticket,
|
||||
SpawnableEntityContainerView(
|
||||
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
|
||||
}
|
||||
|
||||
// Add to the game context, now the entities are active
|
||||
AZStd::for_each(ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end(),
|
||||
[](AZ::Entity* entity)
|
||||
{
|
||||
GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, entity);
|
||||
});
|
||||
|
||||
if (request.m_completionCallback)
|
||||
{
|
||||
request.m_completionCallback(*request.m_ticket, SpawnableConstEntityContainerView(
|
||||
ticket.m_spawnedEntities.begin() + spawnedEntitiesCount, ticket.m_spawnedEntities.end()));
|
||||
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
|
||||
}
|
||||
|
||||
m_onSpawnedEvent.Signal(ticket.m_spawnable);
|
||||
@@ -343,10 +434,23 @@ namespace AzFramework
|
||||
// to load every, simply start over.
|
||||
ticket.m_spawnedEntityIndices.clear();
|
||||
|
||||
size_t entitiesSize = entities.size();
|
||||
for (size_t i = 0; i < entitiesSize; ++i)
|
||||
size_t entitiesToSpawnSize = entities.size();
|
||||
|
||||
// Map keeps track of ids from template (spawnable) to clone (instance)
|
||||
// Allowing patch ups of fields referring to entityIds outside of a given entity
|
||||
EntityIdMap templateToCloneEntityIdMap;
|
||||
templateToCloneEntityIdMap.reserve(entitiesToSpawnSize);
|
||||
|
||||
// Mark all indices as spawned
|
||||
for (size_t i = 0; i < entitiesToSpawnSize; ++i)
|
||||
{
|
||||
ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[i], serializeContext));
|
||||
const AZ::Entity& entityTemplate = *entities[i];
|
||||
|
||||
AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext);
|
||||
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
|
||||
ticket.m_spawnedEntities.emplace_back(clone);
|
||||
ticket.m_spawnedEntityIndices.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace AZ
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
using EntityIdMap = AZStd::unordered_map<AZ::EntityId, AZ::EntityId>;
|
||||
|
||||
class SpawnableEntitiesManager
|
||||
: public SpawnableEntitiesInterface::Registrar
|
||||
{
|
||||
@@ -47,8 +49,8 @@ namespace AzFramework
|
||||
// The following functions are thread safe
|
||||
//
|
||||
|
||||
void SpawnAllEntities(EntitySpawnTicket& ticket, EntitySpawnCallback completionCallback = {}) override;
|
||||
void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices,
|
||||
void SpawnAllEntities(EntitySpawnTicket& ticket, EntityPreInsertionCallback preInsertionCallback = {}, EntitySpawnCallback completionCallback = {}) override;
|
||||
void SpawnEntities(EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, EntityPreInsertionCallback preInsertionCallback = {},
|
||||
EntitySpawnCallback completionCallback = {}) override;
|
||||
void DespawnAllEntities(EntitySpawnTicket& ticket, EntityDespawnCallback completionCallback = {}) override;
|
||||
|
||||
@@ -90,6 +92,7 @@ namespace AzFramework
|
||||
struct SpawnAllEntitiesCommand
|
||||
{
|
||||
EntitySpawnCallback m_completionCallback;
|
||||
EntityPreInsertionCallback m_preInsertionCallback;
|
||||
EntitySpawnTicket* m_ticket;
|
||||
uint32_t m_ticketId;
|
||||
};
|
||||
@@ -97,6 +100,7 @@ namespace AzFramework
|
||||
{
|
||||
AZStd::vector<size_t> m_entityIndices;
|
||||
EntitySpawnCallback m_completionCallback;
|
||||
EntityPreInsertionCallback m_preInsertionCallback;
|
||||
EntitySpawnTicket* m_ticket;
|
||||
uint32_t m_ticketId;
|
||||
};
|
||||
@@ -140,7 +144,11 @@ namespace AzFramework
|
||||
using Requests = AZStd::variant<SpawnAllEntitiesCommand, SpawnEntitiesCommand, DespawnAllEntitiesCommand, ReloadSpawnableCommand,
|
||||
ListEntitiesCommand, ClaimEntitiesCommand, BarrierCommand, DestroyTicketCommand>;
|
||||
|
||||
AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext);
|
||||
AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate,
|
||||
AZ::SerializeContext& serializeContext);
|
||||
|
||||
AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate,
|
||||
EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext);
|
||||
|
||||
bool ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext);
|
||||
bool ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -74,7 +74,6 @@ namespace AzFramework
|
||||
|
||||
void SetCameraClippingVolumeFromPerspectiveFovMatrixRH(CameraState& cameraState, const AZ::Matrix4x4& clipFromView)
|
||||
{
|
||||
const float m11 = clipFromView(1, 1);
|
||||
const float m22 = clipFromView(2, 2);
|
||||
const float m23 = clipFromView(2, 3);
|
||||
cameraState.m_nearClip = m23 / m22;
|
||||
@@ -84,7 +83,12 @@ namespace AzFramework
|
||||
{
|
||||
AZStd::swap(cameraState.m_nearClip, cameraState.m_farClip);
|
||||
}
|
||||
cameraState.m_fovOrZoom = 2 * (AZ::Constants::HalfPi - atanf(m11));
|
||||
cameraState.m_fovOrZoom = RetrieveFov(clipFromView);
|
||||
}
|
||||
|
||||
float RetrieveFov(const AZ::Matrix4x4& clipFromView)
|
||||
{
|
||||
return 2.0f * (AZ::Constants::HalfPi - AZStd::atan(clipFromView(1, 1)));
|
||||
}
|
||||
|
||||
void CameraState::Reflect(AZ::SerializeContext& serializeContext)
|
||||
|
||||
@@ -17,54 +17,57 @@
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
/// Represents the camera state populated by the viewport camera.
|
||||
//! Represents the camera state populated by the viewport camera.
|
||||
struct CameraState
|
||||
{
|
||||
/// @cond
|
||||
//! @cond
|
||||
AZ_TYPE_INFO(CameraState, "{D309D934-044C-4BA8-91F1-EA3A45177A52}")
|
||||
CameraState() = default;
|
||||
/// @endcond
|
||||
//! @endcond
|
||||
|
||||
static void Reflect(AZ::SerializeContext& context);
|
||||
|
||||
/// Return the vertical fov of the camera when the view is in perspective.
|
||||
//! Return the vertical fov of the camera when the view is in perspective.
|
||||
float VerticalFovRadian() const { return m_fovOrZoom; }
|
||||
/// Return the zoom amount of the camera when the view is in orthographic.
|
||||
//! Return the zoom amount of the camera when the view is in orthographic.
|
||||
float Zoom() const { return m_fovOrZoom; }
|
||||
|
||||
AZ::Vector3 m_position = AZ::Vector3::CreateZero(); ///< World position of the camera.
|
||||
AZ::Vector3 m_forward = AZ::Vector3::CreateAxisY(); ///< Forward look direction of the camera (world space).
|
||||
AZ::Vector3 m_side = AZ::Vector3::CreateAxisX(); ///< Side vector of camera (orthogonal to forward and up).
|
||||
AZ::Vector3 m_up = AZ::Vector3::CreateAxisZ(); ///< Up vector of the camera (cameras frame - world space).
|
||||
AZ::Vector2 m_viewportSize = AZ::Vector2::CreateZero(); ///< Dimensions of the viewport.
|
||||
float m_nearClip = 0.01f; ///< Near clip plane of the camera.
|
||||
float m_farClip = 100.0f; ///< Far clip plane of the camera.
|
||||
float m_fovOrZoom = 0.0f; ///< Fov or zoom of camera depending on if it is using orthographic projection or not.
|
||||
bool m_orthographic = false; ///< Is the camera using orthographic projection or not.
|
||||
AZ::Vector3 m_position = AZ::Vector3::CreateZero(); //!< World position of the camera.
|
||||
AZ::Vector3 m_forward = AZ::Vector3::CreateAxisY(); //!< Forward look direction of the camera (world space).
|
||||
AZ::Vector3 m_side = AZ::Vector3::CreateAxisX(); //!< Side vector of camera (orthogonal to forward and up).
|
||||
AZ::Vector3 m_up = AZ::Vector3::CreateAxisZ(); //!< Up vector of the camera (cameras frame - world space).
|
||||
AZ::Vector2 m_viewportSize = AZ::Vector2::CreateZero(); //!< Dimensions of the viewport.
|
||||
float m_nearClip = 0.01f; //!< Near clip plane of the camera.
|
||||
float m_farClip = 100.0f; //!< Far clip plane of the camera.
|
||||
float m_fovOrZoom = 0.0f; //!< Fov or zoom of camera depending on if it is using orthographic projection or not.
|
||||
bool m_orthographic = false; //!< Is the camera using orthographic projection or not.
|
||||
};
|
||||
|
||||
/// Create a camera at the given transform with a specific viewport size.
|
||||
/// @note The near/far clip planes and fov are sensible default values - please
|
||||
/// use SetCameraClippingVolume to override them.
|
||||
//! Create a camera at the given transform with a specific viewport size.
|
||||
//! @note The near/far clip planes and fov are sensible default values - please
|
||||
//! use SetCameraClippingVolume to override them.
|
||||
CameraState CreateDefaultCamera(const AZ::Transform& transform, const AZ::Vector2& viewportSize);
|
||||
|
||||
/// Create a camera at the given position (no orientation) with a specific viewport size.
|
||||
/// @note The near/far clip planes and fov are sensible default values - please
|
||||
/// use SetCameraClippingVolume to override them.
|
||||
//! Create a camera at the given position (no orientation) with a specific viewport size.
|
||||
//! @note The near/far clip planes and fov are sensible default values - please
|
||||
//! use SetCameraClippingVolume to override them.
|
||||
CameraState CreateIdentityDefaultCamera(const AZ::Vector3& position, const AZ::Vector2& viewportSize);
|
||||
|
||||
/// Create a camera transformed by the given view to world matrix with a specific viewport size.
|
||||
/// @note The near/far clip planes and fov are sensible default values - please
|
||||
/// use SetCameraClippingVolume to override them.
|
||||
//! Create a camera transformed by the given view to world matrix with a specific viewport size.
|
||||
//! @note The near/far clip planes and fov are sensible default values - please
|
||||
//! use SetCameraClippingVolume to override them.
|
||||
CameraState CreateCameraFromWorldFromViewMatrix(const AZ::Matrix4x4& worldFromView, const AZ::Vector2& viewportSize);
|
||||
|
||||
/// Override the default near/far clipping planes and fov of the camera.
|
||||
//! Override the default near/far clipping planes and fov of the camera.
|
||||
void SetCameraClippingVolume(CameraState& cameraState, float nearPlane, float farPlane, float fovRad);
|
||||
|
||||
/// Override the default near/far clipping planes and fov of the camera by inferring them the specified right handed transform into clip space.
|
||||
//! Override the default near/far clipping planes and fov of the camera by inferring them the specified right handed transform into clip space.
|
||||
void SetCameraClippingVolumeFromPerspectiveFovMatrixRH(CameraState& cameraState, const AZ::Matrix4x4& clipFromView);
|
||||
|
||||
/// Set the transform for an existing camera.
|
||||
//! Retrieve the field of view (Fov) from the perspective projection matrix (view space to clip space).
|
||||
float RetrieveFov(const AZ::Matrix4x4& clipFromView);
|
||||
|
||||
//! Set the transform for an existing camera.
|
||||
void SetCameraTransform(CameraState& cameraState, const AZ::Transform& transform);
|
||||
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
/*
|
||||
* 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 <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
|
||||
{
|
||||
if (clickEvent == ClickEvent::Down)
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (m_tryBeginTime)
|
||||
{
|
||||
const std::chrono::duration<float> diff = now - m_tryBeginTime.value();
|
||||
if (diff.count() < m_doubleClickInterval)
|
||||
{
|
||||
return ClickOutcome::Nil;
|
||||
}
|
||||
}
|
||||
|
||||
m_detectionState = DetectionState::WaitingForMove;
|
||||
m_moveAccumulator = 0.0f;
|
||||
|
||||
m_tryBeginTime = now;
|
||||
}
|
||||
else if (clickEvent == ClickEvent::Up)
|
||||
{
|
||||
const auto clickOutcome = [detectionState = m_detectionState] {
|
||||
if (detectionState == DetectionState::WaitingForMove)
|
||||
{
|
||||
return ClickOutcome::Click;
|
||||
}
|
||||
if (detectionState == DetectionState::Moved)
|
||||
{
|
||||
return ClickOutcome::Release;
|
||||
}
|
||||
return ClickOutcome::Nil;
|
||||
}();
|
||||
|
||||
m_detectionState = DetectionState::Nil;
|
||||
return clickOutcome;
|
||||
}
|
||||
|
||||
if (m_detectionState == DetectionState::WaitingForMove)
|
||||
{
|
||||
// only allow the action to begin if the mouse has been moved a small amount
|
||||
m_moveAccumulator += ScreenVectorLength(cursorDelta);
|
||||
if (m_moveAccumulator > m_deadZone)
|
||||
{
|
||||
m_detectionState = DetectionState::Moved;
|
||||
return ClickOutcome::Move;
|
||||
}
|
||||
}
|
||||
|
||||
return ClickOutcome::Nil;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
/*
|
||||
* 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 <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
|
||||
{
|
||||
if (clickEvent == ClickEvent::Down)
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (m_tryBeginTime)
|
||||
{
|
||||
const std::chrono::duration<float> diff = now - m_tryBeginTime.value();
|
||||
if (diff.count() < m_doubleClickInterval)
|
||||
{
|
||||
return ClickOutcome::Nil;
|
||||
}
|
||||
}
|
||||
|
||||
m_detectionState = DetectionState::WaitingForMove;
|
||||
m_moveAccumulator = 0.0f;
|
||||
|
||||
m_tryBeginTime = now;
|
||||
}
|
||||
else if (clickEvent == ClickEvent::Up)
|
||||
{
|
||||
const auto clickOutcome = [detectionState = m_detectionState] {
|
||||
if (detectionState == DetectionState::WaitingForMove)
|
||||
{
|
||||
return ClickOutcome::Click;
|
||||
}
|
||||
if (detectionState == DetectionState::Moved)
|
||||
{
|
||||
return ClickOutcome::Release;
|
||||
}
|
||||
return ClickOutcome::Nil;
|
||||
}();
|
||||
|
||||
m_detectionState = DetectionState::Nil;
|
||||
return clickOutcome;
|
||||
}
|
||||
|
||||
if (m_detectionState == DetectionState::WaitingForMove)
|
||||
{
|
||||
// only allow the action to begin if the mouse has been moved a small amount
|
||||
m_moveAccumulator += ScreenVectorLength(cursorDelta);
|
||||
if (m_moveAccumulator > m_deadZone)
|
||||
{
|
||||
m_detectionState = DetectionState::Moved;
|
||||
return ClickOutcome::Move;
|
||||
}
|
||||
}
|
||||
|
||||
return ClickOutcome::Nil;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
|
||||
#include <AzCore/std/optional.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
//! Utility type to wrap a current and last cursor position.
|
||||
struct CursorState
|
||||
{
|
||||
//! Returns the delta between the current and last cursor position.
|
||||
[[nodiscard]] ScreenVector CursorDelta() const;
|
||||
//! Call this in a 'handle event' call to update the most recent cursor position.
|
||||
void SetCurrentPosition(const ScreenPoint& currentPosition);
|
||||
//! Call this in an 'update' call to copy the current cursor position to the last
|
||||
//! cursor position.
|
||||
void Update();
|
||||
|
||||
private:
|
||||
AZStd::optional<ScreenPoint> m_lastCursorPosition;
|
||||
AZStd::optional<ScreenPoint> m_currentCursorPosition;
|
||||
};
|
||||
|
||||
inline void CursorState::SetCurrentPosition(const ScreenPoint& currentPosition)
|
||||
{
|
||||
m_currentCursorPosition = currentPosition;
|
||||
}
|
||||
|
||||
inline ScreenVector CursorState::CursorDelta() const
|
||||
{
|
||||
return m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value()
|
||||
? m_currentCursorPosition.value() - m_lastCursorPosition.value()
|
||||
: ScreenVector(0, 0);
|
||||
}
|
||||
|
||||
inline void CursorState::Update()
|
||||
{
|
||||
if (m_currentCursorPosition.has_value())
|
||||
{
|
||||
m_lastCursorPosition = m_currentCursorPosition;
|
||||
}
|
||||
}
|
||||
} // namespace AzFramework
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
|
||||
#include <AzCore/std/optional.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
//! Utility type to wrap a current and last cursor position.
|
||||
struct CursorState
|
||||
{
|
||||
//! Returns the delta between the current and last cursor position.
|
||||
[[nodiscard]] ScreenVector CursorDelta() const;
|
||||
//! Call this in a 'handle event' call to update the most recent cursor position.
|
||||
void SetCurrentPosition(const ScreenPoint& currentPosition);
|
||||
//! Call this in an 'update' call to copy the current cursor position to the last
|
||||
//! cursor position.
|
||||
void Update();
|
||||
|
||||
private:
|
||||
AZStd::optional<ScreenPoint> m_lastCursorPosition;
|
||||
AZStd::optional<ScreenPoint> m_currentCursorPosition;
|
||||
};
|
||||
|
||||
inline void CursorState::SetCurrentPosition(const ScreenPoint& currentPosition)
|
||||
{
|
||||
m_currentCursorPosition = currentPosition;
|
||||
}
|
||||
|
||||
inline ScreenVector CursorState::CursorDelta() const
|
||||
{
|
||||
return m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value()
|
||||
? m_currentCursorPosition.value() - m_lastCursorPosition.value()
|
||||
: ScreenVector(0, 0);
|
||||
}
|
||||
|
||||
inline void CursorState::Update()
|
||||
{
|
||||
if (m_currentCursorPosition.has_value())
|
||||
{
|
||||
m_lastCursorPosition = m_currentCursorPosition;
|
||||
}
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -188,6 +188,12 @@ set(FILES
|
||||
Script/ScriptDebugMsgReflection.h
|
||||
Script/ScriptRemoteDebugging.cpp
|
||||
Script/ScriptRemoteDebugging.h
|
||||
Session/ISessionHandlingRequests.h
|
||||
Session/ISessionRequests.cpp
|
||||
Session/ISessionRequests.h
|
||||
Session/SessionConfig.cpp
|
||||
Session/SessionConfig.h
|
||||
Session/SessionNotifications.h
|
||||
StreamingInstall/StreamingInstall.h
|
||||
StreamingInstall/StreamingInstall.cpp
|
||||
StreamingInstall/StreamingInstallRequests.h
|
||||
|
||||
@@ -116,8 +116,8 @@ namespace AzNetworking
|
||||
|
||||
int32_t TcpSocket::Receive(uint8_t* outData, uint32_t size) const
|
||||
{
|
||||
AZ_Assert(size > 0, "Invalid data size for send");
|
||||
AZ_Assert(outData != nullptr, "NULL data pointer passed to send");
|
||||
AZ_Assert(size > 0, "Invalid data size for receive");
|
||||
AZ_Assert(outData != nullptr, "NULL data pointer passed to receive");
|
||||
if (!IsOpen())
|
||||
{
|
||||
return SocketOpResultErrorNotOpen;
|
||||
@@ -176,7 +176,7 @@ namespace AzNetworking
|
||||
if (::bind(aznumeric_cast<int32_t>(m_socketFd), (const sockaddr*)&hints, sizeof(hints)) != 0)
|
||||
{
|
||||
const int32_t error = GetLastNetworkError();
|
||||
AZLOG_ERROR("Failed to bind socket (%d:%s)", error, GetNetworkErrorDesc(error));
|
||||
AZLOG_ERROR("Failed to bind TCP socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace AzNetworking
|
||||
const UdpReaderThread::ReceivedPackets* packets = m_readerThread.GetReceivedPackets(m_socket.get());
|
||||
if (packets == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "nullptr was retrieved for the received packet buffer, check that the socket has been registered with the reader thread");
|
||||
// Socket is not yet registered with the reader thread and is likely still pending, try again later
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace AzNetworking
|
||||
if (::bind(static_cast<int32_t>(m_socketFd), (const sockaddr *)&hints, sizeof(hints)) != 0)
|
||||
{
|
||||
const int32_t error = GetLastNetworkError();
|
||||
AZLOG_ERROR("Failed to bind socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error));
|
||||
AZLOG_ERROR("Failed to bind UDP socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@
|
||||
|
||||
#define AZ_TRAIT_OS_USE_WINSOCK 0
|
||||
#define AZ_TRAIT_OS_USE_MACH 0
|
||||
#define AZ_TRAIT_USE_SOCKET_SERVER_EPOLL 1
|
||||
#define AZ_TRAIT_USE_SOCKET_SERVER_SELECT 0
|
||||
#define AZ_TRAIT_USE_SOCKET_SERVER_EPOLL 0
|
||||
#define AZ_TRAIT_USE_SOCKET_SERVER_SELECT 1
|
||||
#define AZ_TRAIT_USE_OPENSSL 1
|
||||
#define AZ_TRAIT_NEEDS_HTONLL 1
|
||||
|
||||
|
||||
@@ -159,6 +159,8 @@ namespace AzQtComponents
|
||||
// Timer for updating our hovered drop zone opacity
|
||||
QObject::connect(m_dropZoneHoverFadeInTimer, &QTimer::timeout, this, &FancyDocking::onDropZoneHoverFadeInUpdate);
|
||||
m_dropZoneHoverFadeInTimer->setInterval(g_FancyDockingConstants.dropZoneHoverFadeUpdateIntervalMS);
|
||||
QIcon dragIcon = QIcon(QStringLiteral(":/Cursors/Grabbing.svg"));
|
||||
m_dragCursor = QCursor(dragIcon.pixmap(16), 5, 2);
|
||||
}
|
||||
|
||||
FancyDocking::~FancyDocking()
|
||||
@@ -1884,6 +1886,8 @@ namespace AzQtComponents
|
||||
return;
|
||||
}
|
||||
|
||||
QApplication::setOverrideCursor(m_dragCursor);
|
||||
|
||||
QPoint relativePressPos = pressPos;
|
||||
|
||||
// If we are dragging a floating window, we need to grab a reference to its
|
||||
@@ -3565,6 +3569,11 @@ namespace AzQtComponents
|
||||
*/
|
||||
void FancyDocking::clearDraggingState()
|
||||
{
|
||||
if (QApplication::overrideCursor())
|
||||
{
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
m_ghostWidget->hide();
|
||||
|
||||
// Release the mouse and keyboard from our main window since we grab them when we start dragging
|
||||
|
||||
@@ -266,6 +266,8 @@ namespace AzQtComponents
|
||||
|
||||
QString m_floatingWindowIdentifierPrefix;
|
||||
QString m_tabContainerIdentifierPrefix;
|
||||
|
||||
QCursor m_dragCursor;
|
||||
};
|
||||
|
||||
} // namespace AzQtComponents
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <QAction>
|
||||
#include <QActionEvent>
|
||||
#include <QApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QIcon>
|
||||
#include <QLayout>
|
||||
@@ -419,6 +420,14 @@ namespace AzQtComponents
|
||||
// a mouse move. The paint handler updates the close button's visibility
|
||||
setAttribute(Qt::WA_Hover);
|
||||
AzQtComponents::Style::addClass(this, g_emptyStyleClass);
|
||||
|
||||
QIcon icon = QIcon(QStringLiteral(":/Cursors/Grab_release.svg"));
|
||||
m_hoverCursor = QCursor(icon.pixmap(16), 5, 2);
|
||||
|
||||
icon = QIcon(QStringLiteral(":/Cursors/Grabbing.svg"));
|
||||
m_dragCursor = QCursor(icon.pixmap(16), 5, 2);
|
||||
|
||||
this->setCursor(m_hoverCursor);
|
||||
}
|
||||
|
||||
void TabBar::setHandleOverflow(bool handleOverflow)
|
||||
@@ -455,6 +464,11 @@ namespace AzQtComponents
|
||||
{
|
||||
if (mouseEvent->buttons() & Qt::LeftButton)
|
||||
{
|
||||
if (!QApplication::overrideCursor() || *QApplication::overrideCursor() != m_dragCursor)
|
||||
{
|
||||
QApplication::setOverrideCursor(m_dragCursor);
|
||||
}
|
||||
|
||||
m_lastMousePress = mouseEvent->pos();
|
||||
}
|
||||
|
||||
@@ -469,6 +483,7 @@ namespace AzQtComponents
|
||||
// selected tab is moved around. The close button is not explicitly rendered for the
|
||||
// moved tab during this operation. We need to make sure not to set it visible again
|
||||
// while the tab is moving. This flag makes sure it happens.
|
||||
|
||||
m_movingTab = true;
|
||||
}
|
||||
|
||||
@@ -479,6 +494,13 @@ namespace AzQtComponents
|
||||
|
||||
void TabBar::mouseReleaseEvent(QMouseEvent* mouseEvent)
|
||||
{
|
||||
// Ensure we don't reset the cursor in the case of a dummy event being sent from DockTabWidget to trigger the animation.
|
||||
Qt::MouseButtons realButtons = QApplication::mouseButtons();
|
||||
if (QApplication::overrideCursor() && !(realButtons & Qt::LeftButton))
|
||||
{
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
if (m_movingTab && !(mouseEvent->buttons() & Qt::LeftButton))
|
||||
{
|
||||
// When a moving tab is released, there is a short animation to put the moving tab
|
||||
@@ -632,13 +654,7 @@ namespace AzQtComponents
|
||||
{
|
||||
QPoint p = tabRect(i).topLeft();
|
||||
|
||||
int rightPadding = g_closeButtonPadding;
|
||||
if (m_overflowing == Overflowing)
|
||||
{
|
||||
rightPadding = 0;
|
||||
}
|
||||
|
||||
p.setX(p.x() + tabRect(i).width() - rightPadding - g_closeButtonWidth);
|
||||
p.setX(p.x() + tabRect(i).width() - g_closeButtonPadding - g_closeButtonWidth);
|
||||
p.setY(p.y() + 1 + (tabRect(i).height() - g_closeButtonWidth) / 2);
|
||||
tabBtn->move(p);
|
||||
}
|
||||
|
||||
@@ -203,6 +203,9 @@ namespace AzQtComponents
|
||||
bool m_movingTab = false;
|
||||
QPoint m_lastMousePress;
|
||||
|
||||
QCursor m_dragCursor;
|
||||
QCursor m_hoverCursor;
|
||||
|
||||
void resetOverflow();
|
||||
void overflowIfNeeded();
|
||||
void showCloseButtonAt(int index);
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Cursor-_x2F_-Grab-release">
|
||||
<g id="Group" transform="translate(3.366335, 2.000000)">
|
||||
<g id="Path">
|
||||
<g>
|
||||
<path id="path-1" d="M2.4,1v7.5H2.2C1.3,7.9,0,7.8-1.1,8.2l-0.3,0.1c-0.9,0.4-1.3,1.5-0.9,2.4l1.6,4c0.3,0.8,1.1,1.8,1.7,2.5
|
||||
l0.5,0.4c0.4,0.3,0.7,0.7,1.1,0.9l0.1,0.1L3,18.8l0.3,0.3c0.1,0.3,0.3,0.4,0.3,0.4v0.1l0,0V22h7l0.5-0.9l0.5,0.9h3.3v-2.5l0,0
|
||||
v-0.1l0.1-0.3l0.1-0.3l0.1-0.3l0.1-0.4l0.3-0.5l0.3-0.5l0.4-0.8l0.1-0.3c0.5-0.5,0.9-1.8,0.9-2.6V7.6c0-1.2-0.4-3.8-2.2-3.8
|
||||
c-1.1,0-1.3,0.5-1.6,0.8c-0.1-0.3-0.4-1.5-1.7-1.6c-1.1,0-1.6,0.5-1.7,0.9C10,3.5,9.7,2.5,8.4,2.5C7.6,2.5,7.4,2.6,7.1,3
|
||||
C6.8,2.5,6.1-2,4.6-2C3.3-2,2.4,0,2.4,1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path id="path-1_1_" d="M2.4,1v7.5H2.2C1.3,7.9,0,7.8-1.1,8.2l-0.3,0.1c-0.9,0.4-1.3,1.5-0.9,2.4l1.6,4c0.3,0.8,1.1,1.8,1.7,2.5
|
||||
l0.5,0.4c0.4,0.3,0.7,0.7,1.1,0.9l0.1,0.1L3,18.8l0.3,0.3c0.1,0.1,0.3,0.3,0.3,0.3v0.3l0,0V22h7l0.5-0.9l0.5,0.9h3.3v-2.5l0,0
|
||||
v-0.1l0.1-0.3l0.1-0.3l0.1-0.3l0.1-0.4l0.3-0.5l0.3-0.5l0.4-0.8l0.1-0.3c0.5-0.5,0.9-1.8,0.9-2.6V7.6c0-1.2-0.4-3.8-2.2-3.8
|
||||
c-1.1,0-1.3,0.5-1.6,0.8c-0.1-0.3-0.4-1.5-1.7-1.6c-1.1,0-1.6,0.5-1.7,0.9C10,3.5,9.7,2.5,8.4,2.5C8,2.5,7.6,3,7.1,3.1
|
||||
c0,0.3,0.3-2.9-0.3-3.8C6.6-1.1,6.1-2,4.6-2C3.3-2,2.4,0,2.4,1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path id="Path_1_" class="st0" d="M7.1,6.4V5.3c0-0.5,0.4-1.5,1.3-1.5s1.2,0.9,1.2,1.6c0,2,0,0.5,0,1.3h0.8c0-2.8,0-0.4,0-0.9
|
||||
c0-0.8,0.5-1.3,1.3-1.3S13,5.1,13,5.9c0,0.5,0,0.9,0,0.9h0.8V6.4c0-0.7,0.3-1.2,1.1-1.2c0.9,0,1.2,0.7,1.3,1.7v1.3v5
|
||||
c0,0.7-0.3,1.5-0.5,2.1l-0.1,0.3l-0.4,0.8L14.8,17l-0.3,0.4l-0.3,0.4L14,18.2c-0.3,0.5-0.4,0.9-0.4,1.3v0.1v1.2h-1.2l-1.2-2.5
|
||||
l-1.3,2.5h-5v-1.2c0-0.4-0.4-0.8-0.8-1.5l-0.4-0.4l-0.4-0.4l-0.1-0.1L2.9,17l-0.3-0.3L2,16.1c-0.4-0.4-1.1-0.9-1.3-1.5l-0.3-0.4
|
||||
l-1.5-4c-0.1-0.3,0-0.7,0.1-0.8c0,0,0,0,0.1-0.1l0.3-0.1c1.1-0.4,2.2,0.1,2.9,1.1l0.1,0.1l1.1,1.7V1c0-0.7,0.7-1.3,1.3-1.3
|
||||
s1.2,0.7,1.2,1.2c0,3.6,0,5.4,0,5.5H7.1z"/>
|
||||
<polygon id="Path_2_" points="12.4,10.9 13.7,10.9 13.7,15.8 12.4,15.8 "/>
|
||||
<polygon id="Path_3_" points="9.9,10.9 11.2,10.9 11.2,15.8 9.9,15.8 "/>
|
||||
<polygon id="Path_4_" points="7.4,10.9 8.7,10.9 8.7,15.8 7.4,15.8 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M5.3,4.2V2.2c0-0.6,0.2-1.1,0.6-1.6C6.3,0.1,6.9,0,7.4,0l0,0c1,0,1.7,0.5,2.4,1.1l0.2,0.2l0,0l0.2-0.2
|
||||
c0.5,0,0.8-0.2,1.1-0.2h0.2c1,0,1.7,0.5,2.4,1.1l0.2,0.2l0,0L14,2c0.3-0.2,0.5-0.3,0.8-0.3H15c1.1,0,1.9,0.5,2.5,1.1L17.7,3l0,0
|
||||
l0.2-0.2c0.5-0.2,0.8-0.3,1.1-0.3h0.2c1,0,1.7,0.5,2.4,1.1c0.6,0.6,1,1.6,1,2.5l0,0v7.5c0,1-0.5,2.5-1,3.3c0,0-2.2,3.3-2.2,4.5l0,0
|
||||
V24h-2.9l-0.6-2.2L14.7,24H6.9v-2.4c0-1.1-4.8-4.3-4.8-4.3c-1-0.6-1.6-2.1-1.6-3.2l0,0V8.4c0-1.1,0.5-2.1,1.1-2.9
|
||||
C2.6,4.6,4,4.1,5.3,4.2L5.3,4.2z"/>
|
||||
<g>
|
||||
<path d="M7.4,1C7.9,1,8.5,1.2,9,1.8l0.2,0.2l0.7,0.7L10.5,2C10.7,2,10.8,2,11,1.9c0.1,0,0.2,0,0.2,0h0.2c0.6,0,1.1,0.3,1.7,0.8
|
||||
l0.2,0.2l0.7,0.7l0.7-0.7l0.1-0.1c0,0,0.1,0,0.1-0.1c0,0,0.1,0,0.1-0.1H15c0.7,0,1.3,0.3,1.8,0.8L17,3.7l0.7,0.7l0.7-0.7
|
||||
c0.1,0,0.1,0,0.2-0.1c0.1-0.1,0.4-0.1,0.4-0.1h0.2c0.6,0,1.1,0.3,1.7,0.8c0.4,0.4,0.7,1.1,0.7,1.8v7.5c0,0.7-0.4,2.2-0.8,2.8
|
||||
c-0.7,1.1-2.4,3.7-2.4,5V23h-1.1l-0.4-1.5L16,19l-1.1,2.3L14.1,23H7.9v-1.4c0-0.4,0-1.6-5.2-5.1C2.1,16,1.5,15,1.5,14.1V8.4
|
||||
c0-0.7,0.3-1.5,0.9-2.2c0.6-0.6,1.5-1,2.4-1c0.1,0,0.2,0,0.4,0l1.1,0.1V4.2V2.2c0-0.4,0.1-0.6,0.3-0.9l0.1-0.1l0.1-0.1
|
||||
C6.8,1,7.1,1,7.4,1 M14.9,2.7L14.9,2.7L14.9,2.7 M7.4,0C6.9,0,6.3,0.1,5.9,0.6c-0.5,0.5-0.6,1-0.6,1.6v2.1c-0.2,0-0.3,0-0.5,0
|
||||
c-1.1,0-2.3,0.5-3.2,1.3C1,6.3,0.5,7.3,0.5,8.4v5.7c0,1.1,0.6,2.5,1.6,3.2c0,0,4.8,3.2,4.8,4.3V24h7.8l1.1-2.2l0.6,2.2h2.9v-2.5
|
||||
c0-1.1,2.2-4.5,2.2-4.5c0.5-0.8,1-2.4,1-3.3V6.1c0-1-0.3-1.9-1-2.5c-0.6-0.6-1.4-1.1-2.4-1.1H19c-0.3,0-0.6,0.2-1.1,0.3L17.7,3
|
||||
l-0.2-0.2c-0.6-0.6-1.4-1.1-2.5-1.1h-0.2c-0.3,0-0.5,0.2-0.8,0.3l-0.2,0.2L13.7,2c-0.6-0.6-1.4-1.1-2.4-1.1h-0.2
|
||||
c-0.3,0-0.6,0.2-1.1,0.2L9.9,1.2L9.8,1.1C9.1,0.4,8.3,0,7.4,0L7.4,0z"/>
|
||||
</g>
|
||||
<polygon class="st1" points="10.9,10.4 12.5,10.4 12.5,16.8 10.9,16.8 "/>
|
||||
<polygon class="st1" points="14,10.4 15.6,10.4 15.6,16.8 14,16.8 "/>
|
||||
<polygon class="st1" points="17.2,10.4 18.8,10.4 18.8,16.8 17.2,16.8 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -637,5 +637,7 @@
|
||||
</qresource>
|
||||
<qresource prefix="/Cursors">
|
||||
<file alias="Pointer.svg">img/UI20/Cursors/Pointer.svg</file>
|
||||
<file alias="Grab_release.svg">img/UI20/Cursors/Grab_release.svg</file>
|
||||
<file alias="Grabbing.svg">img/UI20/Cursors/Grabbing.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#define AZ_TRAIT_DISABLE_FAILED_ZERO_COLOR_CONVERSION_TEST true
|
||||
#define AZ_TRAIT_DISABLE_FAILED_FRAMEPROFILER_TEST true
|
||||
#define AZ_TRAIT_DISABLE_FAILED_FRAMEWORK_TESTS true
|
||||
#define AZ_TRAIT_DISABLE_FAILED_NETWORKING_TESTS true
|
||||
#define AZ_TRAIT_DISABLE_FAILED_GRADIENT_SIGNAL_TESTS true
|
||||
#define AZ_TRAIT_DISABLE_FAILED_MULTIPLAYER_GRIDMATE_TESTS true
|
||||
#define AZ_TRAIT_DISABLE_FAILED_INPUT_TESTS true
|
||||
|
||||
+5
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
@@ -46,6 +47,10 @@ namespace AzToolsFramework
|
||||
|
||||
virtual Prefab::InstanceOptionalReference GetRootPrefabInstance() = 0;
|
||||
|
||||
//! Get all Assets generated by Prefab processing when entering Play-In Editor mode (Ctrl+G)
|
||||
//! /return The vector of Assets generated by Prefab processing
|
||||
virtual const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() = 0;
|
||||
|
||||
virtual bool LoadFromStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0;
|
||||
virtual bool SaveToStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0;
|
||||
|
||||
|
||||
+5
@@ -314,6 +314,11 @@ namespace AzToolsFramework
|
||||
return *m_rootInstance;
|
||||
}
|
||||
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData()
|
||||
{
|
||||
return m_playInEditorData.m_assets;
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::OnEntityRemoved(AZ::EntityId entityId)
|
||||
{
|
||||
AzFramework::SliceEntityRequestBus::MultiHandler::BusDisconnect(entityId);
|
||||
|
||||
+2
@@ -195,6 +195,8 @@ namespace AzToolsFramework
|
||||
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override;
|
||||
|
||||
Prefab::InstanceOptionalReference GetRootPrefabInstance() override;
|
||||
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void OnEntityRemoved(AZ::EntityId entityId);
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace AzToolsFramework
|
||||
~LinearManipulator() = default;
|
||||
|
||||
/// A Manipulator must only be created and managed through a shared_ptr.
|
||||
/// @note worldFromLocal should not contain scale.
|
||||
static AZStd::shared_ptr<LinearManipulator> MakeShared(const AZ::Transform& worldFromLocal);
|
||||
|
||||
/// Unchanging data set once for the linear manipulator.
|
||||
|
||||
@@ -80,6 +80,12 @@ namespace AzToolsFramework
|
||||
|
||||
void Instance::SetTemplateId(const TemplateId& templateId)
|
||||
{
|
||||
// If we aren't changing the template Id, there's no need to unregister / re-register
|
||||
if (templateId == m_templateId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If this instance's templateId is valid, we should be able to unregister this instance from
|
||||
// Template to Instance mapping successfully.
|
||||
if (m_templateId != InvalidTemplateId &&
|
||||
|
||||
+23
-8
@@ -72,10 +72,18 @@ namespace AzToolsFramework
|
||||
|
||||
for (auto instance : findInstancesResult->get())
|
||||
{
|
||||
m_instancesUpdateQueue.emplace(instance);
|
||||
m_instancesUpdateQueue.emplace_back(instance);
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceUpdateExecutor::RemoveTemplateInstanceFromQueue(const Instance* instance)
|
||||
{
|
||||
AZStd::erase_if(m_instancesUpdateQueue, [instance](Instance* entry)
|
||||
{
|
||||
return entry == instance;
|
||||
});
|
||||
}
|
||||
|
||||
bool InstanceUpdateExecutor::UpdateTemplateInstancesInQueue()
|
||||
{
|
||||
bool isUpdateSuccessful = true;
|
||||
@@ -97,9 +105,16 @@ namespace AzToolsFramework
|
||||
ToolsApplicationRequestBus::BroadcastResult(selectedEntityIds, &ToolsApplicationRequests::GetSelectedEntities);
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, EntityIdList());
|
||||
|
||||
for (int i = 0; i < instanceCountToUpdateInBatch; ++i)
|
||||
// Process all instances in the queue, capped to the batch size.
|
||||
// Even though we potentially initialized the batch size to the queue, it's possible for the queue size to shrink
|
||||
// during instance processing if the instance gets deleted and it was queued multiple times. To handle this, we
|
||||
// make sure to end the loop once the queue is empty, regardless of what the initial size was.
|
||||
for (int i = 0; (i < instanceCountToUpdateInBatch) && !m_instancesUpdateQueue.empty(); ++i)
|
||||
{
|
||||
Instance* instanceToUpdate = m_instancesUpdateQueue.front();
|
||||
m_instancesUpdateQueue.pop_front();
|
||||
AZ_Assert(instanceToUpdate != nullptr, "Invalid instance on update queue.");
|
||||
|
||||
TemplateId instanceTemplateId = instanceToUpdate->GetTemplateId();
|
||||
if (currentTemplateId != instanceTemplateId)
|
||||
{
|
||||
@@ -115,19 +130,21 @@ namespace AzToolsFramework
|
||||
|
||||
// Remove the instance from update queue if its corresponding template couldn't be found
|
||||
isUpdateSuccessful = false;
|
||||
m_instancesUpdateQueue.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto findInstancesResult = m_templateInstanceMapperInterface->FindInstancesOwnedByTemplate(instanceTemplateId)->get();
|
||||
auto findInstancesResult = m_templateInstanceMapperInterface->FindInstancesOwnedByTemplate(instanceTemplateId);
|
||||
AZ_Assert(
|
||||
findInstancesResult.has_value(), "Prefab Instances corresponding to template with id %llu couldn't be found.",
|
||||
instanceTemplateId);
|
||||
|
||||
if (findInstancesResult.find(instanceToUpdate) == findInstancesResult.end())
|
||||
if (findInstancesResult == AZStd::nullopt ||
|
||||
findInstancesResult->get().find(instanceToUpdate) == findInstancesResult->get().end())
|
||||
{
|
||||
// Since nested instances get reconstructed during propagation, remove any nested instance that no longer
|
||||
// maps to a template.
|
||||
isUpdateSuccessful = false;
|
||||
m_instancesUpdateQueue.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -148,8 +165,6 @@ namespace AzToolsFramework
|
||||
|
||||
isUpdateSuccessful = false;
|
||||
}
|
||||
|
||||
m_instancesUpdateQueue.pop();
|
||||
}
|
||||
|
||||
for (auto entityIdIterator = selectedEntityIds.begin(); entityIdIterator != selectedEntityIds.end(); entityIdIterator++)
|
||||
|
||||
+3
-2
@@ -14,7 +14,7 @@
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/std/containers/queue.h>
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace AzToolsFramework
|
||||
|
||||
void AddTemplateInstancesToQueue(TemplateId instanceTemplateId) override;
|
||||
bool UpdateTemplateInstancesInQueue() override;
|
||||
virtual void RemoveTemplateInstanceFromQueue(const Instance* instance) override;
|
||||
|
||||
void RegisterInstanceUpdateExecutorInterface();
|
||||
void UnregisterInstanceUpdateExecutorInterface();
|
||||
@@ -45,7 +46,7 @@ namespace AzToolsFramework
|
||||
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
|
||||
TemplateInstanceMapperInterface* m_templateInstanceMapperInterface = nullptr;
|
||||
int m_instanceCountToUpdateInBatch = 0;
|
||||
AZStd::queue<Instance*> m_instancesUpdateQueue;
|
||||
AZStd::deque<Instance*> m_instancesUpdateQueue;
|
||||
bool m_updatingTemplateInstancesInQueue { false };
|
||||
};
|
||||
}
|
||||
|
||||
+3
@@ -31,6 +31,9 @@ namespace AzToolsFramework
|
||||
|
||||
// Update Instances in the waiting queue.
|
||||
virtual bool UpdateTemplateInstancesInQueue() = 0;
|
||||
|
||||
// Remove an Instance from the waiting queue.
|
||||
virtual void RemoveTemplateInstanceFromQueue(const Instance* instance) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -71,6 +72,12 @@ namespace AzToolsFramework
|
||||
|
||||
bool TemplateInstanceMapper::UnregisterInstance(Instance& instance)
|
||||
{
|
||||
// The InstanceUpdateExecutor queries the TemplateInstanceMapper for a list of instances related to a template.
|
||||
// Consequently, if an instance gets unregistered for a template, we need to notify the InstanceUpdateExecutor as well
|
||||
// so that it clears any internal associations that it might have in its queue.
|
||||
AZ_Assert(AZ::Interface<InstanceUpdateExecutorInterface>::Get() != nullptr, "InstanceUpdateExecutor doesn't exist");
|
||||
AZ::Interface<InstanceUpdateExecutorInterface>::Get()->RemoveTemplateInstanceFromQueue(&instance);
|
||||
|
||||
auto found = m_templateIdToInstancesMap.find(instance.GetTemplateId());
|
||||
return found != m_templateIdToInstancesMap.end() &&
|
||||
found->second.erase(&instance) != 0;
|
||||
|
||||
@@ -182,16 +182,16 @@ namespace AzToolsFramework
|
||||
else
|
||||
{
|
||||
AZ::JsonSerializationResult::ResultCode applyPatchResult = AZ::JsonSerialization::ApplyPatch(
|
||||
linkedInstanceDom,
|
||||
sourceTemplateDomCopy,
|
||||
targetTemplatePrefabDom.GetAllocator(),
|
||||
sourceTemplatePrefabDom,
|
||||
patchesReference->get(),
|
||||
AZ::JsonMergeApproach::JsonPatch);
|
||||
linkedInstanceDom.CopyFrom(sourceTemplateDomCopy, targetTemplatePrefabDom.GetAllocator());
|
||||
if (applyPatchResult.GetProcessing() != AZ::JsonSerializationResult::Processing::Completed)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"Link::UpdateTarget - "
|
||||
"ApplyPatches failed for Prefab DOM from source Template '%u' and target Template '%u'.",
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"Link::UpdateTarget - ApplyPatches failed for Prefab DOM from source Template '%u' and target Template '%u'.",
|
||||
m_sourceTemplateId, m_targetTemplateId);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -122,11 +122,14 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
|
||||
|
||||
// Parent the entities to the container entity. Parenting the container entities of the instances passed to createPrefab
|
||||
// will be done during the creation of links below.
|
||||
for (AZ::Entity* topLevelEntity : entities)
|
||||
// Parent the non-container top level entities to the container entity.
|
||||
// Parenting the top level container entities will be done during the creation of links.
|
||||
for (AZ::Entity* topLevelEntity : topLevelEntities)
|
||||
{
|
||||
AZ::TransformBus::Event(topLevelEntity->GetId(), &AZ::TransformBus::Events::SetParent, containerEntityId);
|
||||
if (!IsInstanceContainerEntity(topLevelEntity->GetId()))
|
||||
{
|
||||
AZ::TransformBus::Event(topLevelEntity->GetId(), &AZ::TransformBus::Events::SetParent, containerEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the template of the instance since the entities are modified since the template creation.
|
||||
@@ -142,11 +145,25 @@ namespace AzToolsFramework
|
||||
AZ_Assert(
|
||||
nestedInstanceContainerEntity, "Invalid container entity found for the nested instance used in prefab creation.");
|
||||
|
||||
AZ::EntityId parentId;
|
||||
AZ::TransformBus::EventResult(
|
||||
parentId, nestedInstanceContainerEntity->get().GetId(), &AZ::TransformBus::Events::GetParentId);
|
||||
|
||||
auto entityIterator = AZStd::find_if(
|
||||
entities.begin(), entities.end(), [parentId](AZ::Entity* entity) { return entity->GetId() == parentId; });
|
||||
|
||||
// If the previous parent entity of the nested instance is not part of the entities of the newly created prefab,
|
||||
// then set the parent of the nested prefab as the container entity of the newly created prefab.
|
||||
if (entityIterator == entities.end())
|
||||
{
|
||||
parentId = containerEntityId;
|
||||
}
|
||||
|
||||
// These link creations shouldn't be undone because that would put the template in a non-usable state if a user
|
||||
// chooses to instantiate the template after undoing the creation.
|
||||
CreateLink(
|
||||
{&nestedInstanceContainerEntity->get()}, *nestedInstance, instanceToCreate->get().GetTemplateId(),
|
||||
undoBatch.GetUndoBatch(), containerEntityId, false);
|
||||
undoBatch.GetUndoBatch(), parentId, false);
|
||||
});
|
||||
|
||||
// Create a link between the templates of the newly created instance and the instance it's being parented under.
|
||||
|
||||
+29
-18
@@ -1,20 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
* 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 <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
|
||||
#include <AzCore/Math/ToString.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzFramework/Components/NonUniformScaleComponent.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Math/ToString.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -32,12 +32,13 @@ namespace AzToolsFramework
|
||||
serializeContext->Class<EditorNonUniformScaleComponent, EditorComponentBase>()
|
||||
->Version(1)
|
||||
->Field("NonUniformScale", &EditorNonUniformScaleComponent::m_scale)
|
||||
;
|
||||
->Field("ComponentMode", &EditorNonUniformScaleComponent::m_componentModeDelegate);
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<EditorNonUniformScaleComponent>("Non-uniform Scale",
|
||||
"Non-uniform scale for this entity only (does not propagate through hierarchy)")
|
||||
editContext
|
||||
->Class<EditorNonUniformScaleComponent>(
|
||||
"Non-uniform Scale", "Non-uniform scale for this entity only (does not propagate through hierarchy)")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::FixedComponentListIndex, 1)
|
||||
->Attribute(AZ::Edit::Attributes::RemoveableByUser, true)
|
||||
@@ -50,7 +51,10 @@ namespace AzToolsFramework
|
||||
->Attribute(AZ::Edit::Attributes::Max, AZ::MaxTransformScale)
|
||||
->Attribute(AZ::Edit::Attributes::Step, 0.1f)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorNonUniformScaleComponent::OnScaleChanged)
|
||||
;
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &EditorNonUniformScaleComponent::m_componentModeDelegate, "Component Mode",
|
||||
"Non-uniform Scale Component Mode")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,10 +78,16 @@ namespace AzToolsFramework
|
||||
void EditorNonUniformScaleComponent::Activate()
|
||||
{
|
||||
AZ::NonUniformScaleRequestBus::Handler::BusConnect(GetEntityId());
|
||||
|
||||
// ComponentMode
|
||||
m_componentModeDelegate.ConnectWithSingleComponentMode<EditorNonUniformScaleComponent, NonUniformScaleComponentMode>(
|
||||
AZ::EntityComponentIdPair(GetEntityId(), GetId()), nullptr);
|
||||
}
|
||||
|
||||
void EditorNonUniformScaleComponent::Deactivate()
|
||||
{
|
||||
m_componentModeDelegate.Disconnect();
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -96,7 +106,8 @@ namespace AzToolsFramework
|
||||
else
|
||||
{
|
||||
AZ::Vector3 clampedScale = scale.GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale));
|
||||
AZ_Warning("Editor Non-uniform Scale Component", false, "SetScale value was clamped from %s to %s for entity %s",
|
||||
AZ_Warning(
|
||||
"Editor Non-uniform Scale Component", false, "SetScale value was clamped from %s to %s for entity %s",
|
||||
AZ::ToString(scale).c_str(), AZ::ToString(clampedScale).c_str(), GetEntity()->GetName().c_str());
|
||||
m_scale = clampedScale;
|
||||
}
|
||||
|
||||
+6
@@ -13,6 +13,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.h>
|
||||
#include <AzToolsFramework/ComponentMode/ComponentModeDelegate.h>
|
||||
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
|
||||
#include <AzCore/Component/NonUniformScaleBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -52,6 +55,9 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::Vector3 m_scale = AZ::Vector3::CreateOne();
|
||||
AZ::NonUniformScaleChangedEvent m_scaleChangedEvent;
|
||||
|
||||
//! Responsible for detecting ComponentMode activation and creating a concrete ComponentMode.
|
||||
AzToolsFramework::ComponentModeFramework::ComponentModeDelegate m_componentModeDelegate;
|
||||
};
|
||||
} // namespace Components
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 <AzCore/Component/NonUniformScaleBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzFramework/Viewport/ViewportColors.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
NonUniformScaleComponentMode::NonUniformScaleComponentMode(
|
||||
const AZ::EntityComponentIdPair& entityComponentIdPair, AZ::Uuid componentType)
|
||||
: EditorBaseComponentMode(entityComponentIdPair, componentType)
|
||||
{
|
||||
m_entityComponentIdPair = entityComponentIdPair;
|
||||
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
|
||||
worldFromLocal.ExtractScale();
|
||||
m_manipulators = AZStd::make_unique<ScaleManipulators>(worldFromLocal);
|
||||
m_manipulators->Register(g_mainManipulatorManagerId);
|
||||
m_manipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
|
||||
const float axisLength = 2.0f;
|
||||
m_manipulators->ConfigureView(
|
||||
axisLength, AzFramework::ViewportColors::XAxisColor, AzFramework::ViewportColors::YAxisColor,
|
||||
AzFramework::ViewportColors::ZAxisColor);
|
||||
|
||||
auto mouseDownCallback = [this](const LinearManipulator::Action& action) {
|
||||
AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne();
|
||||
|
||||
AZ::NonUniformScaleRequestBus::EventResult(
|
||||
nonUniformScale, m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale);
|
||||
|
||||
m_initialScale = nonUniformScale + action.m_start.m_scaleSnapOffset;
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, m_initialScale);
|
||||
};
|
||||
|
||||
m_manipulators->InstallAxisLeftMouseDownCallback(mouseDownCallback);
|
||||
|
||||
m_manipulators->InstallAxisMouseMoveCallback([this](const LinearManipulator::Action& action) {
|
||||
const AZ::Vector3 scaleMultiplier =
|
||||
(AZ::Vector3::CreateOne() + ((action.LocalScaleOffset() * action.m_start.m_sign) / m_initialScale));
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale,
|
||||
(scaleMultiplier * m_initialScale).GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale)));
|
||||
});
|
||||
|
||||
m_manipulators->InstallUniformLeftMouseDownCallback(mouseDownCallback);
|
||||
|
||||
m_manipulators->InstallUniformMouseMoveCallback([this](const LinearManipulator::Action& action) {
|
||||
const auto sumVectorElements = [](const AZ::Vector3& vec) { return vec.GetX() + vec.GetY() + vec.GetZ(); };
|
||||
|
||||
const float minScaleMultiplier = AZ::MinTransformScale / m_initialScale.GetMinElement();
|
||||
const float maxScaleMultiplier = AZ::MaxTransformScale / m_initialScale.GetMaxElement();
|
||||
const float scaleMultiplier = AZ::GetClamp(
|
||||
1.0f + sumVectorElements(action.m_start.m_sign * action.LocalScaleOffset() / m_initialScale), minScaleMultiplier,
|
||||
maxScaleMultiplier);
|
||||
|
||||
AZ::NonUniformScaleRequestBus::Event(
|
||||
m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, scaleMultiplier * m_initialScale);
|
||||
});
|
||||
}
|
||||
|
||||
NonUniformScaleComponentMode::~NonUniformScaleComponentMode()
|
||||
{
|
||||
if (m_manipulators)
|
||||
{
|
||||
m_manipulators->Unregister();
|
||||
}
|
||||
m_manipulators.reset();
|
||||
}
|
||||
|
||||
void NonUniformScaleComponentMode::Refresh()
|
||||
{
|
||||
}
|
||||
} // namespace Components
|
||||
} // namespace AzToolsFramework
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <AzToolsFramework/ComponentMode/EditorBaseComponentMode.h>
|
||||
#include <AzToolsFramework/Manipulators/ScaleManipulators.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
class NonUniformScaleComponentMode : public AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(NonUniformScaleComponentMode, AZ::SystemAllocator, 0)
|
||||
|
||||
NonUniformScaleComponentMode(const AZ::EntityComponentIdPair& entityComponentIdPair, AZ::Uuid componentType);
|
||||
NonUniformScaleComponentMode(const NonUniformScaleComponentMode&) = delete;
|
||||
NonUniformScaleComponentMode& operator=(const NonUniformScaleComponentMode&) = delete;
|
||||
NonUniformScaleComponentMode(NonUniformScaleComponentMode&&) = delete;
|
||||
NonUniformScaleComponentMode& operator=(NonUniformScaleComponentMode&&) = delete;
|
||||
~NonUniformScaleComponentMode();
|
||||
|
||||
// EditorBaseComponentMode overrides ...
|
||||
void Refresh() override;
|
||||
|
||||
private:
|
||||
AZ::EntityComponentIdPair m_entityComponentIdPair;
|
||||
AZStd::unique_ptr<ScaleManipulators> m_manipulators;
|
||||
AZ::Vector3 m_initialScale;
|
||||
};
|
||||
} // namespace Components
|
||||
} // namespace AzToolsFramework
|
||||
+7
-1
@@ -162,6 +162,12 @@ namespace AzToolsFramework
|
||||
classElement.RemoveElementByName(AZ_CRC("InterpolateScale", 0x9d00b831));
|
||||
}
|
||||
|
||||
if (classElement.GetVersion() < 10)
|
||||
{
|
||||
// The "Sync Enabled" flag is no longer needed.
|
||||
classElement.RemoveElementByName(AZ_CRC_CE("Sync Enabled"));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace Internal
|
||||
@@ -1305,7 +1311,7 @@ namespace AzToolsFramework
|
||||
Field("IsStatic", &TransformComponent::m_isStatic)->
|
||||
Field("InterpolatePosition", &TransformComponent::m_interpolatePosition)->
|
||||
Field("InterpolateRotation", &TransformComponent::m_interpolateRotation)->
|
||||
Version(9, &Internal::TransformComponentDataConverter);
|
||||
Version(10, &Internal::TransformComponentDataConverter);
|
||||
|
||||
if (AZ::EditContext* ptrEdit = serializeContext->GetEditContext())
|
||||
{
|
||||
|
||||
+28
@@ -27,6 +27,7 @@ AZ_PUSH_DISABLE_WARNING(4244 4251 4800, "-Wunknown-warning-option") // 4244: con
|
||||
#include <QtGui/QTextLayout>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QMessageBox>
|
||||
#include <QStylePainter>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
static const int LabelColumnStretch = 2;
|
||||
@@ -121,6 +122,19 @@ namespace AzToolsFramework
|
||||
setLayout(m_mainLayout);
|
||||
}
|
||||
|
||||
void PropertyRowWidget::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QStylePainter p(this);
|
||||
|
||||
if (CanBeReordered())
|
||||
{
|
||||
const QPen linePen(QColor(0x3B3E3F));
|
||||
p.setPen(linePen);
|
||||
int indent = m_treeDepth * m_treeIndentation;
|
||||
p.drawLine(event->rect().topLeft() + QPoint(indent, 0), event->rect().topRight());
|
||||
}
|
||||
}
|
||||
|
||||
bool PropertyRowWidget::HasChildWidgetAlready() const
|
||||
{
|
||||
return m_childWidget != nullptr;
|
||||
@@ -1661,6 +1675,20 @@ namespace AzToolsFramework
|
||||
m_nameLabel->setFilter(m_currentFilterString);
|
||||
}
|
||||
|
||||
bool PropertyRowWidget::CanChildrenBeReordered() const
|
||||
{
|
||||
return m_containerEditable;
|
||||
}
|
||||
|
||||
bool PropertyRowWidget::CanBeReordered() const
|
||||
{
|
||||
if (!m_parentRow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_parentRow->CanChildrenBeReordered();
|
||||
}
|
||||
}
|
||||
|
||||
#include "UI/PropertyEditor/moc_PropertyRowWidget.cpp"
|
||||
|
||||
+5
@@ -44,6 +44,7 @@ namespace AzToolsFramework
|
||||
Q_PROPERTY(bool hasChildRows READ HasChildRows);
|
||||
Q_PROPERTY(bool isTopLevel READ IsTopLevel);
|
||||
Q_PROPERTY(int getLevel READ GetLevel);
|
||||
Q_PROPERTY(bool canBeReordered READ CanBeReordered);
|
||||
Q_PROPERTY(bool appendDefaultLabelToName READ GetAppendDefaultLabelToName WRITE AppendDefaultLabelToName)
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PropertyRowWidget, AZ::SystemAllocator, 0)
|
||||
@@ -126,6 +127,7 @@ namespace AzToolsFramework
|
||||
void SetSelectionEnabled(bool selectionEnabled);
|
||||
void SetSelected(bool selected);
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
/// Apply tooltip to widget and some of its children.
|
||||
void SetDescription(const QString& text);
|
||||
@@ -146,6 +148,9 @@ namespace AzToolsFramework
|
||||
QLabel* GetNameLabel() { return m_nameLabel; }
|
||||
void SetIndentSize(int w);
|
||||
void SetAsCustom(bool custom) { m_custom = custom; }
|
||||
|
||||
bool CanChildrenBeReordered() const;
|
||||
bool CanBeReordered() const;
|
||||
protected:
|
||||
int CalculateLabelWidth() const;
|
||||
|
||||
|
||||
@@ -303,6 +303,8 @@ set(FILES
|
||||
ToolsComponents/AzToolsFrameworkConfigurationSystemComponent.cpp
|
||||
ToolsComponents/EditorNonUniformScaleComponent.h
|
||||
ToolsComponents/EditorNonUniformScaleComponent.cpp
|
||||
ToolsComponents/EditorNonUniformScaleComponentMode.h
|
||||
ToolsComponents/EditorNonUniformScaleComponentMode.cpp
|
||||
ToolsMessaging/EntityHighlightBus.h
|
||||
UI/Docking/DockWidgetUtils.cpp
|
||||
UI/Docking/DockWidgetUtils.h
|
||||
|
||||
@@ -237,4 +237,23 @@ namespace UnitTest
|
||||
EXPECT_THAT(cameraTransform, IsClose(cameraTransformFromView));
|
||||
EXPECT_THAT(cameraView, IsClose(cameraViewFromTransform));
|
||||
}
|
||||
|
||||
TEST(ViewportScreen, FovCanBeRetrievedFromProjectionMatrix)
|
||||
{
|
||||
using ::testing::FloatNear;
|
||||
|
||||
auto cameraState = AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AZ::Vector2(800.0f, 600.0f));
|
||||
|
||||
{
|
||||
const float fovRadians = AZ::DegToRad(45.0f);
|
||||
AzFramework::SetCameraClippingVolume(cameraState, 0.1f, 100.0f, fovRadians);
|
||||
EXPECT_THAT(AzFramework::RetrieveFov(AzFramework::CameraProjection(cameraState)), FloatNear(fovRadians, 0.001f));
|
||||
}
|
||||
|
||||
{
|
||||
const float fovRadians = AZ::DegToRad(90.0f);
|
||||
AzFramework::SetCameraClippingVolume(cameraState, 0.1f, 100.0f, fovRadians);
|
||||
EXPECT_THAT(AzFramework::RetrieveFov(AzFramework::CameraProjection(cameraState)), FloatNear(fovRadians, 0.001f));
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,142 +1,142 @@
|
||||
/*
|
||||
* 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 <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
std::ostream& operator<<(std::ostream& os, const ClickDetector::ClickOutcome clickOutcome)
|
||||
{
|
||||
switch (clickOutcome)
|
||||
{
|
||||
case ClickDetector::ClickOutcome::Click:
|
||||
os << "ClickOutcome::Click";
|
||||
break;
|
||||
case ClickDetector::ClickOutcome::Move:
|
||||
os << "ClickOutcome::Move";
|
||||
break;
|
||||
case ClickDetector::ClickOutcome::Release:
|
||||
os << "ClickOutcome::Release";
|
||||
break;
|
||||
case ClickDetector::ClickOutcome::Nil:
|
||||
os << "ClickOutcome::Nil";
|
||||
break;
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using AzFramework::ClickDetector;
|
||||
using AzFramework::ScreenVector;
|
||||
|
||||
class ClickDetectorFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
ClickDetector m_clickDetector;
|
||||
};
|
||||
|
||||
TEST_F(ClickDetectorFixture, ClickIsDetectedWithNoMouseMovementOnMouseUp)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, MoveIsDetectedWithMouseMovementAfterMouseDown)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialMoveOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move));
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, ReleaseIsDetectedAfterMouseMovementOnMouseUp)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
// move
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
|
||||
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Release));
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, MoveIsReturnedOnlyAfterFirstMouseMove)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialMoveOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
|
||||
const ClickDetector::ClickOutcome secondaryMoveOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move));
|
||||
EXPECT_THAT(secondaryMoveOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterDoubleClick)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome secondaryDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome secondaryUpOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
|
||||
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // double click
|
||||
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterIgnoredDoubleClick)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome secondaryDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome secondaryUpOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
|
||||
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // ignored double click
|
||||
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
|
||||
}
|
||||
} // namespace UnitTest
|
||||
/*
|
||||
* 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 <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
std::ostream& operator<<(std::ostream& os, const ClickDetector::ClickOutcome clickOutcome)
|
||||
{
|
||||
switch (clickOutcome)
|
||||
{
|
||||
case ClickDetector::ClickOutcome::Click:
|
||||
os << "ClickOutcome::Click";
|
||||
break;
|
||||
case ClickDetector::ClickOutcome::Move:
|
||||
os << "ClickOutcome::Move";
|
||||
break;
|
||||
case ClickDetector::ClickOutcome::Release:
|
||||
os << "ClickOutcome::Release";
|
||||
break;
|
||||
case ClickDetector::ClickOutcome::Nil:
|
||||
os << "ClickOutcome::Nil";
|
||||
break;
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using AzFramework::ClickDetector;
|
||||
using AzFramework::ScreenVector;
|
||||
|
||||
class ClickDetectorFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
ClickDetector m_clickDetector;
|
||||
};
|
||||
|
||||
TEST_F(ClickDetectorFixture, ClickIsDetectedWithNoMouseMovementOnMouseUp)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, MoveIsDetectedWithMouseMovementAfterMouseDown)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialMoveOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move));
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, ReleaseIsDetectedAfterMouseMovementOnMouseUp)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
// move
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
|
||||
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Release));
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, MoveIsReturnedOnlyAfterFirstMouseMove)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialMoveOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
|
||||
const ClickDetector::ClickOutcome secondaryMoveOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move));
|
||||
EXPECT_THAT(secondaryMoveOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterDoubleClick)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome secondaryDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome secondaryUpOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
|
||||
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // double click
|
||||
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
|
||||
}
|
||||
|
||||
TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterIgnoredDoubleClick)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
const ClickDetector::ClickOutcome initialDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome secondaryDownOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(0, 0));
|
||||
const ClickDetector::ClickOutcome secondaryUpOutcome =
|
||||
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
|
||||
|
||||
EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
|
||||
EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
|
||||
EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // ignored double click
|
||||
EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
/*
|
||||
* 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 <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Viewport/CursorState.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using AzFramework::CursorState;
|
||||
using AzFramework::ScreenVector;
|
||||
using AzFramework::ScreenPoint;
|
||||
|
||||
class CursorStateFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
CursorState m_cursorState;
|
||||
};
|
||||
|
||||
TEST_F(CursorStateFixture, CursorStateHasZeroDeltaInitially)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0)));
|
||||
}
|
||||
|
||||
TEST_F(CursorStateFixture, CursorStateReturnsZeroDeltaAfterSingleMoveAndUpdate)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
m_cursorState.SetCurrentPosition(ScreenPoint(10, 10));
|
||||
m_cursorState.Update();
|
||||
|
||||
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0)));
|
||||
}
|
||||
|
||||
TEST_F(CursorStateFixture, CursorStateReturnsDeltaAfterSecondMoveAndUpdate)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
m_cursorState.SetCurrentPosition(ScreenPoint(10, 10));
|
||||
m_cursorState.Update();
|
||||
m_cursorState.SetCurrentPosition(ScreenPoint(15, 22));
|
||||
|
||||
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(5, 12)));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
/*
|
||||
* 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 <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzFramework/Viewport/CursorState.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using AzFramework::CursorState;
|
||||
using AzFramework::ScreenVector;
|
||||
using AzFramework::ScreenPoint;
|
||||
|
||||
class CursorStateFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
CursorState m_cursorState;
|
||||
};
|
||||
|
||||
TEST_F(CursorStateFixture, CursorStateHasZeroDeltaInitially)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0)));
|
||||
}
|
||||
|
||||
TEST_F(CursorStateFixture, CursorStateReturnsZeroDeltaAfterSingleMoveAndUpdate)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
m_cursorState.SetCurrentPosition(ScreenPoint(10, 10));
|
||||
m_cursorState.Update();
|
||||
|
||||
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0)));
|
||||
}
|
||||
|
||||
TEST_F(CursorStateFixture, CursorStateReturnsDeltaAfterSecondMoveAndUpdate)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
m_cursorState.SetCurrentPosition(ScreenPoint(10, 10));
|
||||
m_cursorState.Update();
|
||||
m_cursorState.SetCurrentPosition(ScreenPoint(15, 22));
|
||||
|
||||
EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(5, 12)));
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
Reference in New Issue
Block a user